mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-24 19:07:47 +00:00
feat: new query builder misc fixes (#8359)
* feat: qb fixes * feat: fixed handlerunquery props * feat: fixes logs list order by * feat: fix logs order by issue * feat: safety check and order by correction * feat: updated version in new create dashboards * feat: added new formatOptions for table and fixed the pie chart plotting * feat: keyboard shortcut overriding issue and pie ch correction in dashboard views * feat: fixed dashboard data state management across datasource * paneltypes * feat: fixed explorer pages data management issues * feat: integrated new backend payload/request diff, to the UI types * feat: fixed the collapse behaviour of QB - queries * feat: fix order by and default aggregation to count()
This commit is contained in:
parent
26802cd668
commit
a71a2af0a8
@ -41,174 +41,55 @@ function convertTimeSeriesData(
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to collect columns from scalar data
|
||||
*/
|
||||
function collectColumnsFromScalarData(
|
||||
scalarData: ScalarData[],
|
||||
): { name: string; queryName: string; isValueColumn: boolean }[] {
|
||||
const columnMap = new Map<
|
||||
string,
|
||||
{ name: string; queryName: string; isValueColumn: boolean }
|
||||
>();
|
||||
|
||||
scalarData.forEach((scalar) => {
|
||||
scalar.columns.forEach((col) => {
|
||||
if (col.columnType === 'group') {
|
||||
// For group columns, use the column name as-is
|
||||
const key = `${col.name}_group`;
|
||||
if (!columnMap.has(key)) {
|
||||
columnMap.set(key, {
|
||||
name: col.name,
|
||||
queryName: '', // Group columns don't have query names
|
||||
isValueColumn: false,
|
||||
});
|
||||
}
|
||||
} else if (col.columnType === 'aggregation') {
|
||||
// For aggregation columns, use the query name as the column name
|
||||
const key = `${col.queryName}_aggregation`;
|
||||
if (!columnMap.has(key)) {
|
||||
columnMap.set(key, {
|
||||
name: col.queryName, // Use query name as column name (A, B, etc.)
|
||||
queryName: col.queryName,
|
||||
isValueColumn: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return Array.from(columnMap.values()).sort((a, b) => {
|
||||
if (a.isValueColumn !== b.isValueColumn) {
|
||||
return a.isValueColumn ? 1 : -1;
|
||||
}
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to process scalar data rows with unified table structure
|
||||
*/
|
||||
function processScalarDataRows(
|
||||
scalarData: ScalarData[],
|
||||
): { data: Record<string, any> }[] {
|
||||
// First, identify all group columns and all value columns
|
||||
const allGroupColumns = new Set<string>();
|
||||
const allValueColumns = new Set<string>();
|
||||
|
||||
scalarData.forEach((scalar) => {
|
||||
scalar.columns.forEach((col) => {
|
||||
if (col.columnType === 'group') {
|
||||
allGroupColumns.add(col.name);
|
||||
} else if (col.columnType === 'aggregation') {
|
||||
// Use query name for value columns to match expected format
|
||||
allValueColumns.add(col.queryName);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Create a unified row structure
|
||||
const unifiedRows = new Map<string, Record<string, any>>();
|
||||
|
||||
// Process each scalar result
|
||||
scalarData.forEach((scalar) => {
|
||||
scalar.data.forEach((dataRow) => {
|
||||
const groupColumns = scalar.columns.filter(
|
||||
(col) => col.columnType === 'group',
|
||||
);
|
||||
|
||||
// Create row key based on group columns
|
||||
let rowKey: string;
|
||||
const groupValues: Record<string, any> = {};
|
||||
|
||||
if (groupColumns.length > 0) {
|
||||
const keyParts: string[] = [];
|
||||
groupColumns.forEach((col, index) => {
|
||||
const value = dataRow[index];
|
||||
keyParts.push(String(value));
|
||||
groupValues[col.name] = value;
|
||||
});
|
||||
rowKey = keyParts.join('|');
|
||||
} else {
|
||||
// For scalar values without grouping, create a default row
|
||||
rowKey = 'default_row';
|
||||
// Set all group columns to 'n/a' for this row
|
||||
Array.from(allGroupColumns).forEach((groupCol) => {
|
||||
groupValues[groupCol] = 'n/a';
|
||||
});
|
||||
}
|
||||
|
||||
// Get or create the unified row
|
||||
if (!unifiedRows.has(rowKey)) {
|
||||
const newRow: Record<string, any> = { ...groupValues };
|
||||
// Initialize all value columns to 'n/a'
|
||||
Array.from(allValueColumns).forEach((valueCol) => {
|
||||
newRow[valueCol] = 'n/a';
|
||||
});
|
||||
unifiedRows.set(rowKey, newRow);
|
||||
}
|
||||
|
||||
const row = unifiedRows.get(rowKey)!;
|
||||
|
||||
// Fill in the aggregation values using query name as column name
|
||||
scalar.columns.forEach((col, colIndex) => {
|
||||
if (col.columnType === 'aggregation') {
|
||||
row[col.queryName] = dataRow[colIndex];
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return Array.from(unifiedRows.values()).map((rowData) => ({
|
||||
data: rowData,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts V5 ScalarData array to legacy format with table structure
|
||||
*/
|
||||
function convertScalarDataArrayToTable(
|
||||
scalarDataArray: ScalarData[],
|
||||
legendMap: Record<string, string>,
|
||||
): QueryDataV3 {
|
||||
): QueryDataV3[] {
|
||||
// If no scalar data, return empty structure
|
||||
|
||||
if (!scalarDataArray || scalarDataArray.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Process each scalar data separately to maintain query separation
|
||||
return scalarDataArray?.map((scalarData) => {
|
||||
// Get query name from the first column
|
||||
const queryName = scalarData?.columns?.[0]?.queryName || '';
|
||||
|
||||
// Collect columns for this specific query
|
||||
const columns = scalarData?.columns?.map((col) => ({
|
||||
name: col.columnType === 'aggregation' ? col.queryName : col.name,
|
||||
queryName: col.queryName,
|
||||
isValueColumn: col.columnType === 'aggregation',
|
||||
}));
|
||||
|
||||
// Process rows for this specific query
|
||||
const rows = scalarData?.data?.map((dataRow) => {
|
||||
const rowData: Record<string, any> = {};
|
||||
|
||||
scalarData?.columns?.forEach((col, colIndex) => {
|
||||
const columnName =
|
||||
col.columnType === 'aggregation' ? col.queryName : col.name;
|
||||
rowData[columnName] = dataRow[colIndex];
|
||||
});
|
||||
|
||||
return { data: rowData };
|
||||
});
|
||||
|
||||
return {
|
||||
queryName: '',
|
||||
legend: '',
|
||||
queryName,
|
||||
legend: legendMap[queryName] || '',
|
||||
series: null,
|
||||
list: null,
|
||||
table: {
|
||||
columns: [],
|
||||
rows: [],
|
||||
columns,
|
||||
rows,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Collect columns and process rows
|
||||
const columns = collectColumnsFromScalarData(scalarDataArray);
|
||||
const rows = processScalarDataRows(scalarDataArray);
|
||||
|
||||
// Get the primary query name
|
||||
const primaryQuery = scalarDataArray.find((s) =>
|
||||
s.columns.some((c) => c.columnType === 'aggregation'),
|
||||
);
|
||||
const queryName =
|
||||
primaryQuery?.columns.find((c) => c.columnType === 'aggregation')
|
||||
?.queryName ||
|
||||
scalarDataArray[0]?.columns[0]?.queryName ||
|
||||
'';
|
||||
|
||||
return {
|
||||
queryName,
|
||||
legend: legendMap[queryName] || queryName,
|
||||
series: null,
|
||||
list: null,
|
||||
table: {
|
||||
columns,
|
||||
rows,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -268,11 +149,11 @@ function convertV5DataByType(
|
||||
}
|
||||
case 'scalar': {
|
||||
const scalarData = v5Data.data.results as ScalarData[];
|
||||
// For scalar data, combine all results into a single table
|
||||
const combinedTable = convertScalarDataArrayToTable(scalarData, legendMap);
|
||||
// For scalar data, combine all results into separate table entries
|
||||
const combinedTables = convertScalarDataArrayToTable(scalarData, legendMap);
|
||||
return {
|
||||
resultType: 'scalar',
|
||||
result: [combinedTable],
|
||||
result: combinedTables,
|
||||
};
|
||||
}
|
||||
case 'raw': {
|
||||
@ -339,7 +220,7 @@ export function convertV5ResponseToLegacy(
|
||||
// If metric names is an empty object
|
||||
if (isEmpty(queryData.metric)) {
|
||||
// If metrics list is empty && the user haven't defined a legend then add the legend equal to the name of the query.
|
||||
if (!newQueryData.legend) {
|
||||
if (newQueryData.legend === undefined || newQueryData.legend === null) {
|
||||
newQueryData.legend = queryData.queryName;
|
||||
}
|
||||
// If name of the query and the legend if inserted is same then add the same to the metrics object.
|
||||
|
||||
@ -25,6 +25,7 @@ import {
|
||||
RequestType,
|
||||
TelemetryFieldKey,
|
||||
TraceAggregation,
|
||||
VariableItem,
|
||||
} from 'types/api/v5/queryRange';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
@ -316,13 +317,12 @@ export const prepareQueryRangePayloadV5 = ({
|
||||
variables = {},
|
||||
start: startTime,
|
||||
end: endTime,
|
||||
formatForWeb,
|
||||
}: GetQueryResultsProps): PrepareQueryRangePayloadV5Result => {
|
||||
let legendMap: Record<string, string> = {};
|
||||
const requestType = mapPanelTypeToRequestType(graphType);
|
||||
let queries: QueryEnvelope[] = [];
|
||||
|
||||
console.log('query', query);
|
||||
|
||||
switch (query.queryType) {
|
||||
case EQueryType.QUERY_BUILDER: {
|
||||
const { queryData: data, queryFormulas } = query.builder;
|
||||
@ -380,7 +380,13 @@ export const prepareQueryRangePayloadV5 = ({
|
||||
compositeQuery: {
|
||||
queries,
|
||||
},
|
||||
variables,
|
||||
formatOptions: {
|
||||
formatTableResultForUI: !!formatForWeb,
|
||||
},
|
||||
variables: Object.entries(variables).reduce((acc, [key, value]) => {
|
||||
acc[key] = { value };
|
||||
return acc;
|
||||
}, {} as Record<string, VariableItem>),
|
||||
};
|
||||
|
||||
return { legendMap, queryPayload };
|
||||
|
||||
@ -69,6 +69,30 @@ const conjunctions = [
|
||||
{ label: 'OR', value: 'OR ' },
|
||||
];
|
||||
|
||||
// Custom extension to stop events from propagating to global shortcuts
|
||||
const stopEventsExtension = EditorView.domEventHandlers({
|
||||
keydown: (event) => {
|
||||
// Stop all keyboard events from propagating to global shortcuts
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
return false; // Important for CM to know you handled it
|
||||
},
|
||||
input: (event) => {
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
},
|
||||
focus: (event) => {
|
||||
// Ensure focus events don't interfere with global shortcuts
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
},
|
||||
blur: (event) => {
|
||||
// Ensure blur events don't interfere with global shortcuts
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
function HavingFilter({
|
||||
onClose,
|
||||
onChange,
|
||||
@ -82,6 +106,11 @@ function HavingFilter({
|
||||
const [input, setInput] = useState(
|
||||
queryData?.havingExpression?.expression || '',
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setInput(queryData?.havingExpression?.expression || '');
|
||||
}, [queryData?.havingExpression?.expression]);
|
||||
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
|
||||
const editorRef = useRef<EditorView | null>(null);
|
||||
@ -316,6 +345,7 @@ function HavingFilter({
|
||||
extensions={[
|
||||
havingAutocomplete,
|
||||
javascript({ jsx: false, typescript: false }),
|
||||
stopEventsExtension,
|
||||
keymap.of([
|
||||
...completionKeymap,
|
||||
{
|
||||
|
||||
@ -48,10 +48,12 @@ function QueryAggregationOptions({
|
||||
<div className="query-aggregation-interval-label">every</div>
|
||||
<div className="query-aggregation-interval-input-container">
|
||||
<InputWithLabel
|
||||
initialValue={queryData.stepInterval ? queryData.stepInterval : '60'}
|
||||
initialValue={
|
||||
queryData.stepInterval ? queryData.stepInterval : undefined
|
||||
}
|
||||
className="query-aggregation-interval-input"
|
||||
label="Seconds"
|
||||
placeholder="60"
|
||||
placeholder="Auto"
|
||||
type="number"
|
||||
onChange={handleAggregationIntervalChange}
|
||||
labelAfter
|
||||
|
||||
@ -112,6 +112,30 @@ function getFunctionContextAtCursor(
|
||||
return null;
|
||||
}
|
||||
|
||||
// Custom extension to stop events from propagating to global shortcuts
|
||||
const stopEventsExtension = EditorView.domEventHandlers({
|
||||
keydown: (event) => {
|
||||
// Stop all keyboard events from propagating to global shortcuts
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
return false; // Important for CM to know you handled it
|
||||
},
|
||||
input: (event) => {
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
},
|
||||
focus: (event) => {
|
||||
// Ensure focus events don't interfere with global shortcuts
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
},
|
||||
blur: (event) => {
|
||||
// Ensure blur events don't interfere with global shortcuts
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
// eslint-disable-next-line react/no-this-in-sfc
|
||||
function QueryAggregationSelect({
|
||||
onChange,
|
||||
@ -125,6 +149,13 @@ function QueryAggregationSelect({
|
||||
const [input, setInput] = useState(
|
||||
queryData?.aggregations?.map((i: any) => i.expression).join(' ') || '',
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setInput(
|
||||
queryData?.aggregations?.map((i: any) => i.expression).join(' ') || '',
|
||||
);
|
||||
}, [queryData?.aggregations]);
|
||||
|
||||
const [cursorPos, setCursorPos] = useState(0);
|
||||
const [functionArgPairs, setFunctionArgPairs] = useState<
|
||||
{ func: string; arg: string }[]
|
||||
@ -457,6 +488,7 @@ function QueryAggregationSelect({
|
||||
aggregatorAutocomplete,
|
||||
javascript({ jsx: false, typescript: false }),
|
||||
EditorView.lineWrapping,
|
||||
stopEventsExtension,
|
||||
keymap.of([
|
||||
...completionKeymap,
|
||||
{
|
||||
|
||||
@ -41,14 +41,25 @@ const { Panel } = Collapse;
|
||||
// Custom extension to stop events
|
||||
const stopEventsExtension = EditorView.domEventHandlers({
|
||||
keydown: (event) => {
|
||||
// Stop all keyboard events from propagating to global shortcuts
|
||||
event.stopPropagation();
|
||||
// Optionally: event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
return false; // Important for CM to know you handled it
|
||||
},
|
||||
input: (event) => {
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
},
|
||||
focus: (event) => {
|
||||
// Ensure focus events don't interfere with global shortcuts
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
},
|
||||
blur: (event) => {
|
||||
// Ensure blur events don't interfere with global shortcuts
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
const disallowMultipleSpaces: Extension = EditorView.inputHandler.of(
|
||||
@ -86,6 +97,10 @@ function QuerySearch({
|
||||
errors: [],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setQuery(queryData.filter?.expression || '');
|
||||
}, [queryData.filter?.expression]);
|
||||
|
||||
const [keySuggestions, setKeySuggestions] = useState<
|
||||
QueryKeyDataSuggestionsProps[] | null
|
||||
>(null);
|
||||
|
||||
@ -8,16 +8,11 @@ import SpanScopeSelector from 'container/QueryBuilder/filters/QueryBuilderSearch
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
|
||||
import { Copy, Ellipsis, Trash } from 'lucide-react';
|
||||
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { HandleChangeQueryDataV5 } from 'types/common/operations.types';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import {
|
||||
convertAggregationToExpression,
|
||||
convertFiltersToExpression,
|
||||
convertHavingToExpression,
|
||||
} from '../utils';
|
||||
import MetricsAggregateSection from './MerticsAggregateSection/MetricsAggregateSection';
|
||||
import { MetricsSelect } from './MetricsSelect/MetricsSelect';
|
||||
import QueryAddOns from './QueryAddOns/QueryAddOns';
|
||||
@ -54,44 +49,6 @@ export const QueryV2 = memo(function QueryV2({
|
||||
entityVersion: version,
|
||||
});
|
||||
|
||||
// Convert old format to new format and update query when component mounts or query changes
|
||||
const performQueryConversions = useCallback(() => {
|
||||
// Convert filters if needed
|
||||
if (query.filters?.items?.length > 0 && !query.filter?.expression) {
|
||||
const convertedFilter = convertFiltersToExpression(query.filters);
|
||||
handleChangeQueryData('filter', convertedFilter);
|
||||
}
|
||||
|
||||
// Convert having if needed
|
||||
if (query.having?.length > 0 && !query.havingExpression?.expression) {
|
||||
const convertedHaving = convertHavingToExpression(query.having);
|
||||
handleChangeQueryData('havingExpression', convertedHaving);
|
||||
}
|
||||
|
||||
// Convert aggregation if needed
|
||||
if (!query.aggregations && query.aggregateOperator) {
|
||||
const convertedAggregation = convertAggregationToExpression(
|
||||
query.aggregateOperator,
|
||||
query.aggregateAttribute,
|
||||
query.dataSource,
|
||||
query.timeAggregation,
|
||||
query.spaceAggregation,
|
||||
) as any; // Type assertion to handle union type
|
||||
handleChangeQueryData('aggregations', convertedAggregation);
|
||||
}
|
||||
}, [query, handleChangeQueryData]);
|
||||
|
||||
useEffect(() => {
|
||||
const needsConversion =
|
||||
(query.filters?.items?.length > 0 && !query.filter?.expression) ||
|
||||
(query.having?.length > 0 && !query.havingExpression?.expression) ||
|
||||
(!query.aggregations && query.aggregateOperator);
|
||||
|
||||
if (needsConversion) {
|
||||
performQueryConversions();
|
||||
}
|
||||
}, [performQueryConversions, query]);
|
||||
|
||||
const handleToggleDisableQuery = useCallback(() => {
|
||||
handleChangeQueryData('disabled', !query.disabled);
|
||||
}, [handleChangeQueryData, query]);
|
||||
@ -208,67 +165,69 @@ export const QueryV2 = memo(function QueryV2({
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="qb-elements-container">
|
||||
<div className="qb-search-container">
|
||||
{dataSource === DataSource.METRICS && (
|
||||
<div className="metrics-select-container">
|
||||
<MetricsSelect query={query} index={0} version="v4" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="qb-search-filter-container">
|
||||
<div className="query-search-container">
|
||||
<QuerySearch
|
||||
key={`query-search-${query.queryName}-${query.dataSource}`}
|
||||
onChange={handleSearchChange}
|
||||
queryData={query}
|
||||
dataSource={dataSource}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{showSpanScopeSelector && (
|
||||
<div className="traces-search-filter-container">
|
||||
<div className="traces-search-filter-in">in</div>
|
||||
<SpanScopeSelector query={query} />
|
||||
{!isCollapsed && (
|
||||
<div className="qb-elements-container">
|
||||
<div className="qb-search-container">
|
||||
{dataSource === DataSource.METRICS && (
|
||||
<div className="metrics-select-container">
|
||||
<MetricsSelect query={query} index={index} version="v4" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!showOnlyWhereClause &&
|
||||
!isListViewPanel &&
|
||||
dataSource !== DataSource.METRICS && (
|
||||
<QueryAggregation
|
||||
dataSource={dataSource}
|
||||
key={`query-search-${query.queryName}-${query.dataSource}`}
|
||||
panelType={panelType || undefined}
|
||||
onAggregationIntervalChange={handleChangeAggregateEvery}
|
||||
onChange={handleChangeAggregation}
|
||||
queryData={query}
|
||||
<div className="qb-search-filter-container">
|
||||
<div className="query-search-container">
|
||||
<QuerySearch
|
||||
key={`query-search-${query.queryName}-${query.dataSource}`}
|
||||
onChange={handleSearchChange}
|
||||
queryData={query}
|
||||
dataSource={dataSource}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{showSpanScopeSelector && (
|
||||
<div className="traces-search-filter-container">
|
||||
<div className="traces-search-filter-in">in</div>
|
||||
<SpanScopeSelector query={query} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!showOnlyWhereClause &&
|
||||
!isListViewPanel &&
|
||||
dataSource !== DataSource.METRICS && (
|
||||
<QueryAggregation
|
||||
dataSource={dataSource}
|
||||
key={`query-search-${query.queryName}-${query.dataSource}`}
|
||||
panelType={panelType || undefined}
|
||||
onAggregationIntervalChange={handleChangeAggregateEvery}
|
||||
onChange={handleChangeAggregation}
|
||||
queryData={query}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!showOnlyWhereClause && dataSource === DataSource.METRICS && (
|
||||
<MetricsAggregateSection
|
||||
panelType={panelType}
|
||||
query={query}
|
||||
index={index}
|
||||
key={`metrics-aggregate-section-${query.queryName}-${query.dataSource}`}
|
||||
version="v4"
|
||||
/>
|
||||
)}
|
||||
|
||||
{!showOnlyWhereClause && dataSource === DataSource.METRICS && (
|
||||
<MetricsAggregateSection
|
||||
panelType={panelType}
|
||||
query={query}
|
||||
index={0}
|
||||
key={`metrics-aggregate-section-${query.queryName}-${query.dataSource}`}
|
||||
version="v4"
|
||||
/>
|
||||
)}
|
||||
|
||||
{!showOnlyWhereClause && (
|
||||
<QueryAddOns
|
||||
index={index}
|
||||
query={query}
|
||||
version="v3"
|
||||
isListViewPanel={isListViewPanel}
|
||||
showReduceTo={showReduceTo}
|
||||
panelType={panelType}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{!showOnlyWhereClause && (
|
||||
<QueryAddOns
|
||||
index={index}
|
||||
query={query}
|
||||
version="v3"
|
||||
isListViewPanel={isListViewPanel}
|
||||
showReduceTo={showReduceTo}
|
||||
panelType={panelType}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -169,6 +169,9 @@ export const initialQueryBuilderFormValues: IBuilderQuery = {
|
||||
aggregateAttribute: initialAutocompleteData,
|
||||
timeAggregation: MetricAggregateOperator.RATE,
|
||||
spaceAggregation: MetricAggregateOperator.SUM,
|
||||
filter: { expression: '' },
|
||||
aggregations: [{ expression: 'count() ' }],
|
||||
havingExpression: { expression: '' },
|
||||
functions: [],
|
||||
filters: { items: [], op: 'AND' },
|
||||
expression: createNewBuilderItemName({
|
||||
|
||||
@ -54,19 +54,21 @@ function DomainList(): JSX.Element {
|
||||
|
||||
// initialise tab with default query.
|
||||
useShareBuilderUrl({
|
||||
...initialQueriesMap.traces,
|
||||
builder: {
|
||||
...initialQueriesMap.traces.builder,
|
||||
queryData: [
|
||||
{
|
||||
...initialQueriesMap.traces.builder.queryData[0],
|
||||
dataSource: DataSource.TRACES,
|
||||
aggregateOperator: 'noop',
|
||||
aggregateAttribute: {
|
||||
...initialQueriesMap.traces.builder.queryData[0].aggregateAttribute,
|
||||
defaultValue: {
|
||||
...initialQueriesMap.traces,
|
||||
builder: {
|
||||
...initialQueriesMap.traces.builder,
|
||||
queryData: [
|
||||
{
|
||||
...initialQueriesMap.traces.builder.queryData[0],
|
||||
dataSource: DataSource.TRACES,
|
||||
aggregateOperator: 'noop',
|
||||
aggregateAttribute: {
|
||||
...initialQueriesMap.traces.builder.queryData[0].aggregateAttribute,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ import { QueryParams } from 'constants/query';
|
||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import AnomalyAlertEvaluationView from 'container/AnomalyAlertEvaluationView';
|
||||
import GridPanelSwitch from 'container/GridPanelSwitch';
|
||||
import { populateMultipleResults } from 'container/NewWidget/LeftContainer/WidgetGraph/util';
|
||||
import { getFormatNameByOptionId } from 'container/NewWidget/RightContainer/alertFomatCategories';
|
||||
import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems';
|
||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||
@ -176,6 +177,12 @@ function ChartPreview({
|
||||
queryResponse.data.payload.data.result = sortedSeriesData;
|
||||
}
|
||||
|
||||
if (queryResponse.data && graphType === PANEL_TYPES.PIE) {
|
||||
const transformedData = populateMultipleResults(queryResponse?.data);
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
queryResponse.data = transformedData;
|
||||
}
|
||||
|
||||
const containerDimensions = useResizeObserver(graphRef);
|
||||
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
@ -162,7 +162,7 @@ function FormAlertRules({
|
||||
|
||||
const sq = useMemo(() => mapQueryDataFromApi(initQuery), [initQuery]);
|
||||
|
||||
useShareBuilderUrl(sq);
|
||||
useShareBuilderUrl({ defaultValue: sq });
|
||||
|
||||
const handleDetectionMethodChange = (value: string): void => {
|
||||
setAlertDef((def) => ({
|
||||
@ -832,7 +832,7 @@ function FormAlertRules({
|
||||
queryCategory={currentQuery.queryType}
|
||||
setQueryCategory={onQueryCategoryChange}
|
||||
alertType={alertType || AlertTypes.METRICS_BASED_ALERT}
|
||||
runQuery={(): void => handleRunQuery(true)}
|
||||
runQuery={(): void => handleRunQuery(true, true)}
|
||||
alertDef={alertDef}
|
||||
panelType={panelType || PANEL_TYPES.TIME_SERIES}
|
||||
key={currentQuery.queryType}
|
||||
|
||||
@ -13,6 +13,7 @@ import TimePreference from 'components/TimePreferenceDropDown';
|
||||
import { ENTITY_VERSION_V5 } from 'constants/app';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { populateMultipleResults } from 'container/NewWidget/LeftContainer/WidgetGraph/util';
|
||||
import {
|
||||
timeItems,
|
||||
timePreferance,
|
||||
@ -179,6 +180,12 @@ function FullView({
|
||||
response.data.payload.data.result = sortedSeriesData;
|
||||
}
|
||||
|
||||
if (response.data && widget.panelTypes === PANEL_TYPES.PIE) {
|
||||
const transformedData = populateMultipleResults(response?.data);
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
response.data = transformedData;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
graphsVisibilityStates?.forEach((e, i) => {
|
||||
fullViewChartRef?.current?.toggleGraph(i, e);
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { ENTITY_VERSION_V5 } from 'constants/app';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { populateMultipleResults } from 'container/NewWidget/LeftContainer/WidgetGraph/util';
|
||||
import { CustomTimeType } from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||
import { useIntersectionObserver } from 'hooks/useIntersectionObserver';
|
||||
@ -209,8 +210,7 @@ function GridCardGraph({
|
||||
end: customTimeRange?.endTime || end,
|
||||
originalGraphType: widget?.panelTypes,
|
||||
},
|
||||
ENTITY_VERSION_V5,
|
||||
// version || DEFAULT_ENTITY_VERSION,
|
||||
version || DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
queryKey: [
|
||||
maxTime,
|
||||
@ -272,6 +272,12 @@ function GridCardGraph({
|
||||
queryResponse.data.payload.data.result = sortedSeriesData;
|
||||
}
|
||||
|
||||
if (queryResponse.data && widget.panelTypes === PANEL_TYPES.PIE) {
|
||||
const transformedData = populateMultipleResults(queryResponse?.data);
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
queryResponse.data = transformedData;
|
||||
}
|
||||
|
||||
const menuList =
|
||||
widget.panelTypes === PANEL_TYPES.TABLE ||
|
||||
widget.panelTypes === PANEL_TYPES.LIST ||
|
||||
|
||||
@ -6,6 +6,7 @@ import { Button, Form, Input, Modal, Typography } from 'antd';
|
||||
import { useForm } from 'antd/es/form/Form';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import cx from 'classnames';
|
||||
import { ENTITY_VERSION_V5 } from 'constants/app';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_GROUP_TYPES, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { themeColors } from 'constants/theme';
|
||||
@ -579,7 +580,8 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
|
||||
widget={(currentWidget as Widgets) || ({ id, query: {} } as Widgets)}
|
||||
headerMenuList={widgetActions}
|
||||
variables={variables}
|
||||
version={selectedDashboard?.data?.version}
|
||||
// version={selectedDashboard?.data?.version}
|
||||
version={ENTITY_VERSION_V5}
|
||||
onDragSelect={onDragSelect}
|
||||
dataAvailable={checkIfDataExists}
|
||||
/>
|
||||
|
||||
@ -25,7 +25,7 @@ import logEvent from 'api/common/logEvent';
|
||||
import createDashboard from 'api/v1/dashboards/create';
|
||||
import { AxiosError } from 'axios';
|
||||
import cx from 'classnames';
|
||||
import { ENTITY_VERSION_V4 } from 'constants/app';
|
||||
import { ENTITY_VERSION_V5 } from 'constants/app';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { sanitizeDashboardData } from 'container/NewDashboard/DashboardDescription';
|
||||
@ -293,7 +293,7 @@ function DashboardsList(): JSX.Element {
|
||||
ns: 'dashboard',
|
||||
}),
|
||||
uploadedGrafana: false,
|
||||
version: ENTITY_VERSION_V4,
|
||||
version: ENTITY_VERSION_V5,
|
||||
});
|
||||
|
||||
safeNavigate(
|
||||
|
||||
@ -34,7 +34,7 @@ function LogExplorerQuerySection({
|
||||
[updateAllQueriesOperators],
|
||||
);
|
||||
|
||||
useShareBuilderUrl(defaultValue);
|
||||
useShareBuilderUrl({ defaultValue });
|
||||
|
||||
const filterConfigs: QueryBuilderProps['filterConfigs'] = useMemo(() => {
|
||||
const isTable = panelTypes === PANEL_TYPES.TABLE;
|
||||
|
||||
@ -341,15 +341,15 @@ function LogsExplorerViewsContainer({
|
||||
query.builder.queryData.length > 1
|
||||
? query.builder.queryData.map((item) => ({
|
||||
...item,
|
||||
...(selectedPanelType !== PANEL_TYPES.LIST ? { order: [] } : {}),
|
||||
...(selectedView !== ExplorerViews.LIST ? { order: [] } : {}),
|
||||
}))
|
||||
: [
|
||||
{
|
||||
...(listQuery || initialQueryBuilderFormValues),
|
||||
...paginateData,
|
||||
...(updatedFilters ? { filters: updatedFilters } : {}),
|
||||
...(selectedPanelType === PANEL_TYPES.LIST
|
||||
? { order: orderBy }
|
||||
...(selectedView === ExplorerViews.LIST
|
||||
? { order: orderBy, orderBy }
|
||||
: { order: [] }),
|
||||
},
|
||||
];
|
||||
@ -364,7 +364,7 @@ function LogsExplorerViewsContainer({
|
||||
|
||||
return data;
|
||||
},
|
||||
[activeLogId, orderDirection, listQuery, selectedPanelType],
|
||||
[activeLogId, orderDirection, listQuery, selectedView],
|
||||
);
|
||||
|
||||
const handleEndReached = useCallback(() => {
|
||||
|
||||
@ -75,7 +75,7 @@ function Explorer(): JSX.Element {
|
||||
[currentQuery, updateAllQueriesOperators],
|
||||
);
|
||||
|
||||
useShareBuilderUrl(defaultQuery);
|
||||
useShareBuilderUrl({ defaultValue: defaultQuery });
|
||||
|
||||
const handleExport = useCallback(
|
||||
(
|
||||
@ -132,7 +132,9 @@ function Explorer(): JSX.Element {
|
||||
</div>
|
||||
<div className="explore-header-right-actions">
|
||||
<DateTimeSelector showAutoRefresh />
|
||||
<RightToolbarActions onStageRunQuery={handleRunQuery} />
|
||||
<RightToolbarActions
|
||||
onStageRunQuery={(): void => handleRunQuery(true, true)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* <QuerySection /> */}
|
||||
|
||||
@ -64,7 +64,7 @@ function QuerySection({
|
||||
|
||||
const { query } = selectedWidget;
|
||||
|
||||
useShareBuilderUrl(query);
|
||||
useShareBuilderUrl({ defaultValue: query });
|
||||
|
||||
const handleStageQuery = useCallback(
|
||||
(query: Query): void => {
|
||||
|
||||
@ -5,6 +5,7 @@ import { WidgetGraphContainerProps } from 'container/NewWidget/types';
|
||||
import { getSortedSeriesData } from 'utils/getSortedSeriesData';
|
||||
|
||||
import { NotFoundContainer } from './styles';
|
||||
import { populateMultipleResults } from './util';
|
||||
import WidgetGraph from './WidgetGraphs';
|
||||
|
||||
function WidgetGraphContainer({
|
||||
@ -22,6 +23,12 @@ function WidgetGraphContainer({
|
||||
queryResponse.data.payload.data.result = sortedSeriesData;
|
||||
}
|
||||
|
||||
if (queryResponse.data && selectedGraph === PANEL_TYPES.PIE) {
|
||||
const transformedData = populateMultipleResults(queryResponse?.data);
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
queryResponse.data = transformedData;
|
||||
}
|
||||
|
||||
if (selectedWidget === undefined) {
|
||||
return <Card>Invalid widget</Card>;
|
||||
}
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { QueryData, QueryDataV3 } from 'types/api/widgets/getQuery';
|
||||
|
||||
export function populateMultipleResults(
|
||||
responseData: SuccessResponse<MetricRangePayloadProps, unknown>,
|
||||
): SuccessResponse<MetricRangePayloadProps, unknown> {
|
||||
const queryResults = responseData?.payload?.data?.newResult?.data?.result;
|
||||
const allFormattedResults: QueryData[] = [];
|
||||
|
||||
queryResults?.forEach((query: QueryDataV3) => {
|
||||
const { queryName, legend, table } = query;
|
||||
if (!table) return;
|
||||
|
||||
const { columns, rows } = table;
|
||||
|
||||
const valueCol = columns?.find((c) => c.isValueColumn);
|
||||
const labelCols = columns?.filter((c) => !c.isValueColumn);
|
||||
|
||||
rows?.forEach((row) => {
|
||||
const metric: Record<string, string> = {};
|
||||
labelCols?.forEach((col) => {
|
||||
metric[col.name] = String(row.data[col.name]);
|
||||
});
|
||||
|
||||
allFormattedResults.push({
|
||||
metric,
|
||||
values: [[0, String(row.data[valueCol!.name])]],
|
||||
queryName,
|
||||
legend: legend || '',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Create a copy instead of mutating the original
|
||||
const updatedResponseData: SuccessResponse<
|
||||
MetricRangePayloadProps,
|
||||
unknown
|
||||
> = {
|
||||
...responseData,
|
||||
payload: {
|
||||
...responseData.payload,
|
||||
data: {
|
||||
...responseData.payload.data,
|
||||
result: allFormattedResults,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return updatedResponseData;
|
||||
}
|
||||
@ -581,7 +581,11 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
|
||||
const setGraphHandler = (type: PANEL_TYPES): void => {
|
||||
setIsLoadingPanelData(true);
|
||||
const updatedQuery = handleQueryChange(type as any, supersetQuery);
|
||||
const updatedQuery = handleQueryChange(
|
||||
type as any,
|
||||
supersetQuery,
|
||||
selectedGraph,
|
||||
);
|
||||
setGraphType(type);
|
||||
redirectWithQueryBuilderData(
|
||||
updatedQuery,
|
||||
|
||||
@ -56,9 +56,11 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'aggregateAttribute',
|
||||
'aggregateOperator',
|
||||
'filters',
|
||||
'filter',
|
||||
'groupBy',
|
||||
'limit',
|
||||
'having',
|
||||
'havingExpression',
|
||||
'orderBy',
|
||||
'functions',
|
||||
'stepInterval',
|
||||
@ -66,6 +68,7 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'queryName',
|
||||
'legend',
|
||||
'expression',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -76,10 +79,12 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'aggregateOperator',
|
||||
'timeAggregation',
|
||||
'filters',
|
||||
'filter',
|
||||
'spaceAggregation',
|
||||
'groupBy',
|
||||
'limit',
|
||||
'having',
|
||||
'havingExpression',
|
||||
'orderBy',
|
||||
'stepInterval',
|
||||
'legend',
|
||||
@ -87,6 +92,7 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'disabled',
|
||||
'functions',
|
||||
'expression',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -96,9 +102,11 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'aggregateAttribute',
|
||||
'aggregateOperator',
|
||||
'filters',
|
||||
'filter',
|
||||
'groupBy',
|
||||
'limit',
|
||||
'having',
|
||||
'havingExpression',
|
||||
'orderBy',
|
||||
'functions',
|
||||
'stepInterval',
|
||||
@ -106,6 +114,7 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'queryName',
|
||||
'legend',
|
||||
'expression',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -117,9 +126,11 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'aggregateAttribute',
|
||||
'aggregateOperator',
|
||||
'filters',
|
||||
'filter',
|
||||
'groupBy',
|
||||
'limit',
|
||||
'having',
|
||||
'havingExpression',
|
||||
'orderBy',
|
||||
'functions',
|
||||
'stepInterval',
|
||||
@ -127,6 +138,7 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'queryName',
|
||||
'legend',
|
||||
'expression',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -137,10 +149,12 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'aggregateOperator',
|
||||
'timeAggregation',
|
||||
'filters',
|
||||
'filter',
|
||||
'spaceAggregation',
|
||||
'groupBy',
|
||||
'limit',
|
||||
'having',
|
||||
'havingExpression',
|
||||
'orderBy',
|
||||
'stepInterval',
|
||||
'legend',
|
||||
@ -148,6 +162,7 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'disabled',
|
||||
'functions',
|
||||
'expression',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -157,9 +172,11 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'aggregateAttribute',
|
||||
'aggregateOperator',
|
||||
'filters',
|
||||
'filter',
|
||||
'groupBy',
|
||||
'limit',
|
||||
'having',
|
||||
'havingExpression',
|
||||
'orderBy',
|
||||
'functions',
|
||||
'stepInterval',
|
||||
@ -167,6 +184,7 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'queryName',
|
||||
'legend',
|
||||
'expression',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -178,9 +196,11 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'aggregateAttribute',
|
||||
'aggregateOperator',
|
||||
'filters',
|
||||
'filter',
|
||||
'groupBy',
|
||||
'limit',
|
||||
'having',
|
||||
'havingExpression',
|
||||
'orderBy',
|
||||
'functions',
|
||||
'stepInterval',
|
||||
@ -188,6 +208,7 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'queryName',
|
||||
'legend',
|
||||
'expression',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -198,10 +219,12 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'aggregateOperator',
|
||||
'timeAggregation',
|
||||
'filters',
|
||||
'filter',
|
||||
'spaceAggregation',
|
||||
'groupBy',
|
||||
'limit',
|
||||
'having',
|
||||
'havingExpression',
|
||||
'orderBy',
|
||||
'stepInterval',
|
||||
'legend',
|
||||
@ -209,6 +232,7 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'disabled',
|
||||
'functions',
|
||||
'expression',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -218,9 +242,11 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'aggregateAttribute',
|
||||
'aggregateOperator',
|
||||
'filters',
|
||||
'filter',
|
||||
'groupBy',
|
||||
'limit',
|
||||
'having',
|
||||
'havingExpression',
|
||||
'orderBy',
|
||||
'functions',
|
||||
'stepInterval',
|
||||
@ -228,6 +254,7 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'queryName',
|
||||
'legend',
|
||||
'expression',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -239,9 +266,11 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'aggregateAttribute',
|
||||
'aggregateOperator',
|
||||
'filters',
|
||||
'filter',
|
||||
'groupBy',
|
||||
'limit',
|
||||
'having',
|
||||
'havingExpression',
|
||||
'orderBy',
|
||||
'functions',
|
||||
'stepInterval',
|
||||
@ -249,6 +278,7 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'queryName',
|
||||
'expression',
|
||||
'legend',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -259,11 +289,13 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'aggregateOperator',
|
||||
'timeAggregation',
|
||||
'filters',
|
||||
'filter',
|
||||
'spaceAggregation',
|
||||
'groupBy',
|
||||
'reduceTo',
|
||||
'limit',
|
||||
'having',
|
||||
'havingExpression',
|
||||
'orderBy',
|
||||
'stepInterval',
|
||||
'legend',
|
||||
@ -271,6 +303,7 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'expression',
|
||||
'disabled',
|
||||
'functions',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -280,9 +313,11 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'aggregateAttribute',
|
||||
'aggregateOperator',
|
||||
'filters',
|
||||
'filter',
|
||||
'groupBy',
|
||||
'limit',
|
||||
'having',
|
||||
'havingExpression',
|
||||
'orderBy',
|
||||
'functions',
|
||||
'stepInterval',
|
||||
@ -290,6 +325,7 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'queryName',
|
||||
'expression',
|
||||
'legend',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -301,9 +337,11 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'aggregateAttribute',
|
||||
'aggregateOperator',
|
||||
'filters',
|
||||
'filter',
|
||||
'groupBy',
|
||||
'limit',
|
||||
'having',
|
||||
'havingExpression',
|
||||
'orderBy',
|
||||
'functions',
|
||||
'stepInterval',
|
||||
@ -311,6 +349,7 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'queryName',
|
||||
'expression',
|
||||
'legend',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -321,11 +360,13 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'aggregateOperator',
|
||||
'timeAggregation',
|
||||
'filters',
|
||||
'filter',
|
||||
'spaceAggregation',
|
||||
'groupBy',
|
||||
'reduceTo',
|
||||
'limit',
|
||||
'having',
|
||||
'havingExpression',
|
||||
'orderBy',
|
||||
'stepInterval',
|
||||
'legend',
|
||||
@ -333,6 +374,7 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'expression',
|
||||
'disabled',
|
||||
'functions',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -342,9 +384,11 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'aggregateAttribute',
|
||||
'aggregateOperator',
|
||||
'filters',
|
||||
'filter',
|
||||
'groupBy',
|
||||
'limit',
|
||||
'having',
|
||||
'havingExpression',
|
||||
'orderBy',
|
||||
'functions',
|
||||
'stepInterval',
|
||||
@ -352,6 +396,7 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'queryName',
|
||||
'expression',
|
||||
'legend',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -359,17 +404,31 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
[PANEL_TYPES.LIST]: {
|
||||
[DataSource.LOGS]: {
|
||||
builder: {
|
||||
queryData: ['filters', 'limit', 'orderBy', 'functions'],
|
||||
queryData: [
|
||||
'filters',
|
||||
'filter',
|
||||
'limit',
|
||||
'orderBy',
|
||||
'functions',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
[DataSource.METRICS]: {
|
||||
builder: {
|
||||
queryData: [],
|
||||
queryData: ['filters', 'filter', 'aggregations'],
|
||||
},
|
||||
},
|
||||
[DataSource.TRACES]: {
|
||||
builder: {
|
||||
queryData: ['filters', 'limit', 'orderBy', 'functions'],
|
||||
queryData: [
|
||||
'filters',
|
||||
'filter',
|
||||
'limit',
|
||||
'orderBy',
|
||||
'functions',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -380,14 +439,17 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'aggregateAttribute',
|
||||
'aggregateOperator',
|
||||
'filters',
|
||||
'filter',
|
||||
'reduceTo',
|
||||
'having',
|
||||
'havingExpression',
|
||||
'functions',
|
||||
'stepInterval',
|
||||
'queryName',
|
||||
'expression',
|
||||
'disabled',
|
||||
'legend',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -398,8 +460,10 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'aggregateOperator',
|
||||
'timeAggregation',
|
||||
'filters',
|
||||
'filter',
|
||||
'spaceAggregation',
|
||||
'having',
|
||||
'havingExpression',
|
||||
'reduceTo',
|
||||
'stepInterval',
|
||||
'legend',
|
||||
@ -407,6 +471,7 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'expression',
|
||||
'disabled',
|
||||
'functions',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -416,14 +481,17 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
'aggregateAttribute',
|
||||
'aggregateOperator',
|
||||
'filters',
|
||||
'filter',
|
||||
'reduceTo',
|
||||
'having',
|
||||
'havingExpression',
|
||||
'functions',
|
||||
'stepInterval',
|
||||
'queryName',
|
||||
'expression',
|
||||
'disabled',
|
||||
'legend',
|
||||
'aggregations',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -433,7 +501,9 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
export function handleQueryChange(
|
||||
newPanelType: keyof PartialPanelTypes,
|
||||
supersetQuery: Query,
|
||||
currentPanelType: PANEL_TYPES,
|
||||
): Query {
|
||||
console.log('supersetQuery', supersetQuery);
|
||||
return {
|
||||
...supersetQuery,
|
||||
builder: {
|
||||
@ -454,6 +524,7 @@ export function handleQueryChange(
|
||||
set(tempQuery, 'aggregateOperator', 'noop');
|
||||
set(tempQuery, 'offset', 0);
|
||||
set(tempQuery, 'pageSize', 10);
|
||||
set(tempQuery, 'orderBy', undefined);
|
||||
} else if (tempQuery.aggregateOperator === 'noop') {
|
||||
// this condition takes care of the part where we start with the list panel type and then shift to other panels
|
||||
// because in other cases we never set list operator and other fields in superset query rather just update in the current / staged query
|
||||
@ -462,6 +533,13 @@ export function handleQueryChange(
|
||||
unset(tempQuery, 'pageSize');
|
||||
}
|
||||
|
||||
if (
|
||||
currentPanelType === PANEL_TYPES.LIST &&
|
||||
newPanelType !== PANEL_TYPES.LIST
|
||||
) {
|
||||
set(tempQuery, 'orderBy', undefined);
|
||||
}
|
||||
|
||||
return tempQuery;
|
||||
}),
|
||||
},
|
||||
|
||||
@ -450,7 +450,7 @@ function QueryBuilderSearchV2(
|
||||
if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
handleRunQuery();
|
||||
handleRunQuery(false, true);
|
||||
setIsOpen(false);
|
||||
}
|
||||
},
|
||||
|
||||
@ -21,21 +21,23 @@ function ResourceAttributesFilter(): JSX.Element | null {
|
||||
|
||||
// initialise tab with default query.
|
||||
useShareBuilderUrl({
|
||||
...initialQueriesMap.traces,
|
||||
builder: {
|
||||
...initialQueriesMap.traces.builder,
|
||||
queryData: [
|
||||
{
|
||||
...initialQueriesMap.traces.builder.queryData[0],
|
||||
dataSource: DataSource.TRACES,
|
||||
aggregateOperator: 'noop',
|
||||
aggregateAttribute: {
|
||||
...initialQueriesMap.traces.builder.queryData[0].aggregateAttribute,
|
||||
type: 'resource',
|
||||
defaultValue: {
|
||||
...initialQueriesMap.traces,
|
||||
builder: {
|
||||
...initialQueriesMap.traces.builder,
|
||||
queryData: [
|
||||
{
|
||||
...initialQueriesMap.traces.builder.queryData[0],
|
||||
dataSource: DataSource.TRACES,
|
||||
aggregateOperator: 'noop',
|
||||
aggregateAttribute: {
|
||||
...initialQueriesMap.traces.builder.queryData[0].aggregateAttribute,
|
||||
type: 'resource',
|
||||
},
|
||||
queryName: '',
|
||||
},
|
||||
queryName: '',
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -31,7 +31,7 @@ const KeyboardHotkeysContext = createContext<KeyboardHotkeysContextReturnValue>(
|
||||
},
|
||||
);
|
||||
|
||||
const IGNORE_INPUTS = ['input', 'textarea']; // Inputs in which hotkey events will be ignored
|
||||
const IGNORE_INPUTS = ['input', 'textarea', 'cm-editor']; // Inputs in which hotkey events will be ignored
|
||||
|
||||
const useKeyboardHotkeys = (): KeyboardHotkeysContextReturnValue => {
|
||||
const context = useContext(KeyboardHotkeysContext);
|
||||
@ -54,7 +54,13 @@ function KeyboardHotkeysProvider({
|
||||
const handleKeyPress = (event: KeyboardEvent): void => {
|
||||
const { key, ctrlKey, altKey, shiftKey, metaKey, target } = event;
|
||||
|
||||
if (IGNORE_INPUTS.includes((target as HTMLElement).tagName.toLowerCase())) {
|
||||
const isCodeMirrorEditor =
|
||||
(target as HTMLElement).closest('.cm-editor') !== null;
|
||||
|
||||
if (
|
||||
IGNORE_INPUTS.includes((target as HTMLElement).tagName.toLowerCase()) ||
|
||||
isCodeMirrorEditor
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
import {
|
||||
convertAggregationToExpression,
|
||||
convertFiltersToExpression,
|
||||
convertHavingToExpression,
|
||||
} from 'components/QueryBuilderV2/utils';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { useMemo } from 'react';
|
||||
@ -18,6 +23,41 @@ export const useGetCompositeQueryParam = (): Query | null => {
|
||||
parsedCompositeQuery = JSON.parse(
|
||||
decodeURIComponent(compositeQuery.replace(/\+/g, ' ')),
|
||||
);
|
||||
|
||||
// Convert old format to new format for each query in builder.queryData
|
||||
if (parsedCompositeQuery?.builder?.queryData) {
|
||||
parsedCompositeQuery.builder.queryData = parsedCompositeQuery.builder.queryData.map(
|
||||
(query) => {
|
||||
const convertedQuery = { ...query };
|
||||
|
||||
// Convert filters if needed
|
||||
if (query.filters?.items?.length > 0 && !query.filter?.expression) {
|
||||
const convertedFilter = convertFiltersToExpression(query.filters);
|
||||
convertedQuery.filter = convertedFilter;
|
||||
}
|
||||
|
||||
// Convert having if needed
|
||||
if (query.having?.length > 0 && !query.havingExpression?.expression) {
|
||||
const convertedHaving = convertHavingToExpression(query.having);
|
||||
convertedQuery.havingExpression = convertedHaving;
|
||||
}
|
||||
|
||||
// Convert aggregation if needed
|
||||
if (!query.aggregations && query.aggregateOperator) {
|
||||
const convertedAggregation = convertAggregationToExpression(
|
||||
query.aggregateOperator,
|
||||
query.aggregateAttribute,
|
||||
query.dataSource,
|
||||
query.timeAggregation,
|
||||
query.spaceAggregation,
|
||||
) as any; // Type assertion to handle union type
|
||||
convertedQuery.aggregations = convertedAggregation;
|
||||
}
|
||||
|
||||
return convertedQuery;
|
||||
},
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
parsedCompositeQuery = null;
|
||||
}
|
||||
|
||||
@ -5,24 +5,32 @@ import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { useGetCompositeQueryParam } from './useGetCompositeQueryParam';
|
||||
import { useQueryBuilder } from './useQueryBuilder';
|
||||
|
||||
export type UseShareBuilderUrlParams = { defaultValue: Query };
|
||||
export type UseShareBuilderUrlParams = {
|
||||
defaultValue: Query;
|
||||
/** Force reset the query regardless of URL state */
|
||||
forceReset?: boolean;
|
||||
};
|
||||
|
||||
export const useShareBuilderUrl = (defaultQuery: Query): void => {
|
||||
export const useShareBuilderUrl = ({
|
||||
defaultValue,
|
||||
forceReset = false,
|
||||
}: UseShareBuilderUrlParams): void => {
|
||||
const { resetQuery, redirectWithQueryBuilderData } = useQueryBuilder();
|
||||
const urlQuery = useUrlQuery();
|
||||
|
||||
const compositeQuery = useGetCompositeQueryParam();
|
||||
|
||||
useEffect(() => {
|
||||
if (!compositeQuery) {
|
||||
resetQuery(defaultQuery);
|
||||
redirectWithQueryBuilderData(defaultQuery);
|
||||
if (!compositeQuery || forceReset) {
|
||||
resetQuery(defaultValue);
|
||||
redirectWithQueryBuilderData(defaultValue);
|
||||
}
|
||||
}, [
|
||||
defaultQuery,
|
||||
defaultValue,
|
||||
urlQuery,
|
||||
redirectWithQueryBuilderData,
|
||||
compositeQuery,
|
||||
resetQuery,
|
||||
forceReset,
|
||||
]);
|
||||
};
|
||||
|
||||
@ -9,7 +9,7 @@ import { useEffect } from 'react';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
function LiveLogs(): JSX.Element {
|
||||
useShareBuilderUrl(liveLogsCompositeQuery);
|
||||
useShareBuilderUrl({ defaultValue: liveLogsCompositeQuery });
|
||||
const { handleSetConfig } = useQueryBuilder();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -9,7 +9,7 @@ import QuickFilters from 'components/QuickFilters/QuickFilters';
|
||||
import { QuickFiltersSource, SignalType } from 'components/QuickFilters/types';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import LogExplorerQuerySection from 'container/LogExplorerQuerySection';
|
||||
import LogsExplorerViewsContainer from 'container/LogsExplorerViews';
|
||||
import {
|
||||
@ -23,6 +23,7 @@ import RightToolbarActions from 'container/QueryBuilder/components/ToolbarAction
|
||||
import Toolbar from 'container/Toolbar/Toolbar';
|
||||
import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
||||
import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange';
|
||||
import useUrlQueryData from 'hooks/useUrlQueryData';
|
||||
import { isEqual, isNull } from 'lodash-es';
|
||||
@ -77,7 +78,11 @@ function LogsExplorer(): JSX.Element {
|
||||
window.history.replaceState({}, '', url.toString());
|
||||
}, [selectedView]);
|
||||
|
||||
const { handleRunQuery, handleSetConfig } = useQueryBuilder();
|
||||
const {
|
||||
handleRunQuery,
|
||||
handleSetConfig,
|
||||
updateAllQueriesOperators,
|
||||
} = useQueryBuilder();
|
||||
|
||||
const { handleExplorerTabChange } = useHandleExplorerTabChange();
|
||||
|
||||
@ -87,12 +92,18 @@ function LogsExplorer(): JSX.Element {
|
||||
|
||||
const [isLoadingQueries, setIsLoadingQueries] = useState<boolean>(false);
|
||||
|
||||
const [shouldReset, setShouldReset] = useState(false);
|
||||
|
||||
const handleChangeSelectedView = useCallback(
|
||||
(view: ExplorerViews): void => {
|
||||
if (selectedView === ExplorerViews.LIST) {
|
||||
handleSetConfig(PANEL_TYPES.LIST, DataSource.LOGS);
|
||||
}
|
||||
|
||||
if (view === ExplorerViews.LIST) {
|
||||
setShouldReset(true);
|
||||
}
|
||||
|
||||
setSelectedView(view);
|
||||
handleExplorerTabChange(
|
||||
view === ExplorerViews.TIMESERIES ? PANEL_TYPES.TIME_SERIES : view,
|
||||
@ -101,6 +112,25 @@ function LogsExplorer(): JSX.Element {
|
||||
[handleSetConfig, handleExplorerTabChange, selectedView],
|
||||
);
|
||||
|
||||
const defaultListQuery = useMemo(
|
||||
() =>
|
||||
updateAllQueriesOperators(
|
||||
initialQueriesMap.logs,
|
||||
PANEL_TYPES.LIST,
|
||||
DataSource.LOGS,
|
||||
),
|
||||
[updateAllQueriesOperators],
|
||||
);
|
||||
|
||||
useShareBuilderUrl({
|
||||
defaultValue: defaultListQuery,
|
||||
forceReset: shouldReset,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldReset) setShouldReset(false);
|
||||
}, [shouldReset]);
|
||||
|
||||
const handleFilterVisibilityChange = (): void => {
|
||||
setLocalStorageApi(
|
||||
LOCALSTORAGE.SHOW_LOGS_QUICK_FILTERS,
|
||||
@ -286,7 +316,7 @@ function LogsExplorer(): JSX.Element {
|
||||
}
|
||||
rightActions={
|
||||
<RightToolbarActions
|
||||
onStageRunQuery={handleRunQuery}
|
||||
onStageRunQuery={(): void => handleRunQuery(true, true)}
|
||||
listQueryKeyRef={listQueryKeyRef}
|
||||
chartQueryKeyRef={chartQueryKeyRef}
|
||||
isLoadingQueries={isLoadingQueries}
|
||||
|
||||
@ -92,10 +92,16 @@ function TracesExplorer(): JSX.Element {
|
||||
});
|
||||
}, [selectedView, setSearchParams]);
|
||||
|
||||
const [shouldReset, setShouldReset] = useState(false);
|
||||
|
||||
const handleChangeSelectedView = useCallback(
|
||||
(view: ExplorerViews): void => {
|
||||
if (selectedView === ExplorerViews.LIST) {
|
||||
handleSetConfig(PANEL_TYPES.LIST, DataSource.LOGS);
|
||||
handleSetConfig(PANEL_TYPES.LIST, DataSource.TRACES);
|
||||
}
|
||||
|
||||
if (view === ExplorerViews.LIST) {
|
||||
setShouldReset(true);
|
||||
}
|
||||
|
||||
setSelectedView(view);
|
||||
@ -177,7 +183,11 @@ function TracesExplorer(): JSX.Element {
|
||||
[exportDefaultQuery, panelType, safeNavigate, getUpdatedQueryForExport],
|
||||
);
|
||||
|
||||
useShareBuilderUrl(defaultQuery);
|
||||
useShareBuilderUrl({ defaultValue: defaultQuery, forceReset: shouldReset });
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldReset) setShouldReset(false);
|
||||
}, [shouldReset]);
|
||||
|
||||
const [isOpen, setOpen] = useState<boolean>(true);
|
||||
const logEventCalledRef = useRef(false);
|
||||
@ -263,7 +273,11 @@ function TracesExplorer(): JSX.Element {
|
||||
onChangeSelectedView={handleChangeSelectedView}
|
||||
/>
|
||||
}
|
||||
rightActions={<RightToolbarActions onStageRunQuery={handleRunQuery} />}
|
||||
rightActions={
|
||||
<RightToolbarActions
|
||||
onStageRunQuery={(): void => handleRunQuery(true, true)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<ExplorerCard sourcepage={DataSource.TRACES}>
|
||||
|
||||
@ -853,18 +853,35 @@ export function QueryBuilderProvider({
|
||||
);
|
||||
|
||||
const handleRunQuery = useCallback(
|
||||
(shallUpdateStepInterval?: boolean) => {
|
||||
(shallUpdateStepInterval?: boolean, newQBQuery?: boolean) => {
|
||||
let currentQueryData = currentQuery;
|
||||
if (newQBQuery) {
|
||||
currentQueryData = {
|
||||
...currentQueryData,
|
||||
builder: {
|
||||
...currentQueryData.builder,
|
||||
queryData: currentQueryData.builder.queryData.map((item) => ({
|
||||
...item,
|
||||
filters: {
|
||||
items: [],
|
||||
op: 'AND',
|
||||
},
|
||||
having: [],
|
||||
})),
|
||||
},
|
||||
};
|
||||
}
|
||||
redirectWithQueryBuilderData({
|
||||
...{
|
||||
...currentQuery,
|
||||
...currentQueryData,
|
||||
...updateStepInterval(
|
||||
{
|
||||
builder: currentQuery.builder,
|
||||
clickhouse_sql: currentQuery.clickhouse_sql,
|
||||
promql: currentQuery.promql,
|
||||
id: currentQuery.id,
|
||||
builder: currentQueryData.builder,
|
||||
clickhouse_sql: currentQueryData.clickhouse_sql,
|
||||
promql: currentQueryData.promql,
|
||||
id: currentQueryData.id,
|
||||
queryType,
|
||||
unit: currentQuery.unit,
|
||||
unit: currentQueryData.unit,
|
||||
},
|
||||
maxTime,
|
||||
minTime,
|
||||
|
||||
@ -111,6 +111,15 @@ export type SpaceAggregation =
|
||||
|
||||
export type ColumnType = 'group' | 'aggregation';
|
||||
|
||||
// ===================== Variable Types =====================
|
||||
|
||||
export type VariableType = 'query' | 'dynamic' | 'custom' | 'text';
|
||||
|
||||
export interface VariableItem {
|
||||
type?: VariableType;
|
||||
value: any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
}
|
||||
|
||||
// ===================== Core Interface Types =====================
|
||||
|
||||
export interface TelemetryFieldKey {
|
||||
@ -174,6 +183,7 @@ export interface MetricAggregation {
|
||||
temporality: Temporality;
|
||||
timeAggregation: TimeAggregation;
|
||||
spaceAggregation: SpaceAggregation;
|
||||
reduceTo?: string;
|
||||
}
|
||||
|
||||
export interface SecondaryAggregation {
|
||||
@ -230,6 +240,9 @@ export interface QueryBuilderFormula {
|
||||
name: string;
|
||||
expression: string;
|
||||
functions?: QueryFunction[];
|
||||
order?: OrderBy[];
|
||||
limit?: number;
|
||||
having?: Having;
|
||||
}
|
||||
|
||||
export interface QueryBuilderJoin {
|
||||
@ -254,8 +267,8 @@ export interface PromQuery {
|
||||
name: string;
|
||||
query: string;
|
||||
disabled?: boolean;
|
||||
stats?: boolean;
|
||||
step?: Step;
|
||||
stats?: boolean;
|
||||
}
|
||||
|
||||
export interface ClickHouseQuery {
|
||||
@ -288,7 +301,11 @@ export interface QueryRangeRequestV5 {
|
||||
end: number; // epoch milliseconds
|
||||
requestType: RequestType;
|
||||
compositeQuery: CompositeQuery;
|
||||
variables?: Record<string, any>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
variables?: Record<string, VariableItem>;
|
||||
formatOptions?: {
|
||||
formatTableResultForUI: boolean;
|
||||
fillGaps?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
// ===================== Response Types =====================
|
||||
@ -313,6 +330,7 @@ export interface TimeSeriesValue {
|
||||
value: number;
|
||||
values?: number[]; // For heatmap type charts
|
||||
bucket?: Bucket;
|
||||
partial?: boolean;
|
||||
}
|
||||
|
||||
export interface TimeSeries {
|
||||
@ -336,6 +354,9 @@ export interface ColumnDescriptor extends TelemetryFieldKey {
|
||||
queryName: string;
|
||||
aggregationIndex: number;
|
||||
columnType: ColumnType;
|
||||
meta?: {
|
||||
unit?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ScalarData {
|
||||
|
||||
@ -227,7 +227,10 @@ export type QueryBuilderContextType = {
|
||||
redirectToUrl?: typeof ROUTES[keyof typeof ROUTES],
|
||||
shallStringify?: boolean,
|
||||
) => void;
|
||||
handleRunQuery: (shallUpdateStepInterval?: boolean) => void;
|
||||
handleRunQuery: (
|
||||
shallUpdateStepInterval?: boolean,
|
||||
newQBQuery?: boolean,
|
||||
) => void;
|
||||
resetQuery: (newCurrentQuery?: QueryState) => void;
|
||||
handleOnUnitsChange: (units: Format['id']) => void;
|
||||
updateAllQueriesOperators: (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user