mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-16 20:51:28 +08:00
chore: update query builder to support spatial aggregations and functions (#4569)
This commit is contained in:
parent
97fdba0fae
commit
1a62a13aea
@ -25,5 +25,5 @@
|
||||
"dashboard_unsave_changes": "There are unsaved changes in the Query builder, please stage and run the query or the changes will be lost. Press OK to discard.",
|
||||
"dashboard_save_changes": "Your graph built with {{queryTag}} query will be saved. Press OK to confirm.",
|
||||
"your_graph_build_with": "Your graph built with",
|
||||
"dashboar_ok_confirm": "query will be saved. Press OK to confirm."
|
||||
"dashboard_ok_confirm": "query will be saved. Press OK to confirm."
|
||||
}
|
||||
|
@ -28,5 +28,5 @@
|
||||
"dashboard_unsave_changes": "There are unsaved changes in the Query builder, please stage and run the query or the changes will be lost. Press OK to discard.",
|
||||
"dashboard_save_changes": "Your graph built with {{queryTag}} query will be saved. Press OK to confirm.",
|
||||
"your_graph_build_with": "Your graph built with",
|
||||
"dashboar_ok_confirm": "query will be saved. Press OK to confirm."
|
||||
"dashboard_ok_confirm": "query will be saved. Press OK to confirm."
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ const apiV1 = '/api/v1/';
|
||||
|
||||
export const apiV2 = '/api/v2/';
|
||||
export const apiV3 = '/api/v3/';
|
||||
export const apiV4 = '/api/v4/';
|
||||
export const apiAlertManager = '/api/alertmanager';
|
||||
|
||||
export default apiV1;
|
||||
|
@ -9,7 +9,7 @@ import { ENVIRONMENT } from 'constants/env';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import store from 'store';
|
||||
|
||||
import apiV1, { apiAlertManager, apiV2, apiV3 } from './apiV1';
|
||||
import apiV1, { apiAlertManager, apiV2, apiV3, apiV4 } from './apiV1';
|
||||
import { Logout } from './utils';
|
||||
|
||||
const interceptorsResponse = (
|
||||
@ -114,6 +114,7 @@ ApiV2Instance.interceptors.request.use(interceptorsRequestResponse);
|
||||
export const ApiV3Instance = axios.create({
|
||||
baseURL: `${ENVIRONMENT.baseURL}${apiV3}`,
|
||||
});
|
||||
|
||||
ApiV3Instance.interceptors.response.use(
|
||||
interceptorsResponse,
|
||||
interceptorRejected,
|
||||
@ -121,6 +122,18 @@ ApiV3Instance.interceptors.response.use(
|
||||
ApiV3Instance.interceptors.request.use(interceptorsRequestResponse);
|
||||
//
|
||||
|
||||
// axios V4
|
||||
export const ApiV4Instance = axios.create({
|
||||
baseURL: `${ENVIRONMENT.baseURL}${apiV4}`,
|
||||
});
|
||||
|
||||
ApiV4Instance.interceptors.response.use(
|
||||
interceptorsResponse,
|
||||
interceptorRejected,
|
||||
);
|
||||
ApiV4Instance.interceptors.request.use(interceptorsRequestResponse);
|
||||
//
|
||||
|
||||
AxiosAlertManagerInstance.interceptors.response.use(
|
||||
interceptorsResponse,
|
||||
interceptorRejected,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ApiV3Instance as axios } from 'api';
|
||||
import { ApiV3Instance, ApiV4Instance } from 'api';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
@ -9,10 +9,23 @@ import {
|
||||
|
||||
export const getMetricsQueryRange = async (
|
||||
props: QueryRangePayload,
|
||||
version: string,
|
||||
signal: AbortSignal,
|
||||
): Promise<SuccessResponse<MetricRangePayloadV3> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post('/query_range', props, { signal });
|
||||
if (version && version === 'v4') {
|
||||
const response = await ApiV4Instance.post('/query_range', props, { signal });
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: response.data.status,
|
||||
payload: response.data,
|
||||
params: props,
|
||||
};
|
||||
}
|
||||
|
||||
const response = await ApiV3Instance.post('/query_range', props, { signal });
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
|
@ -72,8 +72,6 @@ export default function LogsFormatOptionsMenu({
|
||||
setAddNewColumn(!addNewColumn);
|
||||
};
|
||||
|
||||
// console.log('optionsMenuConfig', config);
|
||||
|
||||
const handleLinesPerRowChange = (maxLinesPerRow: number | null): void => {
|
||||
if (
|
||||
maxLinesPerRow &&
|
||||
@ -221,8 +219,6 @@ export default function LogsFormatOptionsMenu({
|
||||
className="column-name"
|
||||
key={value}
|
||||
onClick={(eve): void => {
|
||||
console.log('coluimn name', label, value);
|
||||
|
||||
eve.stopPropagation();
|
||||
|
||||
if (addColumn && addColumn?.onSelect) {
|
||||
|
@ -13,3 +13,5 @@ export const SIGNOZ_UPGRADE_PLAN_URL =
|
||||
'https://upgrade.signoz.io/upgrade-from-app';
|
||||
|
||||
export const DASHBOARD_TIME_IN_DURATION = 'refreshInterval';
|
||||
|
||||
export const DEFAULT_ENTITY_VERSION = 'v3';
|
||||
|
@ -36,6 +36,11 @@ import { v4 as uuid } from 'uuid';
|
||||
import {
|
||||
logsAggregateOperatorOptions,
|
||||
metricAggregateOperatorOptions,
|
||||
metricsGaugeAggregateOperatorOptions,
|
||||
metricsGaugeSpaceAggregateOperatorOptions,
|
||||
metricsHistogramSpaceAggregateOperatorOptions,
|
||||
metricsSumAggregateOperatorOptions,
|
||||
metricsSumSpaceAggregateOperatorOptions,
|
||||
tracesAggregateOperatorOptions,
|
||||
} from './queryBuilderOperators';
|
||||
|
||||
@ -74,6 +79,18 @@ export const mapOfOperators = {
|
||||
traces: tracesAggregateOperatorOptions,
|
||||
};
|
||||
|
||||
export const metricsOperatorsByType = {
|
||||
Sum: metricsSumAggregateOperatorOptions,
|
||||
Gauge: metricsGaugeAggregateOperatorOptions,
|
||||
};
|
||||
|
||||
export const metricsSpaceAggregationOperatorsByType = {
|
||||
Sum: metricsSumSpaceAggregateOperatorOptions,
|
||||
Gauge: metricsGaugeSpaceAggregateOperatorOptions,
|
||||
Histogram: metricsHistogramSpaceAggregateOperatorOptions,
|
||||
ExponentialHistogram: metricsHistogramSpaceAggregateOperatorOptions,
|
||||
};
|
||||
|
||||
export const mapOfQueryFilters: Record<DataSource, QueryAdditionalFilter[]> = {
|
||||
metrics: [
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
@ -148,6 +165,9 @@ export const initialQueryBuilderFormValues: IBuilderQuery = {
|
||||
queryName: createNewBuilderItemName({ existNames: [], sourceNames: alphabet }),
|
||||
aggregateOperator: MetricAggregateOperator.COUNT,
|
||||
aggregateAttribute: initialAutocompleteData,
|
||||
timeAggregation: MetricAggregateOperator.RATE,
|
||||
spaceAggregation: MetricAggregateOperator.SUM,
|
||||
functions: [],
|
||||
filters: { items: [], op: 'AND' },
|
||||
expression: createNewBuilderItemName({
|
||||
existNames: [],
|
||||
@ -160,7 +180,7 @@ export const initialQueryBuilderFormValues: IBuilderQuery = {
|
||||
orderBy: [],
|
||||
groupBy: [],
|
||||
legend: '',
|
||||
reduceTo: 'sum',
|
||||
reduceTo: 'avg',
|
||||
};
|
||||
|
||||
const initialQueryBuilderFormLogsValues: IBuilderQuery = {
|
||||
@ -268,6 +288,14 @@ export enum PANEL_TYPES {
|
||||
EMPTY_WIDGET = 'EMPTY_WIDGET',
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
export enum ATTRIBUTE_TYPES {
|
||||
SUM = 'Sum',
|
||||
GAUGE = 'Gauge',
|
||||
HISTOGRAM = 'Histogram',
|
||||
EXPONENTIAL_HISTOGRAM = 'ExponentialHistogram',
|
||||
}
|
||||
|
||||
export type IQueryBuilderState = 'search';
|
||||
|
||||
export const QUERY_BUILDER_SEARCH_VALUES = {
|
||||
|
@ -302,3 +302,126 @@ export const logsAggregateOperatorOptions: SelectOption<string, string>[] = [
|
||||
label: 'Rate_max',
|
||||
},
|
||||
];
|
||||
|
||||
export const metricsSumAggregateOperatorOptions: SelectOption<
|
||||
string,
|
||||
string
|
||||
>[] = [
|
||||
{
|
||||
value: MetricAggregateOperator.RATE,
|
||||
label: 'Rate',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.INCREASE,
|
||||
label: 'Increase',
|
||||
},
|
||||
];
|
||||
|
||||
export const metricsGaugeAggregateOperatorOptions: SelectOption<
|
||||
string,
|
||||
string
|
||||
>[] = [
|
||||
{
|
||||
value: MetricAggregateOperator.LATEST,
|
||||
label: 'Latest',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.SUM,
|
||||
label: 'Sum',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.AVG,
|
||||
label: 'Avg',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.MIN,
|
||||
label: 'Min',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.MAX,
|
||||
label: 'Max',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.COUNT,
|
||||
label: 'Count',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.COUNT_DISTINCT,
|
||||
label: 'Count Distinct',
|
||||
},
|
||||
];
|
||||
|
||||
export const metricsSumSpaceAggregateOperatorOptions: SelectOption<
|
||||
string,
|
||||
string
|
||||
>[] = [
|
||||
{
|
||||
value: MetricAggregateOperator.SUM,
|
||||
label: 'Sum',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.AVG,
|
||||
label: 'Avg',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.MIN,
|
||||
label: 'Min',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.MAX,
|
||||
label: 'Max',
|
||||
},
|
||||
];
|
||||
|
||||
export const metricsGaugeSpaceAggregateOperatorOptions: SelectOption<
|
||||
string,
|
||||
string
|
||||
>[] = [
|
||||
{
|
||||
value: MetricAggregateOperator.SUM,
|
||||
label: 'Sum',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.AVG,
|
||||
label: 'Avg',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.MIN,
|
||||
label: 'Min',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.MAX,
|
||||
label: 'Max',
|
||||
},
|
||||
];
|
||||
|
||||
export const metricsHistogramSpaceAggregateOperatorOptions: SelectOption<
|
||||
string,
|
||||
string
|
||||
>[] = [
|
||||
{
|
||||
value: MetricAggregateOperator.P50,
|
||||
label: 'P50',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.P75,
|
||||
label: 'P75',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.P90,
|
||||
label: 'P90',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.P95,
|
||||
label: 'P95',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.P99,
|
||||
label: 'P99',
|
||||
},
|
||||
];
|
||||
|
||||
export const metricsEmptyTimeAggregateOperatorOptions: SelectOption<
|
||||
string,
|
||||
string
|
||||
>[] = [];
|
||||
|
137
frontend/src/constants/queryFunctionOptions.ts
Normal file
137
frontend/src/constants/queryFunctionOptions.ts
Normal file
@ -0,0 +1,137 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import { QueryFunctionsTypes } from 'types/common/queryBuilder';
|
||||
import { SelectOption } from 'types/common/select';
|
||||
|
||||
export const queryFunctionOptions: SelectOption<string, string>[] = [
|
||||
{
|
||||
value: QueryFunctionsTypes.CUTOFF_MIN,
|
||||
label: 'Cut Off Min',
|
||||
},
|
||||
{
|
||||
value: QueryFunctionsTypes.CUTOFF_MAX,
|
||||
label: 'Cut Off Max',
|
||||
},
|
||||
{
|
||||
value: QueryFunctionsTypes.CLAMP_MIN,
|
||||
label: 'Clamp Min',
|
||||
},
|
||||
{
|
||||
value: QueryFunctionsTypes.CLAMP_MAX,
|
||||
label: 'Clamp Max',
|
||||
},
|
||||
{
|
||||
value: QueryFunctionsTypes.ABSOLUTE,
|
||||
label: 'Absolute',
|
||||
},
|
||||
{
|
||||
value: QueryFunctionsTypes.LOG_2,
|
||||
label: 'Log2',
|
||||
},
|
||||
{
|
||||
value: QueryFunctionsTypes.LOG_10,
|
||||
label: 'Log10',
|
||||
},
|
||||
{
|
||||
value: QueryFunctionsTypes.CUMULATIVE_SUM,
|
||||
label: 'Cumulative Sum',
|
||||
},
|
||||
{
|
||||
value: QueryFunctionsTypes.EWMA_3,
|
||||
label: 'EWMA 3',
|
||||
},
|
||||
{
|
||||
value: QueryFunctionsTypes.EWMA_5,
|
||||
label: 'EWMA 5',
|
||||
},
|
||||
{
|
||||
value: QueryFunctionsTypes.EWMA_7,
|
||||
label: 'EWMA 7',
|
||||
},
|
||||
{
|
||||
value: QueryFunctionsTypes.MEDIAN_3,
|
||||
label: 'Median 3',
|
||||
},
|
||||
{
|
||||
value: QueryFunctionsTypes.MEDIAN_5,
|
||||
label: 'Median 5',
|
||||
},
|
||||
{
|
||||
value: QueryFunctionsTypes.MEDIAN_7,
|
||||
label: 'Median 7',
|
||||
},
|
||||
{
|
||||
value: QueryFunctionsTypes.TIME_SHIFT,
|
||||
label: 'Time Shift',
|
||||
},
|
||||
];
|
||||
|
||||
interface QueryFunctionConfigType {
|
||||
[key: string]: {
|
||||
showInput: boolean;
|
||||
inputType?: string;
|
||||
placeholder?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const queryFunctionsTypesConfig: QueryFunctionConfigType = {
|
||||
cutOffMin: {
|
||||
showInput: true,
|
||||
inputType: 'text',
|
||||
placeholder: 'Threshold',
|
||||
},
|
||||
cutOffMax: {
|
||||
showInput: true,
|
||||
inputType: 'text',
|
||||
placeholder: 'Threshold',
|
||||
},
|
||||
clampMin: {
|
||||
showInput: true,
|
||||
inputType: 'text',
|
||||
placeholder: 'Threshold',
|
||||
},
|
||||
clampMax: {
|
||||
showInput: true,
|
||||
inputType: 'text',
|
||||
placeholder: 'Threshold',
|
||||
},
|
||||
absolute: {
|
||||
showInput: false,
|
||||
},
|
||||
log2: {
|
||||
showInput: false,
|
||||
},
|
||||
log10: {
|
||||
showInput: false,
|
||||
},
|
||||
cumSum: {
|
||||
showInput: false,
|
||||
},
|
||||
ewma3: {
|
||||
showInput: true,
|
||||
inputType: 'text',
|
||||
placeholder: 'Alpha',
|
||||
},
|
||||
ewma5: {
|
||||
showInput: true,
|
||||
inputType: 'text',
|
||||
placeholder: 'Alpha',
|
||||
},
|
||||
ewma7: {
|
||||
showInput: true,
|
||||
inputType: 'text',
|
||||
placeholder: 'Alpha',
|
||||
},
|
||||
median3: {
|
||||
showInput: false,
|
||||
},
|
||||
median5: {
|
||||
showInput: false,
|
||||
},
|
||||
median7: {
|
||||
showInput: false,
|
||||
},
|
||||
timeShift: {
|
||||
showInput: true,
|
||||
inputType: 'text',
|
||||
},
|
||||
};
|
17
frontend/src/constants/shortcuts/DashboardShortcuts.ts
Normal file
17
frontend/src/constants/shortcuts/DashboardShortcuts.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { getUserOperatingSystem, UserOperatingSystem } from 'utils/getUserOS';
|
||||
|
||||
const userOS = getUserOperatingSystem();
|
||||
|
||||
export const DashboardShortcuts = {
|
||||
SaveChanges: 's+meta',
|
||||
DiscardChanges: 'd+meta',
|
||||
};
|
||||
|
||||
export const DashboardShortcutsName = {
|
||||
SaveChanges: `${userOS === UserOperatingSystem.MACOS ? 'cmd' : 'ctrl'}+s`,
|
||||
};
|
||||
|
||||
export const DashboardShortcutsDescription = {
|
||||
SaveChanges: 'Save Changes',
|
||||
DiscardChanges: 'Discard Changes',
|
||||
};
|
17
frontend/src/constants/shortcuts/QBShortcuts.ts
Normal file
17
frontend/src/constants/shortcuts/QBShortcuts.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { getUserOperatingSystem, UserOperatingSystem } from 'utils/getUserOS';
|
||||
|
||||
const userOS = getUserOperatingSystem();
|
||||
|
||||
export const QBShortcuts = {
|
||||
StageAndRunQuery: 'enter+meta',
|
||||
};
|
||||
|
||||
export const QBShortcutsName = {
|
||||
StageAndRunQuery: `${
|
||||
userOS === UserOperatingSystem.MACOS ? 'cmd' : 'ctrl'
|
||||
}+enter`,
|
||||
};
|
||||
|
||||
export const QBShortcutsDescription = {
|
||||
StageAndRunQuery: 'Stage and Run the query',
|
||||
};
|
@ -25,6 +25,7 @@ const defaultAnnotations = {
|
||||
|
||||
export const alertDefaults: AlertDef = {
|
||||
alertType: AlertTypes.METRICS_BASED_ALERT,
|
||||
version: 'v4',
|
||||
condition: {
|
||||
compositeQuery: {
|
||||
builderQueries: {
|
||||
|
@ -2,6 +2,7 @@ import { Form, Row } from 'antd';
|
||||
import FormAlertRules from 'container/FormAlertRules';
|
||||
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||
import { AlertDef } from 'types/api/alerts/def';
|
||||
|
||||
@ -20,6 +21,10 @@ function CreateRules(): JSX.Element {
|
||||
AlertTypes.METRICS_BASED_ALERT,
|
||||
);
|
||||
|
||||
const location = useLocation();
|
||||
const queryParams = new URLSearchParams(location.search);
|
||||
const version = queryParams.get('version');
|
||||
|
||||
const compositeQuery = useGetCompositeQueryParam();
|
||||
|
||||
const [formInstance] = Form.useForm();
|
||||
@ -37,7 +42,10 @@ function CreateRules(): JSX.Element {
|
||||
setInitValues(exceptionAlertDefaults);
|
||||
break;
|
||||
default:
|
||||
setInitValues(alertDefaults);
|
||||
setInitValues({
|
||||
...alertDefaults,
|
||||
version: version || 'v3',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -52,6 +60,7 @@ function CreateRules(): JSX.Element {
|
||||
if (alertType) {
|
||||
onSelectType(alertType);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [compositeQuery]);
|
||||
|
||||
if (!initValues) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import Spinner from 'components/Spinner';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import GridPanelSwitch from 'container/GridPanelSwitch';
|
||||
import { getFormatNameByOptionId } from 'container/NewWidget/RightContainer/alertFomatCategories';
|
||||
@ -39,6 +40,7 @@ export interface ChartPreviewProps {
|
||||
yAxisUnit: string;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
function ChartPreview({
|
||||
name,
|
||||
query,
|
||||
@ -94,6 +96,7 @@ function ChartPreview({
|
||||
allowSelectedIntervalForStepGen,
|
||||
},
|
||||
},
|
||||
alertDef?.version || DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
queryKey: [
|
||||
'chartPreview',
|
||||
|
@ -3,13 +3,16 @@ import './QuerySection.styles.scss';
|
||||
import { Button, Tabs, Tooltip } from 'antd';
|
||||
import { ALERTS_DATA_SOURCE_MAP } from 'constants/alerts';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { QBShortcuts } from 'constants/shortcuts/QBShortcuts';
|
||||
import { QueryBuilder } from 'container/QueryBuilder';
|
||||
import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys';
|
||||
import { Atom, Play, Terminal } from 'lucide-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||
import { AlertDef } from 'types/api/alerts/def';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
|
||||
@ -22,6 +25,7 @@ function QuerySection({
|
||||
setQueryCategory,
|
||||
alertType,
|
||||
runQuery,
|
||||
alertDef,
|
||||
panelType,
|
||||
}: QuerySectionProps): JSX.Element {
|
||||
// init namespace for translations
|
||||
@ -50,6 +54,10 @@ function QuerySection({
|
||||
queryVariant: 'static',
|
||||
initialDataSource: ALERTS_DATA_SOURCE_MAP[alertType],
|
||||
}}
|
||||
showFunctions={
|
||||
alertType === AlertTypes.METRICS_BASED_ALERT && alertDef.version === 'v4'
|
||||
}
|
||||
version={alertDef.version || 'v3'}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -112,6 +120,17 @@ function QuerySection({
|
||||
[],
|
||||
);
|
||||
|
||||
const { registerShortcut, deregisterShortcut } = useKeyboardHotkeys();
|
||||
|
||||
useEffect(() => {
|
||||
registerShortcut(QBShortcuts.StageAndRunQuery, runQuery);
|
||||
|
||||
return (): void => {
|
||||
deregisterShortcut(QBShortcuts.StageAndRunQuery);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [runQuery]);
|
||||
|
||||
const renderTabs = (typ: AlertTypes): JSX.Element | null => {
|
||||
switch (typ) {
|
||||
case AlertTypes.TRACES_BASED_ALERT:
|
||||
@ -197,6 +216,7 @@ interface QuerySectionProps {
|
||||
setQueryCategory: (n: EQueryType) => void;
|
||||
alertType: AlertTypes;
|
||||
runQuery: VoidFunction;
|
||||
alertDef: AlertDef;
|
||||
panelType: PANEL_TYPES;
|
||||
}
|
||||
|
||||
|
@ -304,7 +304,7 @@ function FormAlertRules({
|
||||
panelType,
|
||||
]);
|
||||
|
||||
const isAlertAvialable = useIsFeatureDisabled(
|
||||
const isAlertAvailable = useIsFeatureDisabled(
|
||||
FeatureKeys.QUERY_BUILDER_ALERTS,
|
||||
);
|
||||
|
||||
@ -458,8 +458,8 @@ function FormAlertRules({
|
||||
|
||||
const isAlertNameMissing = !formInstance.getFieldValue('alert');
|
||||
|
||||
const isAlertAvialableToSave =
|
||||
isAlertAvialable &&
|
||||
const isAlertAvailableToSave =
|
||||
isAlertAvailable &&
|
||||
currentQuery.queryType === EQueryType.QUERY_BUILDER &&
|
||||
alertType !== AlertTypes.METRICS_BASED_ALERT;
|
||||
|
||||
@ -509,6 +509,7 @@ function FormAlertRules({
|
||||
setQueryCategory={onQueryCategoryChange}
|
||||
alertType={alertType || AlertTypes.METRICS_BASED_ALERT}
|
||||
runQuery={handleRunQuery}
|
||||
alertDef={alertDef}
|
||||
panelType={panelType || PANEL_TYPES.TIME_SERIES}
|
||||
/>
|
||||
|
||||
@ -521,7 +522,7 @@ function FormAlertRules({
|
||||
|
||||
{renderBasicInfo()}
|
||||
<ButtonContainer>
|
||||
<Tooltip title={isAlertAvialableToSave ? MESSAGE.ALERT : ''}>
|
||||
<Tooltip title={isAlertAvailableToSave ? MESSAGE.ALERT : ''}>
|
||||
<ActionButton
|
||||
loading={loading || false}
|
||||
type="primary"
|
||||
@ -529,7 +530,7 @@ function FormAlertRules({
|
||||
icon={<SaveOutlined />}
|
||||
disabled={
|
||||
isAlertNameMissing ||
|
||||
isAlertAvialableToSave ||
|
||||
isAlertAvailableToSave ||
|
||||
!isChannelConfigurationValid
|
||||
}
|
||||
>
|
||||
|
@ -6,6 +6,7 @@ import cx from 'classnames';
|
||||
import { ToggleGraphProps } from 'components/Graph/types';
|
||||
import Spinner from 'components/Spinner';
|
||||
import TimePreference from 'components/TimePreferenceDropDown';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import GridPanelSwitch from 'container/GridPanelSwitch';
|
||||
import {
|
||||
@ -96,6 +97,7 @@ function FullView({
|
||||
globalSelectedInterval: globalSelectedTime,
|
||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||
},
|
||||
selectedDashboard?.data?.version || DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
queryKey: `FullViewGetMetricsQueryRange-${selectedTime.enum}-${globalSelectedTime}-${widget.id}`,
|
||||
enabled: !isDependedDataLoaded && widget.panelTypes !== PANEL_TYPES.LIST, // Internally both the list view panel has it's own query range api call, so we don't need to call it again
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||
@ -39,6 +40,7 @@ function GridCardGraph({
|
||||
threshold,
|
||||
variables,
|
||||
fillSpans = false,
|
||||
version,
|
||||
}: GridCardGraphProps): JSX.Element {
|
||||
const dispatch = useDispatch();
|
||||
const [errorMessage, setErrorMessage] = useState<string>();
|
||||
@ -132,6 +134,7 @@ function GridCardGraph({
|
||||
globalSelectedInterval,
|
||||
variables: getDashboardVariables(variables),
|
||||
},
|
||||
version || DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
queryKey: [
|
||||
maxTime,
|
||||
@ -253,6 +256,7 @@ GridCardGraph.defaultProps = {
|
||||
isQueryEnabled: true,
|
||||
threshold: undefined,
|
||||
headerMenuList: [MenuItemKeys.View],
|
||||
version: 'v3',
|
||||
};
|
||||
|
||||
export default memo(GridCardGraph);
|
||||
|
@ -43,6 +43,7 @@ export interface GridCardGraphProps {
|
||||
isQueryEnabled: boolean;
|
||||
variables?: Dashboard['data']['variables'];
|
||||
fillSpans?: boolean;
|
||||
version?: string;
|
||||
}
|
||||
|
||||
export interface GetGraphVisibilityStateOnLegendClickProps {
|
||||
|
@ -1,6 +1,6 @@
|
||||
.fullscreen-grid-container {
|
||||
overflow: auto;
|
||||
margin-top: 1rem;
|
||||
margin: 8px -8px;
|
||||
|
||||
.react-grid-layout {
|
||||
border: none !important;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import './GridCardLayout.styles.scss';
|
||||
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Tooltip } from 'antd';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { themeColors } from 'constants/theme';
|
||||
@ -144,17 +145,19 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<ButtonContainer>
|
||||
<Button
|
||||
loading={updateDashboardMutation.isLoading}
|
||||
onClick={handle.enter}
|
||||
icon={<FullscreenIcon size={16} />}
|
||||
disabled={updateDashboardMutation.isLoading}
|
||||
>
|
||||
{t('dashboard:full_view')}
|
||||
</Button>
|
||||
<Tooltip title="Open in Full Screen">
|
||||
<Button
|
||||
className="periscope-btn"
|
||||
loading={updateDashboardMutation.isLoading}
|
||||
onClick={handle.enter}
|
||||
icon={<FullscreenIcon size={16} />}
|
||||
disabled={updateDashboardMutation.isLoading}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
{!isDashboardLocked && addPanelPermission && (
|
||||
<Button
|
||||
className="periscope-btn"
|
||||
onClick={onAddPanelHandler}
|
||||
icon={<PlusOutlined />}
|
||||
data-testid="add-panel"
|
||||
@ -201,6 +204,7 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
|
||||
headerMenuList={widgetActions}
|
||||
variables={variables}
|
||||
fillSpans={currentWidget?.fillSpans}
|
||||
version={selectedDashboard?.data?.version}
|
||||
/>
|
||||
</Card>
|
||||
</CardContainer>
|
||||
|
@ -80,7 +80,6 @@ export const ReactGridLayout = styled(ReactGridLayoutComponent)`
|
||||
export const ButtonContainer = styled(Space)`
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
margin-top: 1rem;
|
||||
`;
|
||||
|
||||
export const Button = styled(ButtonComponent)`
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ToggleGraphProps } from 'components/Graph/types';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { getComponentForPanelType } from 'constants/panelTypes';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { GRID_TABLE_CONFIG } from 'container/GridTableComponent/config';
|
||||
@ -50,11 +51,13 @@ const GridPanelSwitch = forwardRef<
|
||||
? {
|
||||
selectedLogsFields: selectedLogFields || [],
|
||||
query,
|
||||
version: DEFAULT_ENTITY_VERSION, // As we don't support for Metrics, defaulting to v3
|
||||
selectedTime,
|
||||
}
|
||||
: {
|
||||
selectedTracesFields: selectedTracesFields || [],
|
||||
query,
|
||||
version: DEFAULT_ENTITY_VERSION, // As we don't support for Metrics, defaulting to v3
|
||||
selectedTime,
|
||||
},
|
||||
[PANEL_TYPES.TRACE]: null,
|
||||
|
@ -109,7 +109,6 @@ function DashboardsList(): JSX.Element {
|
||||
width: 30,
|
||||
key: DynamicColumnsKey.CreatedAt,
|
||||
sorter: (a: Data, b: Data): number => {
|
||||
console.log({ a });
|
||||
const prev = new Date(a.createdAt).getTime();
|
||||
const next = new Date(b.createdAt).getTime();
|
||||
|
||||
@ -211,6 +210,7 @@ function DashboardsList(): JSX.Element {
|
||||
ns: 'dashboard',
|
||||
}),
|
||||
uploadedGrafana: false,
|
||||
version: 'v4',
|
||||
});
|
||||
|
||||
if (response.statusCode === 200) {
|
||||
@ -304,6 +304,7 @@ function DashboardsList(): JSX.Element {
|
||||
loading={isFilteringDashboards}
|
||||
style={{ marginBottom: 16, marginTop: 16 }}
|
||||
defaultValue={searchString}
|
||||
autoFocus
|
||||
/>
|
||||
</Col>
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { LIVE_TAIL_GRAPH_INTERVAL } from 'constants/liveTail';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import LogsExplorerChart from 'container/LogsExplorerChart';
|
||||
@ -41,6 +42,7 @@ function LiveLogsListChart({
|
||||
const { data, isFetching } = useGetExplorerQueryRange(
|
||||
listChartQuery,
|
||||
PANEL_TYPES.TIME_SERIES,
|
||||
DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
enabled: isConnectionOpen,
|
||||
refetchInterval: LIVE_TAIL_GRAPH_INTERVAL,
|
||||
|
@ -62,6 +62,7 @@ function LogExplorerQuerySection({
|
||||
index: 0,
|
||||
query,
|
||||
filterConfigs,
|
||||
entityVersion: '',
|
||||
});
|
||||
|
||||
const renderOrderBy = useCallback(
|
||||
@ -103,6 +104,7 @@ function LogExplorerQuerySection({
|
||||
config={{ initialDataSource: DataSource.LOGS, queryVariant: 'static' }}
|
||||
filterConfigs={filterConfigs}
|
||||
queryComponents={queryComponents}
|
||||
version="v3" // setting this to v3 as we this is rendered in logs explorer
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
@ -2,6 +2,7 @@ import './LogsContextList.styles.scss';
|
||||
|
||||
import RawLogView from 'components/Logs/RawLogView';
|
||||
import Spinner from 'components/Spinner';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
|
||||
import { useGetExplorerQueryRange } from 'hooks/queryBuilder/useGetExplorerQueryRange';
|
||||
@ -105,6 +106,7 @@ function LogsContextList({
|
||||
const { isError, isFetching } = useGetExplorerQueryRange(
|
||||
requestData,
|
||||
PANEL_TYPES.LIST,
|
||||
DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
keepPreviousData: true,
|
||||
enabled: !!requestData,
|
||||
|
@ -3,6 +3,7 @@ import './LogsExplorerViews.styles.scss';
|
||||
|
||||
import { Button } from 'antd';
|
||||
import LogsFormatOptionsMenu from 'components/LogsFormatOptionsMenu/LogsFormatOptionsMenu';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import { AVAILABLE_EXPORT_PANEL_TYPES } from 'constants/panelTypes';
|
||||
import { QueryParams } from 'constants/query';
|
||||
@ -189,13 +190,19 @@ function LogsExplorerViews({
|
||||
data: listChartData,
|
||||
isFetching: isFetchingListChartData,
|
||||
isLoading: isLoadingListChartData,
|
||||
} = useGetExplorerQueryRange(listChartQuery, PANEL_TYPES.TIME_SERIES, {
|
||||
enabled: !!listChartQuery && panelType === PANEL_TYPES.LIST,
|
||||
});
|
||||
} = useGetExplorerQueryRange(
|
||||
listChartQuery,
|
||||
PANEL_TYPES.TIME_SERIES,
|
||||
DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
enabled: !!listChartQuery && panelType === PANEL_TYPES.LIST,
|
||||
},
|
||||
);
|
||||
|
||||
const { data, isLoading, isFetching, isError } = useGetExplorerQueryRange(
|
||||
requestData,
|
||||
panelType,
|
||||
DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
keepPreviousData: true,
|
||||
enabled: !isLimit && !!requestData,
|
||||
|
@ -1,80 +1,88 @@
|
||||
.logs-table {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.resize-table {
|
||||
height: calc(92% - 5px);
|
||||
overflow: scroll;
|
||||
|
||||
.ant-table-wrapper .ant-table-tbody >tr >td {
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
font-family: Inter;
|
||||
.ant-typography {
|
||||
background-color: transparent;
|
||||
color: var(--bg-vanilla-100);
|
||||
margin-bottom: 0;
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
font-family: Inter;
|
||||
}
|
||||
padding: 14px 8px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.resize-table {
|
||||
height: calc(100% - 40px);
|
||||
overflow: scroll;
|
||||
overflow-x: hidden;
|
||||
|
||||
.ant-table-wrapper .ant-table-header {
|
||||
border-bottom: 0.5px solid var(--bg-slate-400);
|
||||
}
|
||||
&::-webkit-scrollbar {
|
||||
width: 0.2rem;
|
||||
height: 0.2rem;
|
||||
}
|
||||
|
||||
.ant-table-wrapper .ant-table-thead > tr > th {
|
||||
font-family: Inter;
|
||||
color: var(--bg-vanilla-100);
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
padding: 8px;
|
||||
}
|
||||
.ant-table-wrapper .ant-table-tbody > tr > td {
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
font-family: Inter;
|
||||
.ant-typography {
|
||||
background-color: transparent;
|
||||
color: var(--bg-vanilla-100);
|
||||
margin-bottom: 0;
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
font-family: Inter;
|
||||
}
|
||||
padding: 14px 8px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ant-table-wrapper .ant-table-thead > tr > th::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.ant-table-wrapper .ant-table-header {
|
||||
border-bottom: 0.5px solid var(--bg-slate-400);
|
||||
}
|
||||
|
||||
.controller {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
right: 10px;
|
||||
}
|
||||
.ant-table-wrapper .ant-table-thead > tr > th {
|
||||
font-family: Inter;
|
||||
color: var(--bg-vanilla-100);
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.ant-table-wrapper .ant-table-thead > tr > th::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.controller {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
justify-content: end;
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.logs-table {
|
||||
.resize-table {
|
||||
.ant-table-wrapper .ant-table-tbody >tr >td {
|
||||
background-color: var(--bg-vanilla-100);
|
||||
.ant-typography {
|
||||
color: var(--bg-ink-500);
|
||||
}
|
||||
}
|
||||
.ant-table-wrapper .ant-table-thead > tr > th {
|
||||
background-color: var(--bg-vanilla-100);
|
||||
color: var(--bg-ink-500);
|
||||
}
|
||||
.logs-table {
|
||||
.resize-table {
|
||||
.ant-table-wrapper .ant-table-tbody > tr > td {
|
||||
background-color: var(--bg-vanilla-100);
|
||||
.ant-typography {
|
||||
color: var(--bg-ink-500);
|
||||
}
|
||||
}
|
||||
.ant-table-wrapper .ant-table-thead > tr > th {
|
||||
background-color: var(--bg-vanilla-100);
|
||||
color: var(--bg-ink-500);
|
||||
}
|
||||
|
||||
.ant-table-wrapper .ant-table-header {
|
||||
border-bottom: 0.5px solid var(--bg-vanilla-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-table-wrapper .ant-table-header {
|
||||
border-bottom: 0.5px solid var(--bg-vanilla-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import { Table } from 'antd';
|
||||
import LogDetail from 'components/LogDetail';
|
||||
import { VIEW_TYPES } from 'components/LogDetail/constants';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { OPERATORS, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import Controls from 'container/Controls';
|
||||
@ -106,6 +107,7 @@ function LogsPanelComponent({
|
||||
selectedTime: selectedTime?.enum || 'GLOBAL_TIME',
|
||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||
},
|
||||
DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
queryKey: [
|
||||
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
||||
|
@ -48,7 +48,7 @@ export const getQueryBuilderQueries = ({
|
||||
items: filterItems[index],
|
||||
op: 'AND',
|
||||
},
|
||||
reduceTo: 'sum',
|
||||
reduceTo: 'avg',
|
||||
dataSource,
|
||||
};
|
||||
|
||||
@ -86,7 +86,7 @@ export const getQueryBuilderQuerieswithFormula = ({
|
||||
aggregateAttribute: autocompleteData[index],
|
||||
queryName: alphabet[index],
|
||||
expression: alphabet[index],
|
||||
reduceTo: 'sum',
|
||||
reduceTo: 'avg',
|
||||
filters: {
|
||||
items: additionalItems[index],
|
||||
op: 'AND',
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { topOperationMetricsDownloadOptions } from 'container/MetricsApplication/constant';
|
||||
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
|
||||
@ -67,6 +68,7 @@ function TopOperationMetrics(): JSX.Element {
|
||||
globalSelectedInterval,
|
||||
variables: {},
|
||||
},
|
||||
DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
queryKey: [
|
||||
`GetMetricsQueryRange-${keyOperationWidget?.timePreferance}-${globalSelectedInterval}-${keyOperationWidget?.id}`,
|
||||
|
@ -2,12 +2,10 @@ import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import createQueryParams from 'lib/createQueryParams';
|
||||
import history from 'lib/history';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { CSSProperties } from 'react';
|
||||
import { LogsAggregatorOperator } from 'types/common/queryBuilder';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
@ -20,8 +18,6 @@ import menuItems from './menuItems';
|
||||
import { Card, Container, Text } from './styles';
|
||||
|
||||
function DashboardGraphSlider(): JSX.Element {
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
const {
|
||||
handleToggleDashboardSlider,
|
||||
layouts,
|
||||
@ -47,6 +43,7 @@ function DashboardGraphSlider(): JSX.Element {
|
||||
description: data?.description || '',
|
||||
name: data?.name || '',
|
||||
tags: data?.tags || [],
|
||||
version: data?.version || 'v3',
|
||||
layout: [
|
||||
{
|
||||
i: id,
|
||||
@ -146,13 +143,11 @@ function DashboardGraphSlider(): JSX.Element {
|
||||
);
|
||||
};
|
||||
|
||||
const fillColor: CSSProperties['color'] = isDarkMode ? 'white' : 'black';
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{menuItems.map(({ name, Icon, display }) => (
|
||||
{menuItems.map(({ name, icon, display }) => (
|
||||
<Card onClick={onClickHandler(name)} id={name} key={name}>
|
||||
<Icon fillColor={fillColor} />
|
||||
{icon}
|
||||
<Text>{display}</Text>
|
||||
</Card>
|
||||
))}
|
||||
|
@ -1,35 +0,0 @@
|
||||
import BarIcon from 'assets/Dashboard/BarIcon';
|
||||
import List from 'assets/Dashboard/List';
|
||||
import TableIcon from 'assets/Dashboard/Table';
|
||||
import TimeSeriesIcon from 'assets/Dashboard/TimeSeries';
|
||||
import ValueIcon from 'assets/Dashboard/Value';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { CSSProperties } from 'react';
|
||||
|
||||
const Items: ItemsProps[] = [
|
||||
{
|
||||
name: PANEL_TYPES.TIME_SERIES,
|
||||
Icon: TimeSeriesIcon,
|
||||
display: 'Time Series',
|
||||
},
|
||||
{
|
||||
name: PANEL_TYPES.VALUE,
|
||||
Icon: ValueIcon,
|
||||
display: 'Value',
|
||||
},
|
||||
{ name: PANEL_TYPES.TABLE, Icon: TableIcon, display: 'Table' },
|
||||
{ name: PANEL_TYPES.LIST, Icon: List, display: 'List' },
|
||||
{ name: PANEL_TYPES.BAR, Icon: BarIcon, display: 'Bar' },
|
||||
];
|
||||
|
||||
interface ItemsProps {
|
||||
name: PANEL_TYPES;
|
||||
Icon: (props: IconProps) => JSX.Element;
|
||||
display: string;
|
||||
}
|
||||
|
||||
interface IconProps {
|
||||
fillColor: CSSProperties['color'];
|
||||
}
|
||||
|
||||
export default Items;
|
@ -0,0 +1,39 @@
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { BarChart3, LineChart, List, SigmaSquare, Table } from 'lucide-react';
|
||||
|
||||
const Items: ItemsProps[] = [
|
||||
{
|
||||
name: PANEL_TYPES.TIME_SERIES,
|
||||
icon: <LineChart size={32} color={Color.BG_ROBIN_400} />,
|
||||
display: 'Time Series',
|
||||
},
|
||||
{
|
||||
name: PANEL_TYPES.VALUE,
|
||||
icon: <SigmaSquare size={32} color={Color.BG_ROBIN_400} />,
|
||||
display: 'Value',
|
||||
},
|
||||
{
|
||||
name: PANEL_TYPES.TABLE,
|
||||
icon: <Table size={32} color={Color.BG_ROBIN_400} />,
|
||||
display: 'Table',
|
||||
},
|
||||
{
|
||||
name: PANEL_TYPES.LIST,
|
||||
icon: <List size={32} color={Color.BG_ROBIN_400} />,
|
||||
display: 'List',
|
||||
},
|
||||
{
|
||||
name: PANEL_TYPES.BAR,
|
||||
icon: <BarChart3 size={32} color={Color.BG_ROBIN_400} />,
|
||||
display: 'Bar',
|
||||
},
|
||||
];
|
||||
|
||||
interface ItemsProps {
|
||||
name: PANEL_TYPES;
|
||||
icon: JSX.Element;
|
||||
display: string;
|
||||
}
|
||||
|
||||
export default Items;
|
@ -5,20 +5,33 @@ export const Container = styled.div`
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
`;
|
||||
|
||||
export const Card = styled(CardComponent)`
|
||||
min-height: 10vh;
|
||||
min-height: 80px;
|
||||
min-width: 120px;
|
||||
overflow-y: auto;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s;
|
||||
|
||||
.ant-card-body {
|
||||
padding: 12px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.ant-typography {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.05);
|
||||
border: 1px solid var(--bg-robin-400);
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -1,3 +1,16 @@
|
||||
.dashboard-description-container {
|
||||
box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.25);
|
||||
|
||||
border: 1px solid var(--bg-slate-400, #1d212d);
|
||||
background: var(--bg-ink-400, #121317);
|
||||
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
||||
color: var(--bg-vanilla-400, #c0c1c3);
|
||||
|
||||
.ant-card-body {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-description {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2; /* Show up to 2 lines */
|
||||
@ -5,3 +18,22 @@
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dashboard-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.dashboard-description-container {
|
||||
box-shadow: none;
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background-color: rgb(250, 250, 250);
|
||||
color: var(--bg-ink-300);
|
||||
|
||||
.ant-card-body {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { SettingOutlined } from '@ant-design/icons';
|
||||
import { Button } from 'antd';
|
||||
import { Button, Tooltip } from 'antd';
|
||||
import { Cog } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import DashboardSettingsContent from '../DashboardSettings';
|
||||
@ -18,14 +18,16 @@ function SettingsDrawer({ drawerTitle }: { drawerTitle: string }): JSX.Element {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={showDrawer}
|
||||
style={{ width: '100%' }}
|
||||
data-testid="show-drawer"
|
||||
>
|
||||
<SettingOutlined /> Configure
|
||||
</Button>
|
||||
<Tooltip title="Configure" placement="left">
|
||||
<Button
|
||||
className="periscope-btn"
|
||||
onClick={showDrawer}
|
||||
style={{ width: '100%' }}
|
||||
data-testid="show-drawer"
|
||||
icon={<Cog size={16} />}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
<DrawerContainer
|
||||
title={drawerTitle}
|
||||
placement="right"
|
||||
|
@ -1,11 +1,11 @@
|
||||
import './Description.styles.scss';
|
||||
|
||||
import { LockFilled, ShareAltOutlined, UnlockFilled } from '@ant-design/icons';
|
||||
import { Button, Card, Col, Row, Space, Tag, Tooltip, Typography } from 'antd';
|
||||
import { LockFilled, UnlockFilled } from '@ant-design/icons';
|
||||
import { Button, Card, Col, Row, Tag, Tooltip, Typography } from 'antd';
|
||||
import useComponentPermission from 'hooks/useComponentPermission';
|
||||
import { Share2 } from 'lucide-react';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { DashboardData } from 'types/api/dashboard/getAll';
|
||||
@ -29,7 +29,6 @@ function DashboardDescription(): JSX.Element {
|
||||
|
||||
const [openDashboardJSON, setOpenDashboardJSON] = useState<boolean>(false);
|
||||
|
||||
const { t } = useTranslation('common');
|
||||
const { user, role } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||
const [editDashboard] = useComponentPermission(['edit_dashboard'], role);
|
||||
|
||||
@ -48,7 +47,7 @@ function DashboardDescription(): JSX.Element {
|
||||
};
|
||||
|
||||
return (
|
||||
<Card style={{ marginTop: '1rem' }}>
|
||||
<Card className="dashboard-description-container">
|
||||
<Row gutter={16}>
|
||||
<Col flex={1} span={9}>
|
||||
<Typography.Title
|
||||
@ -80,12 +79,12 @@ function DashboardDescription(): JSX.Element {
|
||||
</div>
|
||||
)}
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Col span={14}>
|
||||
<Row justify="end">
|
||||
<DashboardVariableSelection />
|
||||
</Row>
|
||||
</Col>
|
||||
<Col span={3} style={{ textAlign: 'right' }}>
|
||||
<Col span={1} style={{ textAlign: 'right' }}>
|
||||
{selectedData && (
|
||||
<ShareModal
|
||||
isJSONModalVisible={openDashboardJSON}
|
||||
@ -94,18 +93,20 @@ function DashboardDescription(): JSX.Element {
|
||||
/>
|
||||
)}
|
||||
|
||||
<Space direction="vertical">
|
||||
<div className="dashboard-actions">
|
||||
{!isDashboardLocked && editDashboard && (
|
||||
<SettingsDrawer drawerTitle={title} />
|
||||
)}
|
||||
<Button
|
||||
style={{ width: '100%' }}
|
||||
type="dashed"
|
||||
onClick={onToggleHandler}
|
||||
icon={<ShareAltOutlined />}
|
||||
>
|
||||
{t('share')}
|
||||
</Button>
|
||||
|
||||
<Tooltip title="Share" placement="left">
|
||||
<Button
|
||||
className="periscope-btn"
|
||||
style={{ width: '100%' }}
|
||||
onClick={onToggleHandler}
|
||||
icon={<Share2 size={16} />}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
{(isAuthor || role === USER_ROLES.ADMIN) && (
|
||||
<Tooltip
|
||||
placement="left"
|
||||
@ -113,15 +114,13 @@ function DashboardDescription(): JSX.Element {
|
||||
>
|
||||
<Button
|
||||
style={{ width: '100%' }}
|
||||
type="dashed"
|
||||
className="periscope-btn"
|
||||
onClick={handleLockDashboardToggle}
|
||||
icon={isDashboardLocked ? <LockFilled /> : <UnlockFilled />}
|
||||
>
|
||||
{isDashboardLocked ? 'Unlock' : 'Lock'}
|
||||
</Button>
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Space>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
|
@ -2,10 +2,13 @@ import './QuerySection.styles.scss';
|
||||
|
||||
import { Button, Tabs, Tooltip, Typography } from 'antd';
|
||||
import TextToolTip from 'components/TextToolTip';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { QBShortcuts } from 'constants/shortcuts/QBShortcuts';
|
||||
import { WidgetGraphProps } from 'container/NewWidget/types';
|
||||
import { QueryBuilder } from 'container/QueryBuilder';
|
||||
import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces';
|
||||
import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys';
|
||||
import { useGetWidgetQueryRange } from 'hooks/queryBuilder/useGetWidgetQueryRange';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
||||
@ -18,7 +21,7 @@ import {
|
||||
getPreviousWidgets,
|
||||
getSelectedWidgetIndex,
|
||||
} from 'providers/Dashboard/util';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
@ -36,6 +39,7 @@ function QuerySection({
|
||||
}: QueryProps): JSX.Element {
|
||||
const { currentQuery, redirectWithQueryBuilderData } = useQueryBuilder();
|
||||
const urlQuery = useUrlQuery();
|
||||
const { registerShortcut, deregisterShortcut } = useKeyboardHotkeys();
|
||||
|
||||
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
||||
(state) => state.globalTime,
|
||||
@ -47,10 +51,13 @@ function QuerySection({
|
||||
|
||||
const { selectedDashboard, setSelectedDashboard } = useDashboard();
|
||||
|
||||
const getWidgetQueryRange = useGetWidgetQueryRange({
|
||||
graphType: selectedGraph,
|
||||
selectedTime: selectedTime.enum,
|
||||
});
|
||||
const getWidgetQueryRange = useGetWidgetQueryRange(
|
||||
{
|
||||
graphType: selectedGraph,
|
||||
selectedTime: selectedTime.enum,
|
||||
},
|
||||
selectedDashboard?.data?.version || DEFAULT_ENTITY_VERSION,
|
||||
);
|
||||
|
||||
const { widgets } = selectedDashboard?.data || {};
|
||||
|
||||
@ -149,6 +156,7 @@ function QuerySection({
|
||||
<QueryBuilder
|
||||
panelType={PANEL_TYPES.LIST}
|
||||
filterConfigs={filterConfigs}
|
||||
version={selectedDashboard?.data?.version || 'v3'}
|
||||
isListViewPanel
|
||||
/>
|
||||
),
|
||||
@ -167,7 +175,11 @@ function QuerySection({
|
||||
),
|
||||
tab: <Typography>Query Builder</Typography>,
|
||||
children: (
|
||||
<QueryBuilder panelType={selectedGraph} filterConfigs={filterConfigs} />
|
||||
<QueryBuilder
|
||||
panelType={selectedGraph}
|
||||
filterConfigs={filterConfigs}
|
||||
version={selectedDashboard?.data?.version || 'v3'}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
@ -196,6 +208,15 @@ function QuerySection({
|
||||
},
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
registerShortcut(QBShortcuts.StageAndRunQuery, handleRunQuery);
|
||||
|
||||
return (): void => {
|
||||
deregisterShortcut(QBShortcuts.StageAndRunQuery);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [handleRunQuery]);
|
||||
|
||||
return (
|
||||
<div className="dashboard-navigation">
|
||||
<Tabs
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Card, Typography } from 'antd';
|
||||
import Spinner from 'components/Spinner';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { WidgetGraphProps } from 'container/NewWidget/types';
|
||||
import { useGetWidgetQueryRange } from 'hooks/queryBuilder/useGetWidgetQueryRange';
|
||||
@ -32,10 +33,13 @@ function WidgetGraphContainer({
|
||||
|
||||
const selectedWidget = widgets.find((e) => e.id === widgetId);
|
||||
|
||||
const getWidgetQueryRange = useGetWidgetQueryRange({
|
||||
graphType: getGraphType(selectedGraph),
|
||||
selectedTime: selectedTime.enum,
|
||||
});
|
||||
const getWidgetQueryRange = useGetWidgetQueryRange(
|
||||
{
|
||||
graphType: getGraphType(selectedGraph),
|
||||
selectedTime: selectedTime.enum,
|
||||
},
|
||||
selectedDashboard?.data?.version || DEFAULT_ENTITY_VERSION,
|
||||
);
|
||||
|
||||
if (getWidgetQueryRange.data && selectedGraph === PANEL_TYPES.BAR) {
|
||||
const sortedSeriesData = getSortedSeriesData(
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { Card } from 'container/GridCardLayout/styles';
|
||||
import { useGetWidgetQueryRange } from 'hooks/queryBuilder/useGetWidgetQueryRange';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
@ -34,10 +35,13 @@ function WidgetGraph({
|
||||
|
||||
const selectedWidget = widgets.find((e) => e.id === widgetId);
|
||||
|
||||
const getWidgetQueryRange = useGetWidgetQueryRange({
|
||||
graphType: getGraphType(selectedGraph),
|
||||
selectedTime: selectedTime.enum,
|
||||
});
|
||||
const getWidgetQueryRange = useGetWidgetQueryRange(
|
||||
{
|
||||
graphType: getGraphType(selectedGraph),
|
||||
selectedTime: selectedTime.enum,
|
||||
},
|
||||
selectedDashboard?.data?.version || DEFAULT_ENTITY_VERSION,
|
||||
);
|
||||
|
||||
if (selectedWidget === undefined) {
|
||||
return <Card $panelType={selectedGraph}>Invalid widget</Card>;
|
||||
|
@ -5,7 +5,9 @@ import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { DashboardShortcuts } from 'constants/shortcuts/DashboardShortcuts';
|
||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||
import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { MESSAGE, useIsFeatureDisabled } from 'hooks/useFeatureFlag';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
@ -18,7 +20,7 @@ import {
|
||||
getPreviousWidgets,
|
||||
getSelectedWidgetIndex,
|
||||
} from 'providers/Dashboard/util';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { generatePath, useLocation, useParams } from 'react-router-dom';
|
||||
@ -53,6 +55,8 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
|
||||
const { t } = useTranslation(['dashboard']);
|
||||
|
||||
const { registerShortcut, deregisterShortcut } = useKeyboardHotkeys();
|
||||
|
||||
const { currentQuery, stagedQuery } = useQueryBuilder();
|
||||
|
||||
const isQueryModified = useMemo(
|
||||
@ -311,6 +315,17 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
isNewTraceLogsAvailable,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
registerShortcut(DashboardShortcuts.SaveChanges, onSaveDashboard);
|
||||
registerShortcut(DashboardShortcuts.DiscardChanges, onClickDiscardHandler);
|
||||
|
||||
return (): void => {
|
||||
deregisterShortcut(DashboardShortcuts.SaveChanges);
|
||||
deregisterShortcut(DashboardShortcuts.DiscardChanges);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [onSaveDashboard]);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ButtonContainer>
|
||||
@ -414,7 +429,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
<Typography>
|
||||
{t('your_graph_build_with')}{' '}
|
||||
<QueryTypeTag queryType={currentQuery.queryType} />
|
||||
{t('dashboar_ok_confirm')}
|
||||
{t('dashboard_ok_confirm')}
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography>{t('dashboard_unsave_changes')} </Typography>
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
CloseCircleTwoTone,
|
||||
LoadingOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import Header from 'container/OnboardingContainer/common/Header/Header';
|
||||
import { useOnboardingContext } from 'container/OnboardingContainer/context/OnboardingContext';
|
||||
@ -72,6 +73,9 @@ export default function LogsConnectionStatus(): JSX.Element {
|
||||
reduceTo: 'sum',
|
||||
offset: 0,
|
||||
pageSize: 100,
|
||||
timeAggregation: '',
|
||||
spaceAggregation: '',
|
||||
functions: [],
|
||||
},
|
||||
],
|
||||
queryFormulas: [],
|
||||
@ -84,6 +88,7 @@ export default function LogsConnectionStatus(): JSX.Element {
|
||||
const { data, isFetching, error, isError } = useGetExplorerQueryRange(
|
||||
requestData,
|
||||
PANEL_TYPES.LIST,
|
||||
DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
keepPreviousData: true,
|
||||
refetchInterval: pollingInterval,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import './styles.scss';
|
||||
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import {
|
||||
initialFilters,
|
||||
initialQueriesMap,
|
||||
@ -26,12 +27,15 @@ function LogsCountInInterval({
|
||||
return q;
|
||||
}, [filter]);
|
||||
|
||||
const result = useGetQueryRange({
|
||||
graphType: PANEL_TYPES.TABLE,
|
||||
query,
|
||||
selectedTime: 'GLOBAL_TIME',
|
||||
globalSelectedInterval: timeInterval,
|
||||
});
|
||||
const result = useGetQueryRange(
|
||||
{
|
||||
graphType: PANEL_TYPES.TABLE,
|
||||
query,
|
||||
selectedTime: 'GLOBAL_TIME',
|
||||
globalSelectedInterval: timeInterval,
|
||||
},
|
||||
DEFAULT_ENTITY_VERSION,
|
||||
);
|
||||
|
||||
if (!result.isFetched) {
|
||||
return null;
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import {
|
||||
initialFilters,
|
||||
initialQueriesMap,
|
||||
@ -42,12 +43,15 @@ const useSampleLogs = ({
|
||||
return q;
|
||||
}, [count, filter]);
|
||||
|
||||
const response = useGetQueryRange({
|
||||
graphType: PANEL_TYPES.LIST,
|
||||
query,
|
||||
selectedTime: 'GLOBAL_TIME',
|
||||
globalSelectedInterval: timeInterval,
|
||||
});
|
||||
const response = useGetQueryRange(
|
||||
{
|
||||
graphType: PANEL_TYPES.LIST,
|
||||
query,
|
||||
selectedTime: 'GLOBAL_TIME',
|
||||
globalSelectedInterval: timeInterval,
|
||||
},
|
||||
DEFAULT_ENTITY_VERSION,
|
||||
);
|
||||
|
||||
const { isFetching: isLoading, data } = response;
|
||||
|
||||
|
@ -27,4 +27,6 @@ export type QueryBuilderProps = {
|
||||
filterConfigs?: Partial<FilterConfigs>;
|
||||
queryComponents?: { renderOrderBy?: (props: OrderByFilterProps) => ReactNode };
|
||||
isListViewPanel?: boolean;
|
||||
showFunctions?: boolean;
|
||||
version: string;
|
||||
};
|
||||
|
@ -25,6 +25,8 @@ export const QueryBuilder = memo(function QueryBuilder({
|
||||
filterConfigs = {},
|
||||
queryComponents,
|
||||
isListViewPanel = false,
|
||||
showFunctions = false,
|
||||
version,
|
||||
}: QueryBuilderProps): JSX.Element {
|
||||
const {
|
||||
currentQuery,
|
||||
@ -170,6 +172,8 @@ export const QueryBuilder = memo(function QueryBuilder({
|
||||
: listViewLogFilterConfigs
|
||||
}
|
||||
queryComponents={queryComponents}
|
||||
showFunctions={showFunctions}
|
||||
version={version}
|
||||
isListViewPanel
|
||||
/>
|
||||
)}
|
||||
@ -188,6 +192,8 @@ export const QueryBuilder = memo(function QueryBuilder({
|
||||
query={query}
|
||||
filterConfigs={filterConfigs}
|
||||
queryComponents={queryComponents}
|
||||
showFunctions={showFunctions}
|
||||
version={version}
|
||||
/>
|
||||
</Col>
|
||||
))}
|
||||
|
@ -39,6 +39,7 @@ export function Formula({
|
||||
query,
|
||||
filterConfigs,
|
||||
formula,
|
||||
entityVersion: '',
|
||||
});
|
||||
|
||||
const [isCollapse, setIsCollapsed] = useState(false);
|
||||
@ -146,6 +147,7 @@ export function Formula({
|
||||
<Row gutter={[0, 15]}>
|
||||
<QBEntityOptions
|
||||
isCollapsed={isCollapse}
|
||||
showFunctions={false}
|
||||
entityType="formula"
|
||||
entityData={formula}
|
||||
onToggleVisibility={handleToggleDisableFormula}
|
||||
|
@ -2,11 +2,13 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 4px 0;
|
||||
width: 100%;
|
||||
|
||||
.left-col-items {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
max-width: 100%;
|
||||
|
||||
.title {
|
||||
font-weight: 500;
|
||||
@ -21,6 +23,7 @@
|
||||
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 2px;
|
||||
margin-left: -51px;
|
||||
max-width: 100%;
|
||||
|
||||
border-radius: 2px;
|
||||
|
||||
@ -63,6 +66,10 @@
|
||||
color: var(--bg-sienna-400) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.options-group {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,80 +1,107 @@
|
||||
import './QBEntityOptions.styles.scss';
|
||||
|
||||
import { Button, Col } from 'antd';
|
||||
import { Button } from 'antd';
|
||||
import cx from 'classnames';
|
||||
import { ChevronDown, ChevronRight, Eye, EyeOff, Trash2 } from 'lucide-react';
|
||||
import {
|
||||
IBuilderQuery,
|
||||
QueryFunctionProps,
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import QueryFunctions from '../QueryFunctions/QueryFunctions';
|
||||
|
||||
interface QBEntityOptionsProps {
|
||||
query?: IBuilderQuery;
|
||||
isMetricsDataSource?: boolean;
|
||||
showFunctions?: boolean;
|
||||
isCollapsed: boolean;
|
||||
entityType: string;
|
||||
entityData: any;
|
||||
onDelete: () => void;
|
||||
onToggleVisibility: () => void;
|
||||
onCollapseEntity: () => void;
|
||||
onQueryFunctionsUpdates?: (functions: QueryFunctionProps[]) => void;
|
||||
showDeleteButton: boolean;
|
||||
isListViewPanel?: boolean;
|
||||
}
|
||||
|
||||
export default function QBEntityOptions({
|
||||
query,
|
||||
isMetricsDataSource,
|
||||
isCollapsed,
|
||||
showFunctions,
|
||||
entityType,
|
||||
entityData,
|
||||
onDelete,
|
||||
onToggleVisibility,
|
||||
onCollapseEntity,
|
||||
showDeleteButton,
|
||||
isListViewPanel = false,
|
||||
onQueryFunctionsUpdates,
|
||||
isListViewPanel,
|
||||
}: QBEntityOptionsProps): JSX.Element {
|
||||
return (
|
||||
<Col span={24}>
|
||||
<div className="qb-entity-options">
|
||||
<div className="left-col-items">
|
||||
<div className="options periscope-btn-group">
|
||||
<Button.Group>
|
||||
<Button
|
||||
value="search"
|
||||
className="periscope-btn collapse"
|
||||
onClick={onCollapseEntity}
|
||||
>
|
||||
{isCollapsed ? <ChevronRight size={16} /> : <ChevronDown size={16} />}
|
||||
</Button>
|
||||
<Button
|
||||
value="query-builder"
|
||||
className="periscope-btn visibility-toggle"
|
||||
onClick={onToggleVisibility}
|
||||
disabled={isListViewPanel}
|
||||
>
|
||||
{entityData.disabled ? <EyeOff size={16} /> : <Eye size={16} />}
|
||||
</Button>
|
||||
<Button
|
||||
className={cx(
|
||||
'periscope-btn',
|
||||
entityType === 'query' ? 'query-name' : 'formula-name',
|
||||
)}
|
||||
>
|
||||
{entityData.queryName}
|
||||
</Button>
|
||||
</Button.Group>
|
||||
</div>
|
||||
<div className="qb-entity-options">
|
||||
<div className="left-col-items">
|
||||
<div className="options periscope-btn-group">
|
||||
<Button.Group className="options-group">
|
||||
<Button
|
||||
value="search"
|
||||
className="periscope-btn collapse"
|
||||
onClick={onCollapseEntity}
|
||||
>
|
||||
{isCollapsed ? <ChevronRight size={16} /> : <ChevronDown size={16} />}
|
||||
</Button>
|
||||
<Button
|
||||
value="query-builder"
|
||||
className="periscope-btn visibility-toggle"
|
||||
onClick={onToggleVisibility}
|
||||
disabled={isListViewPanel}
|
||||
>
|
||||
{entityData.disabled ? <EyeOff size={16} /> : <Eye size={16} />}
|
||||
</Button>
|
||||
|
||||
{isCollapsed && (
|
||||
<div className="title">
|
||||
<span className="entityType"> {entityType} </span> -{' '}
|
||||
<span className="entityData"> {entityData.queryName} </span>
|
||||
</div>
|
||||
)}
|
||||
<Button
|
||||
className={cx(
|
||||
'periscope-btn',
|
||||
entityType === 'query' ? 'query-name' : 'formula-name',
|
||||
)}
|
||||
>
|
||||
{entityData.queryName}
|
||||
</Button>
|
||||
|
||||
{showFunctions &&
|
||||
isMetricsDataSource &&
|
||||
query &&
|
||||
onQueryFunctionsUpdates && (
|
||||
<QueryFunctions
|
||||
queryFunctions={query.functions}
|
||||
onChange={onQueryFunctionsUpdates}
|
||||
/>
|
||||
)}
|
||||
</Button.Group>
|
||||
</div>
|
||||
|
||||
{showDeleteButton && (
|
||||
<Button className="periscope-btn ghost" onClick={onDelete}>
|
||||
<Trash2 size={14} />
|
||||
</Button>
|
||||
{isCollapsed && (
|
||||
<div className="title">
|
||||
<span className="entityType"> {entityType} </span> -{' '}
|
||||
<span className="entityData"> {entityData.queryName} </span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Col>
|
||||
|
||||
{showDeleteButton && (
|
||||
<Button className="periscope-btn ghost" onClick={onDelete}>
|
||||
<Trash2 size={14} />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
QBEntityOptions.defaultProps = {
|
||||
isListViewPanel: false,
|
||||
query: undefined,
|
||||
isMetricsDataSource: false,
|
||||
onQueryFunctionsUpdates: undefined,
|
||||
showFunctions: false,
|
||||
};
|
||||
|
@ -7,4 +7,6 @@ export type QueryProps = {
|
||||
query: IBuilderQuery;
|
||||
queryVariant: 'static' | 'dropdown';
|
||||
isListViewPanel?: boolean;
|
||||
showFunctions?: boolean;
|
||||
version: string;
|
||||
} & Pick<QueryBuilderProps, 'filterConfigs' | 'queryComponents'>;
|
||||
|
@ -3,7 +3,7 @@ import './Query.styles.scss';
|
||||
|
||||
import { Col, Input, Row } from 'antd';
|
||||
// ** Constants
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { ATTRIBUTE_TYPES, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import ROUTES from 'constants/routes';
|
||||
// ** Components
|
||||
import {
|
||||
@ -38,9 +38,11 @@ import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { transformToUpperCase } from 'utils/transformToUpperCase';
|
||||
|
||||
import QBEntityOptions from '../QBEntityOptions/QBEntityOptions';
|
||||
import SpaceAggregationOptions from '../SpaceAggregationOptions/SpaceAggregationOptions';
|
||||
// ** Types
|
||||
import { QueryProps } from './Query.interfaces';
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
export const Query = memo(function Query({
|
||||
index,
|
||||
queryVariant,
|
||||
@ -48,6 +50,8 @@ export const Query = memo(function Query({
|
||||
filterConfigs,
|
||||
queryComponents,
|
||||
isListViewPanel = false,
|
||||
showFunctions = false,
|
||||
version,
|
||||
}: QueryProps): JSX.Element {
|
||||
const { panelType, currentQuery } = useQueryBuilder();
|
||||
const { pathname } = useLocation();
|
||||
@ -56,6 +60,7 @@ export const Query = memo(function Query({
|
||||
|
||||
const {
|
||||
operators,
|
||||
spaceAggregationOptions,
|
||||
isMetricsDataSource,
|
||||
isTracePanelType,
|
||||
listOfAdditionalFilters,
|
||||
@ -63,8 +68,16 @@ export const Query = memo(function Query({
|
||||
handleChangeQueryData,
|
||||
handleChangeDataSource,
|
||||
handleChangeOperator,
|
||||
handleSpaceAggregationChange,
|
||||
handleDeleteQuery,
|
||||
} = useQueryOperations({ index, query, filterConfigs, isListViewPanel });
|
||||
handleQueryFunctionsUpdates,
|
||||
} = useQueryOperations({
|
||||
index,
|
||||
query,
|
||||
filterConfigs,
|
||||
isListViewPanel,
|
||||
entityVersion: version,
|
||||
});
|
||||
|
||||
const handleChangeAggregateEvery = useCallback(
|
||||
(value: IBuilderQuery['stepInterval']) => {
|
||||
@ -192,13 +205,17 @@ export const Query = memo(function Query({
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col span={11}>
|
||||
<Col span={24}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel label="HAVING" />
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">
|
||||
<HavingFilter onChange={handleChangeHavingFilter} query={query} />
|
||||
<HavingFilter
|
||||
entityVersion={version}
|
||||
onChange={handleChangeHavingFilter}
|
||||
query={query}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
@ -225,7 +242,11 @@ export const Query = memo(function Query({
|
||||
<FilterLabel label="HAVING" />
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">
|
||||
<HavingFilter onChange={handleChangeHavingFilter} query={query} />
|
||||
<HavingFilter
|
||||
onChange={handleChangeHavingFilter}
|
||||
entityVersion={version}
|
||||
query={query}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
@ -257,7 +278,11 @@ export const Query = memo(function Query({
|
||||
<FilterLabel label="HAVING" />
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">
|
||||
<HavingFilter onChange={handleChangeHavingFilter} query={query} />
|
||||
<HavingFilter
|
||||
entityVersion={version}
|
||||
onChange={handleChangeHavingFilter}
|
||||
query={query}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
@ -279,23 +304,33 @@ export const Query = memo(function Query({
|
||||
}, [
|
||||
panelType,
|
||||
query,
|
||||
filterConfigs?.limit?.isHidden,
|
||||
filterConfigs?.having?.isHidden,
|
||||
handleChangeLimit,
|
||||
version,
|
||||
handleChangeHavingFilter,
|
||||
renderOrderByFilter,
|
||||
renderAggregateEveryFilter,
|
||||
filterConfigs?.limit?.isHidden,
|
||||
filterConfigs?.having?.isHidden,
|
||||
]);
|
||||
|
||||
const disableOperatorSelector =
|
||||
!query?.aggregateAttribute.key || query?.aggregateAttribute.key === '';
|
||||
|
||||
const isVersionV4 = version && version === 'v4';
|
||||
|
||||
return (
|
||||
<Row gutter={[0, 12]}>
|
||||
<QBEntityOptions
|
||||
isMetricsDataSource={isMetricsDataSource}
|
||||
showFunctions={(version && version === 'v4') || showFunctions || false}
|
||||
isCollapsed={isCollapse}
|
||||
entityType="query"
|
||||
entityData={query}
|
||||
onToggleVisibility={handleToggleDisableQuery}
|
||||
onDelete={handleDeleteQuery}
|
||||
onCollapseEntity={handleToggleCollapsQuery}
|
||||
query={query}
|
||||
onQueryFunctionsUpdates={handleQueryFunctionsUpdates}
|
||||
showDeleteButton={currentQuery.builder.queryData.length > 1}
|
||||
isListViewPanel={isListViewPanel}
|
||||
/>
|
||||
@ -322,23 +357,42 @@ export const Query = memo(function Query({
|
||||
{isMetricsDataSource && (
|
||||
<Col span={12}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<OperatorsSelect
|
||||
value={query.aggregateOperator}
|
||||
onChange={handleChangeOperator}
|
||||
operators={operators}
|
||||
/>
|
||||
</Col>
|
||||
{version && version === 'v3' && (
|
||||
<Col flex="5.93rem">
|
||||
<OperatorsSelect
|
||||
value={query.aggregateOperator}
|
||||
onChange={handleChangeOperator}
|
||||
operators={operators}
|
||||
/>
|
||||
</Col>
|
||||
)}
|
||||
|
||||
<Col flex="auto">
|
||||
<AggregatorFilter
|
||||
onChange={handleChangeAggregatorAttribute}
|
||||
query={query}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
{version &&
|
||||
version === 'v4' &&
|
||||
operators &&
|
||||
Array.isArray(operators) &&
|
||||
operators.length > 0 && (
|
||||
<Col flex="5.93rem">
|
||||
<OperatorsSelect
|
||||
value={query.aggregateOperator}
|
||||
onChange={handleChangeOperator}
|
||||
operators={operators}
|
||||
disabled={disableOperatorSelector}
|
||||
/>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
<Col flex="1 1 20rem">
|
||||
|
||||
<Col flex="1 1 40rem">
|
||||
<Row gutter={[11, 5]}>
|
||||
{isMetricsDataSource && (
|
||||
<Col>
|
||||
@ -379,16 +433,40 @@ export const Query = memo(function Query({
|
||||
</Col>
|
||||
)}
|
||||
{!isListViewPanel && (
|
||||
<Col span={11} offset={isMetricsDataSource ? 0 : 2}>
|
||||
<Col span={24}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel
|
||||
label={panelType === PANEL_TYPES.VALUE ? 'Reduce to' : 'Group by'}
|
||||
/>
|
||||
{isVersionV4 && isMetricsDataSource ? (
|
||||
<SpaceAggregationOptions
|
||||
panelType={panelType}
|
||||
key={`${panelType}${query.spaceAggregation}${query.timeAggregation}`}
|
||||
aggregatorAttributeType={
|
||||
query?.aggregateAttribute.type as ATTRIBUTE_TYPES
|
||||
}
|
||||
selectedValue={query.spaceAggregation}
|
||||
disabled={disableOperatorSelector}
|
||||
onSelect={handleSpaceAggregationChange}
|
||||
operators={spaceAggregationOptions}
|
||||
/>
|
||||
) : (
|
||||
<FilterLabel
|
||||
label={panelType === PANEL_TYPES.VALUE ? 'Reduce to' : 'Group by'}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
|
||||
<Col flex="1 1 12.5rem">
|
||||
{panelType === PANEL_TYPES.VALUE ? (
|
||||
<ReduceToFilter query={query} onChange={handleChangeReduceTo} />
|
||||
<Row>
|
||||
{isVersionV4 && isMetricsDataSource && (
|
||||
<Col span={4}>
|
||||
<FilterLabel label="Reduce to" />
|
||||
</Col>
|
||||
)}
|
||||
<Col span={isVersionV4 && isMetricsDataSource ? 20 : 24}>
|
||||
<ReduceToFilter query={query} onChange={handleChangeReduceTo} />
|
||||
</Col>
|
||||
</Row>
|
||||
) : (
|
||||
<GroupByFilter
|
||||
disabled={isMetricsDataSource && !query.aggregateAttribute.key}
|
||||
@ -397,6 +475,20 @@ export const Query = memo(function Query({
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
|
||||
{isVersionV4 && isMetricsDataSource && panelType === PANEL_TYPES.TABLE && (
|
||||
<Col flex="1 1 12.5rem">
|
||||
<Row>
|
||||
<Col span={6}>
|
||||
<FilterLabel label="Reduce to" />
|
||||
</Col>
|
||||
|
||||
<Col span={18}>
|
||||
<ReduceToFilter query={query} onChange={handleChangeReduceTo} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
|
@ -0,0 +1,89 @@
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
import { Button, Flex, Input, Select } from 'antd';
|
||||
import cx from 'classnames';
|
||||
import {
|
||||
queryFunctionOptions,
|
||||
queryFunctionsTypesConfig,
|
||||
} from 'constants/queryFunctionOptions';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { debounce, isNil } from 'lodash-es';
|
||||
import { X } from 'lucide-react';
|
||||
import { QueryFunctionProps } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
interface FunctionProps {
|
||||
funcData: QueryFunctionProps;
|
||||
index: any;
|
||||
handleUpdateFunctionArgs: any;
|
||||
handleUpdateFunctionName: any;
|
||||
handleDeleteFunction: any;
|
||||
}
|
||||
|
||||
export default function Function({
|
||||
funcData,
|
||||
index,
|
||||
handleUpdateFunctionArgs,
|
||||
handleUpdateFunctionName,
|
||||
handleDeleteFunction,
|
||||
}: FunctionProps): JSX.Element {
|
||||
const isDarkMode = useIsDarkMode();
|
||||
const { showInput } = queryFunctionsTypesConfig[funcData.name];
|
||||
|
||||
let functionValue;
|
||||
|
||||
const hasValue = !isNil(
|
||||
funcData.args && funcData.args.length > 0 && funcData.args[0],
|
||||
);
|
||||
|
||||
if (hasValue) {
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
functionValue = funcData.args[0];
|
||||
}
|
||||
|
||||
const debouncedhandleUpdateFunctionArgs = debounce(
|
||||
handleUpdateFunctionArgs,
|
||||
500,
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex className="query-function">
|
||||
<Select
|
||||
className={cx('query-function-name-selector', showInput ? 'showInput' : '')}
|
||||
value={funcData.name}
|
||||
style={{ minWidth: '100px' }}
|
||||
onChange={(value): void => {
|
||||
handleUpdateFunctionName(funcData, index, value);
|
||||
}}
|
||||
dropdownStyle={{
|
||||
minWidth: 200,
|
||||
borderRadius: '4px',
|
||||
border: isDarkMode
|
||||
? '1px solid var(--bg-slate-400)'
|
||||
: '1px solid var(--bg-vanilla-300)',
|
||||
boxShadow: `4px 10px 16px 2px rgba(0, 0, 0, 0.20)`,
|
||||
}}
|
||||
placement="bottomRight"
|
||||
options={queryFunctionOptions}
|
||||
/>
|
||||
|
||||
{showInput && (
|
||||
<Input
|
||||
className="query-function-value"
|
||||
autoFocus
|
||||
defaultValue={functionValue}
|
||||
onChange={(event): void => {
|
||||
debouncedhandleUpdateFunctionArgs(funcData, index, event.target.value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Button
|
||||
className="periscope-btn query-function-delete-btn"
|
||||
onClick={(): void => {
|
||||
handleDeleteFunction(funcData, index);
|
||||
}}
|
||||
>
|
||||
<X size={12} />
|
||||
</Button>
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
.query-functions-container {
|
||||
display: flex;
|
||||
margin: 0 12px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.function-btn,
|
||||
.add-function-btn {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
cursor: pointer;
|
||||
border-radius: 3px !important;
|
||||
}
|
||||
|
||||
.function-btn {
|
||||
border-top-right-radius: 0px !important;
|
||||
border-bottom-right-radius: 0px !important;
|
||||
|
||||
.function-icon {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.add-function-btn {
|
||||
border-top-left-radius: 0px !important;
|
||||
border-bottom-left-radius: 0px !important;
|
||||
|
||||
background-color: var(--bg-slate-500) !important;
|
||||
opacity: 0.8;
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
|
||||
&.hasFunctions {
|
||||
.function-btn {
|
||||
border-top-right-radius: 3px !important;
|
||||
border-bottom-right-radius: 3px !important;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.add-function-btn {
|
||||
border-top-left-radius: 3px !important;
|
||||
border-bottom-left-radius: 3px !important;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.query-functions-list {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
.query-function {
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
height: 1px;
|
||||
width: 8px;
|
||||
position: absolute;
|
||||
left: -8px;
|
||||
top: 16px;
|
||||
z-index: 0;
|
||||
color: var(--bg-sakura-500);
|
||||
background-color: var(--bg-sakura-500);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
height: 1px;
|
||||
width: 8px;
|
||||
position: absolute;
|
||||
right: -8px;
|
||||
top: 16px;
|
||||
z-index: 0;
|
||||
color: var(--bg-sakura-500);
|
||||
background-color: var(--bg-sakura-500);
|
||||
}
|
||||
|
||||
.query-function-name-selector {
|
||||
border-top-left-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
|
||||
.ant-select-selector {
|
||||
border: none;
|
||||
background: var(--bg-ink-200);
|
||||
}
|
||||
|
||||
&.showInput {
|
||||
.ant-select-selector {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.query-function-value {
|
||||
width: 55px;
|
||||
border-left: 0;
|
||||
background: var(--bg-ink-200);
|
||||
border-radius: 0;
|
||||
border: 1px solid transparent;
|
||||
|
||||
&:focus {
|
||||
border-color: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
.query-function-delete-btn {
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
|
||||
border: none !important;
|
||||
|
||||
border-top-left-radius: 0px !important;
|
||||
border-bottom-left-radius: 0px !important;
|
||||
min-width: 24px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.query-functions-container {
|
||||
.add-function-btn {
|
||||
background-color: var(--bg-vanilla-100) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.query-functions-list {
|
||||
.query-function {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
.query-function-name-selector {
|
||||
.ant-select-selector {
|
||||
background: var(--bg-vanilla-100);
|
||||
}
|
||||
}
|
||||
|
||||
.query-function-value {
|
||||
background: var(--bg-vanilla-100);
|
||||
|
||||
&:focus {
|
||||
border-color: transparent !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
import './QueryFunctions.styles.scss';
|
||||
|
||||
import { Button, Tooltip } from 'antd';
|
||||
import cx from 'classnames';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { cloneDeep, pullAt } from 'lodash-es';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { QueryFunctionProps } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { QueryFunctionsTypes } from 'types/common/queryBuilder';
|
||||
|
||||
import Function from './Function';
|
||||
|
||||
const defaultFunctionStruct: QueryFunctionProps = {
|
||||
name: QueryFunctionsTypes.CUTOFF_MIN,
|
||||
args: [],
|
||||
};
|
||||
|
||||
interface QueryFunctionsProps {
|
||||
queryFunctions: QueryFunctionProps[];
|
||||
onChange: (functions: QueryFunctionProps[]) => void;
|
||||
}
|
||||
|
||||
// SVG component
|
||||
function FunctionIcon({
|
||||
fillColor = 'white',
|
||||
className,
|
||||
}: {
|
||||
fillColor: string;
|
||||
className: string;
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
d="M3 18.13C5.71436 18.13 6.8001 16.7728 6.8001 14.3299V8.62978C6.8001 5.91542 8.15728 4.15109 11.1431 4.55824"
|
||||
stroke={fillColor}
|
||||
strokeWidth="1.995"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3 10.2583H10.7359"
|
||||
stroke={fillColor}
|
||||
strokeWidth="1.995"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M22.0005 11.344L15.2146 18.1299"
|
||||
stroke={fillColor}
|
||||
strokeWidth="1.995"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M15.2146 11.344L22.0005 18.1299"
|
||||
stroke={fillColor}
|
||||
strokeWidth="1.995"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default function QueryFunctions({
|
||||
queryFunctions,
|
||||
onChange,
|
||||
}: QueryFunctionsProps): JSX.Element {
|
||||
const [functions, setFunctions] = useState<QueryFunctionProps[]>(
|
||||
queryFunctions,
|
||||
);
|
||||
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
const handleAddNewFunction = (): void => {
|
||||
const updatedFunctionsArr = [
|
||||
...functions,
|
||||
{
|
||||
...defaultFunctionStruct,
|
||||
},
|
||||
];
|
||||
|
||||
setFunctions(updatedFunctionsArr);
|
||||
|
||||
onChange(updatedFunctionsArr);
|
||||
};
|
||||
|
||||
const handleDeleteFunction = (
|
||||
queryFunction: QueryFunctionProps,
|
||||
index: number,
|
||||
): void => {
|
||||
const clonedFunctions = cloneDeep(functions);
|
||||
pullAt(clonedFunctions, index);
|
||||
|
||||
setFunctions(clonedFunctions);
|
||||
onChange(clonedFunctions);
|
||||
};
|
||||
|
||||
const handleUpdateFunctionName = (
|
||||
func: QueryFunctionProps,
|
||||
index: number,
|
||||
value: string,
|
||||
): void => {
|
||||
const updateFunctions = cloneDeep(functions);
|
||||
|
||||
if (updateFunctions && updateFunctions.length > 0 && updateFunctions[index]) {
|
||||
updateFunctions[index].name = value;
|
||||
setFunctions(updateFunctions);
|
||||
onChange(updateFunctions);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateFunctionArgs = (
|
||||
func: QueryFunctionProps,
|
||||
index: number,
|
||||
value: string,
|
||||
): void => {
|
||||
const updateFunctions = cloneDeep(functions);
|
||||
|
||||
if (updateFunctions && updateFunctions.length > 0 && updateFunctions[index]) {
|
||||
updateFunctions[index].args = [value];
|
||||
setFunctions(updateFunctions);
|
||||
onChange(updateFunctions);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
'query-functions-container',
|
||||
functions && functions.length > 0 ? 'hasFunctions' : '',
|
||||
)}
|
||||
>
|
||||
<Button className="periscope-btn function-btn">
|
||||
<FunctionIcon
|
||||
className="function-icon"
|
||||
fillColor={!isDarkMode ? '#0B0C0E' : 'white'}
|
||||
/>
|
||||
</Button>
|
||||
|
||||
<div className="query-functions-list">
|
||||
{functions.map((func, index) => (
|
||||
<Function
|
||||
funcData={func}
|
||||
index={index}
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
key={index}
|
||||
handleUpdateFunctionArgs={handleUpdateFunctionArgs}
|
||||
handleUpdateFunctionName={handleUpdateFunctionName}
|
||||
handleDeleteFunction={handleDeleteFunction}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Tooltip
|
||||
title={
|
||||
functions && functions.length >= 3
|
||||
? 'Functions are in early access. You can add a maximum of 3 function as of now.'
|
||||
: ''
|
||||
}
|
||||
placement="right"
|
||||
>
|
||||
<Button
|
||||
className="periscope-btn add-function-btn"
|
||||
disabled={functions && functions.length >= 3}
|
||||
onClick={handleAddNewFunction}
|
||||
>
|
||||
<Plus size={14} color={!isDarkMode ? '#0B0C0E' : 'white'} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
import { Select } from 'antd';
|
||||
import { ATTRIBUTE_TYPES, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { MetricAggregateOperator } from 'types/common/queryBuilder';
|
||||
|
||||
interface SpaceAggregationOptionsProps {
|
||||
panelType: PANEL_TYPES | null;
|
||||
selectedValue: string | undefined;
|
||||
aggregatorAttributeType: ATTRIBUTE_TYPES | null;
|
||||
disabled: boolean;
|
||||
onSelect: (value: string) => void;
|
||||
operators: any[];
|
||||
}
|
||||
|
||||
export default function SpaceAggregationOptions({
|
||||
panelType,
|
||||
selectedValue,
|
||||
aggregatorAttributeType = ATTRIBUTE_TYPES.GAUGE,
|
||||
disabled,
|
||||
onSelect,
|
||||
operators,
|
||||
}: SpaceAggregationOptionsProps): JSX.Element {
|
||||
const placeHolderText = panelType === PANEL_TYPES.VALUE ? 'Sum' : 'Sum By';
|
||||
const [defaultValue, setDefaultValue] = useState(
|
||||
selectedValue || placeHolderText,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedValue) {
|
||||
if (
|
||||
aggregatorAttributeType === ATTRIBUTE_TYPES.HISTOGRAM ||
|
||||
aggregatorAttributeType === ATTRIBUTE_TYPES.EXPONENTIAL_HISTOGRAM
|
||||
) {
|
||||
setDefaultValue(MetricAggregateOperator.P90);
|
||||
onSelect(MetricAggregateOperator.P90);
|
||||
} else if (aggregatorAttributeType === ATTRIBUTE_TYPES.SUM) {
|
||||
setDefaultValue(MetricAggregateOperator.SUM);
|
||||
onSelect(MetricAggregateOperator.SUM);
|
||||
} else if (aggregatorAttributeType === ATTRIBUTE_TYPES.GAUGE) {
|
||||
setDefaultValue(MetricAggregateOperator.AVG);
|
||||
onSelect(MetricAggregateOperator.AVG);
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [aggregatorAttributeType]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="spaceAggregationOptionsContainer"
|
||||
key={aggregatorAttributeType}
|
||||
>
|
||||
<Select
|
||||
defaultValue={defaultValue}
|
||||
style={{ minWidth: '5.625rem' }}
|
||||
disabled={disabled}
|
||||
onChange={onSelect}
|
||||
>
|
||||
{operators.map((operator) => (
|
||||
<Select.Option key={operator.value} value={operator.value}>
|
||||
{operator.label} {panelType !== PANEL_TYPES.VALUE ? ' By' : ''}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -111,7 +111,7 @@ export const AggregatorFilter = memo(function AggregatorFilter({
|
||||
debouncedValue,
|
||||
query.aggregateOperator,
|
||||
query.dataSource,
|
||||
])?.payload.attributeKeys || [],
|
||||
])?.payload?.attributeKeys || [],
|
||||
[debouncedValue, query.aggregateOperator, query.dataSource, queryClient],
|
||||
);
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Having, IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
export type HavingFilterProps = {
|
||||
entityVersion: string;
|
||||
query: IBuilderQuery;
|
||||
onChange: (having: Having[]) => void;
|
||||
};
|
||||
|
@ -22,6 +22,7 @@ import { getHavingObject, isValidHavingValue } from '../utils';
|
||||
import { HavingFilterProps } from './HavingFilter.interfaces';
|
||||
|
||||
export function HavingFilter({
|
||||
entityVersion,
|
||||
query,
|
||||
onChange,
|
||||
}: HavingFilterProps): JSX.Element {
|
||||
@ -48,10 +49,18 @@ export function HavingFilter({
|
||||
[query],
|
||||
);
|
||||
|
||||
const columnName = useMemo(
|
||||
() => `${query.aggregateOperator.toUpperCase()}(${aggregatorAttribute})`,
|
||||
[query, aggregatorAttribute],
|
||||
);
|
||||
const columnName = useMemo(() => {
|
||||
if (
|
||||
query &&
|
||||
query.dataSource === DataSource.METRICS &&
|
||||
query.spaceAggregation &&
|
||||
entityVersion === 'v4'
|
||||
) {
|
||||
return `${query.spaceAggregation.toUpperCase()}(${aggregatorAttribute})`;
|
||||
}
|
||||
|
||||
return `${query.aggregateOperator.toUpperCase()}(${aggregatorAttribute})`;
|
||||
}, [query, aggregatorAttribute, entityVersion]);
|
||||
|
||||
const aggregatorOptions: SelectOption<string, string>[] = useMemo(
|
||||
() => [{ label: columnName, value: columnName }],
|
||||
@ -211,7 +220,7 @@ export function HavingFilter({
|
||||
disabled={isMetricsDataSource && !query.aggregateAttribute.key}
|
||||
style={{ width: '100%' }}
|
||||
notFoundContent={currentFormValue.value.length === 0 ? undefined : null}
|
||||
placeholder="Count(operation) > 5"
|
||||
placeholder="GroupBy(operation) > 5"
|
||||
onDeselect={handleDeselect}
|
||||
onChange={handleChange}
|
||||
onSelect={handleSelect}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
// Constants
|
||||
import {
|
||||
HAVING_OPERATORS,
|
||||
@ -31,6 +32,7 @@ describe('Having filter behaviour', () => {
|
||||
<HavingFilter
|
||||
query={initialQueryBuilderFormValuesMap.metrics}
|
||||
onChange={mockFn}
|
||||
entityVersion={DEFAULT_ENTITY_VERSION}
|
||||
/>,
|
||||
);
|
||||
|
||||
@ -49,6 +51,7 @@ describe('Having filter behaviour', () => {
|
||||
<HavingFilter
|
||||
query={initialQueryBuilderFormValuesMap.metrics}
|
||||
onChange={mockFn}
|
||||
entityVersion={DEFAULT_ENTITY_VERSION}
|
||||
/>,
|
||||
);
|
||||
|
||||
@ -62,7 +65,11 @@ describe('Having filter behaviour', () => {
|
||||
test('Is having filter is enable', () => {
|
||||
const mockFn = jest.fn();
|
||||
const { unmount } = render(
|
||||
<HavingFilter query={valueWithAttributeAndOperator} onChange={mockFn} />,
|
||||
<HavingFilter
|
||||
query={valueWithAttributeAndOperator}
|
||||
onChange={mockFn}
|
||||
entityVersion={DEFAULT_ENTITY_VERSION}
|
||||
/>,
|
||||
);
|
||||
|
||||
const input = screen.getByRole('combobox');
|
||||
@ -80,7 +87,11 @@ describe('Having filter behaviour', () => {
|
||||
const optionTestTitle = 'havingOption';
|
||||
|
||||
const { unmount } = render(
|
||||
<HavingFilter query={valueWithAttributeAndOperator} onChange={onChange} />,
|
||||
<HavingFilter
|
||||
query={valueWithAttributeAndOperator}
|
||||
onChange={onChange}
|
||||
entityVersion={DEFAULT_ENTITY_VERSION}
|
||||
/>,
|
||||
);
|
||||
|
||||
// get input
|
||||
|
@ -1,9 +1,8 @@
|
||||
import {
|
||||
SelectOptionContainer,
|
||||
TagContainer,
|
||||
TagLabel,
|
||||
TagValue,
|
||||
} from './style';
|
||||
import './QueryBuilderSearch.styles.scss';
|
||||
|
||||
import { Tooltip } from 'antd';
|
||||
|
||||
import { TagContainer, TagLabel, TagValue } from './style';
|
||||
import { getOptionType } from './utils';
|
||||
|
||||
function OptionRenderer({
|
||||
@ -16,21 +15,25 @@ function OptionRenderer({
|
||||
return (
|
||||
<span className="option">
|
||||
{optionType ? (
|
||||
<SelectOptionContainer>
|
||||
<div className="option-value">{value}</div>
|
||||
<div className="option-meta-data-container">
|
||||
<TagContainer>
|
||||
<TagLabel>Type: </TagLabel>
|
||||
<TagValue>{optionType}</TagValue>
|
||||
</TagContainer>
|
||||
<TagContainer>
|
||||
<TagLabel>Data type: </TagLabel>
|
||||
<TagValue>{dataType}</TagValue>
|
||||
</TagContainer>
|
||||
<Tooltip title={`${value}`} placement="topLeft">
|
||||
<div className="selectOptionContainer">
|
||||
<div className="option-value">{value}</div>
|
||||
<div className="option-meta-data-container">
|
||||
<TagContainer>
|
||||
<TagLabel>Type: </TagLabel>
|
||||
<TagValue>{optionType}</TagValue>
|
||||
</TagContainer>
|
||||
<TagContainer>
|
||||
<TagLabel>Data type: </TagLabel>
|
||||
<TagValue>{dataType}</TagValue>
|
||||
</TagContainer>
|
||||
</div>
|
||||
</div>
|
||||
</SelectOptionContainer>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<span>{label}</span>
|
||||
<Tooltip title={label} placement="topLeft">
|
||||
<span>{label}</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
|
@ -1,3 +1,16 @@
|
||||
.selectOptionContainer {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
overflow-x: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0.2rem;
|
||||
height: 0.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.query-builder-search {
|
||||
.ant-select-dropdown {
|
||||
|
@ -16,19 +16,11 @@ export const StyledCheckOutlined = styled(CheckOutlined)`
|
||||
float: right;
|
||||
`;
|
||||
|
||||
export const SelectOptionContainer = styled.div`
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
overflow-x: auto;
|
||||
`;
|
||||
|
||||
export const TagContainer = styled(Tag)`
|
||||
&&& {
|
||||
border-radius: 3px;
|
||||
padding: 0.3rem 0.3rem;
|
||||
font-weight: 400;
|
||||
padding: 0.1rem 0.2rem;
|
||||
font-weight: 300;
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
`;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { WarningFilled } from '@ant-design/icons';
|
||||
import { Flex, Typography } from 'antd';
|
||||
import { ResizeTable } from 'components/ResizeTable';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { MAX_RPS_LIMIT } from 'constants/global';
|
||||
import ResourceAttributesFilter from 'container/ResourceAttributesFilter';
|
||||
import { useGetQueriesRange } from 'hooks/queryBuilder/useGetQueriesRange';
|
||||
@ -35,22 +36,26 @@ function ServiceMetricTable({
|
||||
const { data: licenseData, isFetching } = useLicense();
|
||||
const isCloudUserVal = isCloudUser();
|
||||
|
||||
const queries = useGetQueriesRange(queryRangeRequestData, {
|
||||
queryKey: [
|
||||
`GetMetricsQueryRange-${queryRangeRequestData[0].selectedTime}-${globalSelectedInterval}`,
|
||||
maxTime,
|
||||
minTime,
|
||||
globalSelectedInterval,
|
||||
],
|
||||
keepPreviousData: true,
|
||||
enabled: true,
|
||||
refetchOnMount: false,
|
||||
onError: (error) => {
|
||||
notifications.error({
|
||||
message: error.message,
|
||||
});
|
||||
const queries = useGetQueriesRange(
|
||||
queryRangeRequestData,
|
||||
DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
queryKey: [
|
||||
`GetMetricsQueryRange-${queryRangeRequestData[0].selectedTime}-${globalSelectedInterval}`,
|
||||
maxTime,
|
||||
minTime,
|
||||
globalSelectedInterval,
|
||||
],
|
||||
keepPreviousData: true,
|
||||
enabled: true,
|
||||
refetchOnMount: false,
|
||||
onError: (error) => {
|
||||
notifications.error({
|
||||
message: error.message,
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
const isLoading = queries.some((query) => query.isLoading);
|
||||
const services: ServicesList[] = useMemo(
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||
@ -49,6 +50,7 @@ function TimeSeriesViewContainer({
|
||||
dataSource,
|
||||
},
|
||||
},
|
||||
DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
queryKey: [
|
||||
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
||||
|
@ -49,13 +49,16 @@ export const getTraceToLogsQuery = (
|
||||
limit: null,
|
||||
aggregateAttribute: initialAutocompleteData,
|
||||
aggregateOperator: LogsAggregatorOperator.NOOP,
|
||||
timeAggregation: '',
|
||||
spaceAggregation: '',
|
||||
functions: [],
|
||||
expression: 'A',
|
||||
groupBy: [],
|
||||
having: [],
|
||||
legend: '',
|
||||
orderBy: [],
|
||||
queryName: 'A',
|
||||
reduceTo: 'min',
|
||||
reduceTo: 'avg',
|
||||
stepInterval: getStep({
|
||||
start: minTime,
|
||||
end: maxTime,
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ResizeTable } from 'components/ResizeTable';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
@ -62,6 +63,7 @@ function ListView(): JSX.Element {
|
||||
selectColumns: options?.selectColumns,
|
||||
},
|
||||
},
|
||||
DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
queryKey: [
|
||||
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
||||
|
@ -53,6 +53,7 @@ function QuerySection(): JSX.Element {
|
||||
}}
|
||||
filterConfigs={filterConfigs}
|
||||
queryComponents={queryComponents}
|
||||
version="v3" // setting this to v3 as we this is rendered in logs explorer
|
||||
actions={
|
||||
<ButtonWrapper>
|
||||
<Button onClick={handleRunQuery} type="primary">
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Space } from 'antd';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import { QueryTable } from 'container/QueryTable';
|
||||
@ -27,6 +28,7 @@ function TableView(): JSX.Element {
|
||||
dataSource: 'traces',
|
||||
},
|
||||
},
|
||||
DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
queryKey: [
|
||||
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Typography } from 'antd';
|
||||
import { ResizeTable } from 'components/ResizeTable';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
@ -41,6 +42,7 @@ function TracesView(): JSX.Element {
|
||||
pagination: paginationQueryData,
|
||||
},
|
||||
},
|
||||
DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
queryKey: [
|
||||
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
||||
|
@ -1,65 +1,73 @@
|
||||
.traces-table {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.resize-table {
|
||||
height: calc(90% - 5px);
|
||||
overflow: scroll;
|
||||
|
||||
.ant-table-wrapper .ant-table-tbody >tr >td {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
color: var(--bg-vanilla-100);
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
padding: 10px 8px;
|
||||
font-family: Inter;
|
||||
cursor: pointer;
|
||||
}
|
||||
.resize-table {
|
||||
height: calc(100% - 40px);
|
||||
overflow: scroll;
|
||||
overflow-x: hidden;
|
||||
|
||||
.ant-table-wrapper .ant-table-thead > tr > th {
|
||||
font-family: Inter;
|
||||
color: var(--bg-vanilla-100);
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-bottom: 0.5px solid var(--bg-slate-400);
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
padding: 8px;
|
||||
}
|
||||
&::-webkit-scrollbar {
|
||||
width: 0.2rem;
|
||||
height: 0.2rem;
|
||||
}
|
||||
|
||||
.ant-table-wrapper .ant-table-thead > tr > th::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.ant-table-wrapper .ant-table-tbody > tr > td {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
color: var(--bg-vanilla-100);
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
padding: 10px 8px;
|
||||
font-family: Inter;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.controller {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
right: 10px;
|
||||
}
|
||||
.ant-table-wrapper .ant-table-thead > tr > th {
|
||||
font-family: Inter;
|
||||
color: var(--bg-vanilla-100);
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-bottom: 0.5px solid var(--bg-slate-400);
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.ant-table-wrapper .ant-table-thead > tr > th::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.controller {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
justify-content: end;
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.traces-table {
|
||||
.resize-table {
|
||||
.ant-table-wrapper .ant-table-tbody >tr >td {
|
||||
background-color: var(--bg-vanilla-100);
|
||||
color: var(--bg-ink-500);
|
||||
border-color: rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
.ant-table-wrapper .ant-table-thead > tr > th {
|
||||
background-color: var(--bg-vanilla-300);
|
||||
color: var(--bg-ink-500);
|
||||
border-color: rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.traces-table {
|
||||
.resize-table {
|
||||
.ant-table-wrapper .ant-table-tbody > tr > td {
|
||||
background-color: var(--bg-vanilla-100);
|
||||
color: var(--bg-ink-500);
|
||||
border-color: rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
.ant-table-wrapper .ant-table-thead > tr > th {
|
||||
background-color: var(--bg-vanilla-300);
|
||||
color: var(--bg-ink-500);
|
||||
border-color: rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
function TracesTableComponent({
|
||||
selectedTracesFields,
|
||||
query,
|
||||
version,
|
||||
selectedTime,
|
||||
}: TracesTableComponentProps): JSX.Element {
|
||||
const { selectedTime: globalSelectedTime, maxTime, minTime } = useSelector<
|
||||
@ -59,6 +60,7 @@ function TracesTableComponent({
|
||||
},
|
||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||
},
|
||||
version,
|
||||
{
|
||||
queryKey: [
|
||||
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
||||
@ -160,6 +162,7 @@ function TracesTableComponent({
|
||||
export type TracesTableComponentProps = {
|
||||
selectedTracesFields: Widgets['selectedTracesFields'];
|
||||
query: Query;
|
||||
version: string;
|
||||
selectedTime?: timePreferance;
|
||||
};
|
||||
|
||||
|
@ -72,6 +72,9 @@ function KeyboardHotkeysProvider({
|
||||
shortcutKey = shortcutKey + isAltKey + isShiftKey + isMetaKey;
|
||||
|
||||
if (shortcuts.current[shortcutKey]) {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
shortcuts.current[shortcutKey]();
|
||||
}
|
||||
};
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { getQueryRangeFormat } from 'api/dashboard/queryRangeFormat';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
@ -45,7 +46,9 @@ const useCreateAlerts = (widget?: Widgets): VoidFunction => {
|
||||
history.push(
|
||||
`${ROUTES.ALERTS_NEW}?${QueryParams.compositeQuery}=${encodeURIComponent(
|
||||
JSON.stringify(updatedQuery),
|
||||
)}&${QueryParams.panelTypes}=${widget.panelTypes}`,
|
||||
)}&${QueryParams.panelTypes}=${widget.panelTypes}&version=${
|
||||
selectedDashboard?.data.version || DEFAULT_ENTITY_VERSION
|
||||
}`,
|
||||
);
|
||||
},
|
||||
onError: () => {
|
||||
@ -59,6 +62,7 @@ const useCreateAlerts = (widget?: Widgets): VoidFunction => {
|
||||
notifications,
|
||||
queryRangeMutation,
|
||||
selectedDashboard?.data.variables,
|
||||
selectedDashboard?.data.version,
|
||||
widget,
|
||||
]);
|
||||
};
|
||||
|
@ -15,6 +15,7 @@ import { useQueryBuilder } from './useQueryBuilder';
|
||||
export const useGetExplorerQueryRange = (
|
||||
requestData: Query | null,
|
||||
panelType: PANEL_TYPES | null,
|
||||
version: string,
|
||||
options?: UseQueryOptions<SuccessResponse<MetricRangePayloadProps>, Error>,
|
||||
params?: Record<string, unknown>,
|
||||
isDependentOnQB = true,
|
||||
@ -47,6 +48,7 @@ export const useGetExplorerQueryRange = (
|
||||
query: requestData || initialQueriesMap.metrics,
|
||||
params,
|
||||
},
|
||||
version,
|
||||
{
|
||||
...options,
|
||||
retry: false,
|
||||
|
@ -15,6 +15,7 @@ import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
|
||||
export const useGetQueriesRange = (
|
||||
requestData: GetQueryResultsProps[],
|
||||
version: string,
|
||||
options: UseQueryOptions<SuccessResponse<MetricRangePayloadProps>, Error>,
|
||||
): UseQueryResult<SuccessResponse<MetricRangePayloadProps>, Error>[] => {
|
||||
const queryKey = useMemo(() => {
|
||||
@ -26,7 +27,7 @@ export const useGetQueriesRange = (
|
||||
|
||||
const queryData = requestData.map((request, index) => ({
|
||||
queryFn: async (): Promise<SuccessResponse<MetricRangePayloadProps>> =>
|
||||
GetMetricQueryRange(request),
|
||||
GetMetricQueryRange(request, version),
|
||||
...options,
|
||||
queryKey: [...queryKey, index] as QueryKey,
|
||||
}));
|
||||
|
@ -11,10 +11,15 @@ import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
|
||||
type UseGetQueryRange = (
|
||||
requestData: GetQueryResultsProps,
|
||||
version: string,
|
||||
options?: UseQueryOptions<SuccessResponse<MetricRangePayloadProps>, Error>,
|
||||
) => UseQueryResult<SuccessResponse<MetricRangePayloadProps>, Error>;
|
||||
|
||||
export const useGetQueryRange: UseGetQueryRange = (requestData, options) => {
|
||||
export const useGetQueryRange: UseGetQueryRange = (
|
||||
requestData,
|
||||
version,
|
||||
options,
|
||||
) => {
|
||||
const newRequestData: GetQueryResultsProps = useMemo(
|
||||
() => ({
|
||||
...requestData,
|
||||
@ -39,7 +44,8 @@ export const useGetQueryRange: UseGetQueryRange = (requestData, options) => {
|
||||
}, [options?.queryKey, newRequestData]);
|
||||
|
||||
return useQuery<SuccessResponse<MetricRangePayloadProps>, Error>({
|
||||
queryFn: async ({ signal }) => GetMetricQueryRange(newRequestData, signal),
|
||||
queryFn: async ({ signal }) =>
|
||||
GetMetricQueryRange(requestData, version, signal),
|
||||
...options,
|
||||
queryKey,
|
||||
});
|
||||
|
@ -18,6 +18,7 @@ export const useGetWidgetQueryRange = (
|
||||
graphType,
|
||||
selectedTime,
|
||||
}: Pick<GetQueryResultsProps, 'graphType' | 'selectedTime'>,
|
||||
version: string,
|
||||
options?: UseQueryOptions<SuccessResponse<MetricRangePayloadProps>, Error>,
|
||||
): UseQueryResult<SuccessResponse<MetricRangePayloadProps>, Error> => {
|
||||
const { selectedTime: globalSelectedInterval } = useSelector<
|
||||
@ -37,6 +38,7 @@ export const useGetWidgetQueryRange = (
|
||||
query: stagedQuery || initialQueriesMap.metrics,
|
||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||
},
|
||||
version,
|
||||
{
|
||||
enabled: !!stagedQuery,
|
||||
retry: false,
|
||||
|
@ -1,16 +1,23 @@
|
||||
import { LEGEND } from 'constants/global';
|
||||
import {
|
||||
ATTRIBUTE_TYPES,
|
||||
initialAutocompleteData,
|
||||
initialQueryBuilderFormValuesMap,
|
||||
mapOfFormulaToFilters,
|
||||
mapOfQueryFilters,
|
||||
PANEL_TYPES,
|
||||
} from 'constants/queryBuilder';
|
||||
import {
|
||||
metricsGaugeSpaceAggregateOperatorOptions,
|
||||
metricsHistogramSpaceAggregateOperatorOptions,
|
||||
metricsSumSpaceAggregateOperatorOptions,
|
||||
} from 'constants/queryBuilderOperators';
|
||||
import {
|
||||
listViewInitialLogQuery,
|
||||
listViewInitialTraceQuery,
|
||||
} from 'container/NewDashboard/ComponentsSlider/constants';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { getMetricsOperatorsByAttributeType } from 'lib/newQueryBuilder/getMetricsOperatorsByAttributeType';
|
||||
import { getOperatorsBySourceAndPanelType } from 'lib/newQueryBuilder/getOperatorsBySourceAndPanelType';
|
||||
import { findDataTypeOfOperator } from 'lib/query/findDataTypeOfOperator';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
@ -18,13 +25,14 @@ import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteRe
|
||||
import {
|
||||
IBuilderFormula,
|
||||
IBuilderQuery,
|
||||
QueryFunctionProps,
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
import {
|
||||
HandleChangeFormulaData,
|
||||
HandleChangeQueryData,
|
||||
UseQueryOperations,
|
||||
} from 'types/common/operations.types';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
import { DataSource, MetricAggregateOperator } from 'types/common/queryBuilder';
|
||||
import { SelectOption } from 'types/common/select';
|
||||
import { getFormatedLegend } from 'utils/getFormatedLegend';
|
||||
|
||||
@ -34,6 +42,7 @@ export const useQueryOperations: UseQueryOperations = ({
|
||||
filterConfigs,
|
||||
formula,
|
||||
isListViewPanel = false,
|
||||
entityVersion,
|
||||
}) => {
|
||||
const {
|
||||
handleSetQueryData,
|
||||
@ -46,6 +55,9 @@ export const useQueryOperations: UseQueryOperations = ({
|
||||
} = useQueryBuilder();
|
||||
|
||||
const [operators, setOperators] = useState<SelectOption<string, string>[]>([]);
|
||||
const [spaceAggregationOptions, setSpaceAggregationOptions] = useState<
|
||||
SelectOption<string, string>[]
|
||||
>([]);
|
||||
|
||||
const { dataSource, aggregateOperator } = query;
|
||||
|
||||
@ -104,6 +116,7 @@ export const useQueryOperations: UseQueryOperations = ({
|
||||
const newQuery: IBuilderQuery = {
|
||||
...query,
|
||||
aggregateOperator: value,
|
||||
timeAggregation: value,
|
||||
having: [],
|
||||
limit: null,
|
||||
...(shouldResetAggregateAttribute
|
||||
@ -116,6 +129,52 @@ export const useQueryOperations: UseQueryOperations = ({
|
||||
[index, query, handleSetQueryData],
|
||||
);
|
||||
|
||||
const handleSpaceAggregationChange = useCallback(
|
||||
(value: string): void => {
|
||||
const newQuery: IBuilderQuery = {
|
||||
...query,
|
||||
spaceAggregation: value,
|
||||
};
|
||||
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[index, query, handleSetQueryData],
|
||||
);
|
||||
|
||||
const handleMetricAggregateAtributeTypes = useCallback(
|
||||
(aggregateAttribute: BaseAutocompleteData): any => {
|
||||
const newOperators = getMetricsOperatorsByAttributeType({
|
||||
dataSource: DataSource.METRICS,
|
||||
panelType: panelType || PANEL_TYPES.TIME_SERIES,
|
||||
aggregateAttributeType:
|
||||
(aggregateAttribute.type as ATTRIBUTE_TYPES) || ATTRIBUTE_TYPES.GAUGE,
|
||||
});
|
||||
|
||||
switch (aggregateAttribute.type) {
|
||||
case ATTRIBUTE_TYPES.SUM:
|
||||
setSpaceAggregationOptions(metricsSumSpaceAggregateOperatorOptions);
|
||||
break;
|
||||
case ATTRIBUTE_TYPES.GAUGE:
|
||||
setSpaceAggregationOptions(metricsGaugeSpaceAggregateOperatorOptions);
|
||||
break;
|
||||
|
||||
case ATTRIBUTE_TYPES.HISTOGRAM:
|
||||
setSpaceAggregationOptions(metricsHistogramSpaceAggregateOperatorOptions);
|
||||
break;
|
||||
|
||||
case ATTRIBUTE_TYPES.EXPONENTIAL_HISTOGRAM:
|
||||
setSpaceAggregationOptions(metricsHistogramSpaceAggregateOperatorOptions);
|
||||
break;
|
||||
default:
|
||||
setSpaceAggregationOptions(metricsGaugeSpaceAggregateOperatorOptions);
|
||||
break;
|
||||
}
|
||||
|
||||
setOperators(newOperators);
|
||||
},
|
||||
[panelType],
|
||||
);
|
||||
|
||||
const handleChangeAggregatorAttribute = useCallback(
|
||||
(value: BaseAutocompleteData): void => {
|
||||
const newQuery: IBuilderQuery = {
|
||||
@ -124,9 +183,31 @@ export const useQueryOperations: UseQueryOperations = ({
|
||||
having: [],
|
||||
};
|
||||
|
||||
if (newQuery.dataSource === DataSource.METRICS && entityVersion === 'v4') {
|
||||
handleMetricAggregateAtributeTypes(newQuery.aggregateAttribute);
|
||||
|
||||
if (newQuery.aggregateAttribute.type === ATTRIBUTE_TYPES.SUM) {
|
||||
newQuery.aggregateOperator = MetricAggregateOperator.RATE;
|
||||
newQuery.timeAggregation = MetricAggregateOperator.RATE;
|
||||
} else if (newQuery.aggregateAttribute.type === ATTRIBUTE_TYPES.GAUGE) {
|
||||
newQuery.aggregateOperator = MetricAggregateOperator.AVG;
|
||||
newQuery.timeAggregation = MetricAggregateOperator.AVG;
|
||||
} else {
|
||||
newQuery.timeAggregation = '';
|
||||
}
|
||||
|
||||
newQuery.spaceAggregation = '';
|
||||
}
|
||||
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[index, query, handleSetQueryData],
|
||||
[
|
||||
query,
|
||||
entityVersion,
|
||||
handleSetQueryData,
|
||||
index,
|
||||
handleMetricAggregateAtributeTypes,
|
||||
],
|
||||
);
|
||||
|
||||
const handleChangeDataSource = useCallback(
|
||||
@ -203,6 +284,21 @@ export const useQueryOperations: UseQueryOperations = ({
|
||||
[formula, handleSetFormulaData, index],
|
||||
);
|
||||
|
||||
const handleQueryFunctionsUpdates = useCallback(
|
||||
(functions: QueryFunctionProps[]): void => {
|
||||
const newQuery: IBuilderQuery = {
|
||||
...query,
|
||||
};
|
||||
|
||||
if (newQuery.dataSource === DataSource.METRICS) {
|
||||
newQuery.functions = functions;
|
||||
}
|
||||
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[query, handleSetQueryData, index],
|
||||
);
|
||||
|
||||
const isMetricsDataSource = query.dataSource === DataSource.METRICS;
|
||||
|
||||
const isTracePanelType = panelType === PANEL_TYPES.TRACE;
|
||||
@ -210,15 +306,26 @@ export const useQueryOperations: UseQueryOperations = ({
|
||||
useEffect(() => {
|
||||
if (initialDataSource && dataSource !== initialDataSource) return;
|
||||
|
||||
const initialOperators = getOperatorsBySourceAndPanelType({
|
||||
dataSource,
|
||||
panelType: panelType || PANEL_TYPES.TIME_SERIES,
|
||||
});
|
||||
if (
|
||||
dataSource === DataSource.METRICS &&
|
||||
query &&
|
||||
query.aggregateAttribute &&
|
||||
entityVersion === 'v4'
|
||||
) {
|
||||
handleMetricAggregateAtributeTypes(query.aggregateAttribute);
|
||||
} else {
|
||||
const initialOperators = getOperatorsBySourceAndPanelType({
|
||||
dataSource,
|
||||
panelType: panelType || PANEL_TYPES.TIME_SERIES,
|
||||
});
|
||||
|
||||
if (JSON.stringify(operators) === JSON.stringify(initialOperators)) return;
|
||||
if (JSON.stringify(operators) === JSON.stringify(initialOperators)) return;
|
||||
|
||||
setOperators(initialOperators);
|
||||
}, [dataSource, initialDataSource, panelType, operators]);
|
||||
setOperators(initialOperators);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [dataSource, initialDataSource, panelType, operators, entityVersion]);
|
||||
|
||||
useEffect(() => {
|
||||
const additionalFilters = getNewListOfAdditionalFilters(dataSource, true);
|
||||
@ -236,13 +343,16 @@ export const useQueryOperations: UseQueryOperations = ({
|
||||
isTracePanelType,
|
||||
isMetricsDataSource,
|
||||
operators,
|
||||
spaceAggregationOptions,
|
||||
listOfAdditionalFilters,
|
||||
handleChangeOperator,
|
||||
handleSpaceAggregationChange,
|
||||
handleChangeAggregatorAttribute,
|
||||
handleChangeDataSource,
|
||||
handleDeleteQuery,
|
||||
handleChangeQueryData,
|
||||
listOfAdditionalFormulaFilters,
|
||||
handleChangeFormulaData,
|
||||
handleQueryFunctionsUpdates,
|
||||
};
|
||||
};
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import {
|
||||
initialQueryBuilderFormValues,
|
||||
@ -126,6 +127,7 @@ export const useLogsData = ({
|
||||
const { data, isFetching } = useGetExplorerQueryRange(
|
||||
requestData,
|
||||
panelType,
|
||||
DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
keepPreviousData: true,
|
||||
enabled: !isLimit && !!requestData,
|
||||
|
@ -18,11 +18,16 @@ import { prepareQueryRangePayload } from './prepareQueryRangePayload';
|
||||
|
||||
export async function GetMetricQueryRange(
|
||||
props: GetQueryResultsProps,
|
||||
version: string,
|
||||
signal?: AbortSignal,
|
||||
): Promise<SuccessResponse<MetricRangePayloadProps>> {
|
||||
const { legendMap, queryPayload } = prepareQueryRangePayload(props);
|
||||
|
||||
const response = await getMetricsQueryRange(queryPayload, signal);
|
||||
const response = await getMetricsQueryRange(
|
||||
queryPayload,
|
||||
version || 'v3',
|
||||
signal,
|
||||
);
|
||||
|
||||
if (response.statusCode >= 400) {
|
||||
throw new Error(
|
||||
|
@ -144,8 +144,6 @@ export const parseQuery = (queryString) => {
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(parsedRaw);
|
||||
return parsedRaw;
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,31 @@
|
||||
import {
|
||||
ATTRIBUTE_TYPES,
|
||||
metricsOperatorsByType,
|
||||
PANEL_TYPES,
|
||||
} from 'constants/queryBuilder';
|
||||
import { metricsEmptyTimeAggregateOperatorOptions } from 'constants/queryBuilderOperators';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
import { SelectOption } from 'types/common/select';
|
||||
|
||||
type GetQueryOperatorsParams = {
|
||||
dataSource: DataSource;
|
||||
panelType: PANEL_TYPES;
|
||||
aggregateAttributeType: ATTRIBUTE_TYPES;
|
||||
};
|
||||
|
||||
export const getMetricsOperatorsByAttributeType = ({
|
||||
dataSource,
|
||||
aggregateAttributeType,
|
||||
}: GetQueryOperatorsParams): SelectOption<string, string>[] => {
|
||||
if (dataSource === DataSource.METRICS && aggregateAttributeType) {
|
||||
if (aggregateAttributeType === ATTRIBUTE_TYPES.SUM) {
|
||||
return metricsOperatorsByType.Sum;
|
||||
}
|
||||
|
||||
if (aggregateAttributeType === ATTRIBUTE_TYPES.GAUGE) {
|
||||
return metricsOperatorsByType.Gauge;
|
||||
}
|
||||
}
|
||||
|
||||
return metricsEmptyTimeAggregateOperatorOptions;
|
||||
};
|
@ -18,6 +18,7 @@ interface UplotTooltipDataProps {
|
||||
value: number;
|
||||
tooltipValue: string;
|
||||
textContent: string;
|
||||
queryName: string;
|
||||
}
|
||||
|
||||
const generateTooltipContent = (
|
||||
@ -35,6 +36,7 @@ const generateTooltipContent = (
|
||||
|
||||
let tooltipTitle = '';
|
||||
const formattedData: Record<string, UplotTooltipDataProps> = {};
|
||||
const duplicatedLegendLabels: Record<string, true> = {};
|
||||
|
||||
function sortTooltipContentBasedOnValue(
|
||||
tooltipDataObj: Record<string, UplotTooltipDataProps>,
|
||||
@ -57,8 +59,29 @@ const generateTooltipContent = (
|
||||
|
||||
const color = generateColor(label, themeColors.chartcolors);
|
||||
|
||||
let tooltipItemLabel = label;
|
||||
|
||||
if (Number.isFinite(value)) {
|
||||
const tooltipValue = getToolTipValue(value, yAxisUnit);
|
||||
if (
|
||||
duplicatedLegendLabels[label] ||
|
||||
Object.prototype.hasOwnProperty.call(formattedData, label)
|
||||
) {
|
||||
duplicatedLegendLabels[label] = true;
|
||||
const tempDataObj = formattedData[label];
|
||||
|
||||
if (tempDataObj) {
|
||||
const newLabel = `${tempDataObj.queryName}: ${tempDataObj.label}`;
|
||||
|
||||
tempDataObj.textContent = `${newLabel} : ${tempDataObj.tooltipValue}`;
|
||||
|
||||
formattedData[newLabel] = tempDataObj;
|
||||
|
||||
delete formattedData[label];
|
||||
}
|
||||
|
||||
tooltipItemLabel = `${queryName}: ${label}`;
|
||||
}
|
||||
|
||||
const dataObj = {
|
||||
show: item.show || false,
|
||||
@ -69,11 +92,13 @@ const generateTooltipContent = (
|
||||
focus: item?._focus || false,
|
||||
value,
|
||||
tooltipValue,
|
||||
textContent: `${label} : ${tooltipValue}`,
|
||||
queryName,
|
||||
textContent: `${tooltipItemLabel} : ${tooltipValue}`,
|
||||
};
|
||||
|
||||
tooltipCount += 1;
|
||||
formattedData[label] = dataObj;
|
||||
|
||||
formattedData[tooltipItemLabel] = dataObj;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -224,7 +224,6 @@ const appReducer = (
|
||||
}
|
||||
|
||||
case UPDATE_USER_FLAG: {
|
||||
console.log('herei n update user flag');
|
||||
return {
|
||||
...state,
|
||||
userFlags: { ...state.userFlags, ...action.payload.flags },
|
||||
|
@ -22,6 +22,7 @@ export interface AlertDef {
|
||||
disabled?: boolean;
|
||||
preferredChannels?: string[];
|
||||
broadcastToAll?: boolean;
|
||||
version?: string;
|
||||
}
|
||||
|
||||
export interface RuleCondition {
|
||||
|
@ -4,6 +4,7 @@ export type Props =
|
||||
| {
|
||||
title: Dashboard['data']['title'];
|
||||
uploadedGrafana: boolean;
|
||||
version?: string;
|
||||
}
|
||||
| { DashboardData: DashboardData; uploadedGrafana: boolean };
|
||||
|
||||
|
@ -62,6 +62,7 @@ export interface DashboardData {
|
||||
title: string;
|
||||
layout?: Layout[];
|
||||
variables: Record<string, IDashboardVariable>;
|
||||
version?: string;
|
||||
}
|
||||
|
||||
export interface IBaseWidget {
|
||||
|
@ -47,12 +47,20 @@ export type OrderByPayload = {
|
||||
order: string;
|
||||
};
|
||||
|
||||
export interface QueryFunctionProps {
|
||||
name: string;
|
||||
args: string[];
|
||||
}
|
||||
|
||||
// Type for query builder
|
||||
export type IBuilderQuery = {
|
||||
queryName: string;
|
||||
dataSource: DataSource;
|
||||
aggregateOperator: string;
|
||||
aggregateAttribute: BaseAutocompleteData;
|
||||
timeAggregation: string;
|
||||
spaceAggregation?: string;
|
||||
functions: QueryFunctionProps[];
|
||||
filters: TagFilter;
|
||||
groupBy: BaseAutocompleteData[];
|
||||
expression: string;
|
||||
|
@ -4,6 +4,7 @@ import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteRe
|
||||
import {
|
||||
IBuilderFormula,
|
||||
IBuilderQuery,
|
||||
QueryFunctionProps,
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
@ -13,6 +14,7 @@ type UseQueryOperationsParams = Pick<QueryProps, 'index' | 'query'> &
|
||||
Pick<QueryBuilderProps, 'filterConfigs'> & {
|
||||
formula?: IBuilderFormula;
|
||||
isListViewPanel?: boolean;
|
||||
entityVersion: string;
|
||||
};
|
||||
|
||||
export type HandleChangeQueryData = <
|
||||
@ -37,12 +39,15 @@ export type UseQueryOperations = (
|
||||
isTracePanelType: boolean;
|
||||
isMetricsDataSource: boolean;
|
||||
operators: SelectOption<string, string>[];
|
||||
spaceAggregationOptions: SelectOption<string, string>[];
|
||||
listOfAdditionalFilters: string[];
|
||||
handleChangeOperator: (value: string) => void;
|
||||
handleSpaceAggregationChange: (value: string) => void;
|
||||
handleChangeAggregatorAttribute: (value: BaseAutocompleteData) => void;
|
||||
handleChangeDataSource: (newSource: DataSource) => void;
|
||||
handleDeleteQuery: () => void;
|
||||
handleChangeQueryData: HandleChangeQueryData;
|
||||
handleChangeFormulaData: HandleChangeFormulaData;
|
||||
handleQueryFunctionsUpdates: (functions: QueryFunctionProps[]) => void;
|
||||
listOfAdditionalFormulaFilters: string[];
|
||||
};
|
||||
|
@ -92,6 +92,8 @@ export enum MetricAggregateOperator {
|
||||
HIST_QUANTILE_90 = 'hist_quantile_90',
|
||||
HIST_QUANTILE_95 = 'hist_quantile_95',
|
||||
HIST_QUANTILE_99 = 'hist_quantile_99',
|
||||
INCREASE = 'increase',
|
||||
LATEST = 'latest',
|
||||
}
|
||||
|
||||
export enum TracesAggregatorOperator {
|
||||
@ -142,6 +144,24 @@ export enum LogsAggregatorOperator {
|
||||
RATE_MAX = 'rate_max',
|
||||
}
|
||||
|
||||
export enum QueryFunctionsTypes {
|
||||
CUTOFF_MIN = 'cutOffMin',
|
||||
CUTOFF_MAX = 'cutOffMax',
|
||||
CLAMP_MIN = 'clampMin',
|
||||
CLAMP_MAX = 'clampMax',
|
||||
ABSOLUTE = 'absolute',
|
||||
LOG_2 = 'log2',
|
||||
LOG_10 = 'log10',
|
||||
CUMULATIVE_SUM = 'cumSum',
|
||||
EWMA_3 = 'ewma3',
|
||||
EWMA_5 = 'ewma5',
|
||||
EWMA_7 = 'ewma7',
|
||||
MEDIAN_3 = 'median3',
|
||||
MEDIAN_5 = 'median5',
|
||||
MEDIAN_7 = 'median7',
|
||||
TIME_SHIFT = 'timeShift',
|
||||
}
|
||||
|
||||
export type PanelTypeKeys =
|
||||
| 'TIME_SERIES'
|
||||
| 'VALUE'
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Page } from '@playwright/test';
|
||||
|
||||
import { JsonApplicationType } from '../fixtures/constant';
|
||||
|
||||
// API endpoints
|
||||
@ -41,6 +42,99 @@ export const timeSeriesGraphName = 'Time1';
|
||||
|
||||
let widgetsId: string;
|
||||
|
||||
export const insertWidgetIdInResponse = (widgetID: string): any => ({
|
||||
status: 'success',
|
||||
data: {
|
||||
id: 219,
|
||||
uuid: 'd697fddb-a771-4bb4-aa38-810f000ed96a',
|
||||
created_at: '2023-11-17T20:44:03.167646604Z',
|
||||
created_by: 'vikrant@signoz.io',
|
||||
updated_at: '2023-11-17T20:51:23.058536475Z',
|
||||
updated_by: 'vikrant@signoz.io',
|
||||
data: {
|
||||
description: 'Playwright Dashboard T',
|
||||
layout: [
|
||||
{
|
||||
h: 3,
|
||||
i: '9fbcf0db-1572-4572-bf6b-0a84dd10ed85',
|
||||
w: 6,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
],
|
||||
version: 'v3',
|
||||
name: '',
|
||||
tags: [],
|
||||
title: 'Playwright Dashboard',
|
||||
variables: {},
|
||||
widgets: [
|
||||
{
|
||||
description: '',
|
||||
id: widgetID,
|
||||
isStacked: false,
|
||||
nullZeroValues: '',
|
||||
opacity: '',
|
||||
panelTypes: 'graph',
|
||||
query: {
|
||||
builder: {
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: '',
|
||||
id: '------',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: '',
|
||||
type: '',
|
||||
},
|
||||
aggregateOperator: 'count',
|
||||
dataSource: 'metrics',
|
||||
disabled: false,
|
||||
expression: 'A',
|
||||
filters: {
|
||||
items: [],
|
||||
op: 'AND',
|
||||
},
|
||||
groupBy: [],
|
||||
having: [],
|
||||
legend: '',
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
queryName: 'A',
|
||||
reduceTo: 'avg',
|
||||
stepInterval: 60,
|
||||
},
|
||||
],
|
||||
queryFormulas: [],
|
||||
},
|
||||
clickhouse_sql: [
|
||||
{
|
||||
disabled: false,
|
||||
legend: '',
|
||||
name: 'A',
|
||||
query: '',
|
||||
},
|
||||
],
|
||||
id: '6b4011e4-bcea-497d-81a9-0ee7816b679d',
|
||||
promql: [
|
||||
{
|
||||
disabled: false,
|
||||
legend: '',
|
||||
name: 'A',
|
||||
query: '',
|
||||
},
|
||||
],
|
||||
queryType: 'builder',
|
||||
},
|
||||
timePreferance: 'GLOBAL_TIME',
|
||||
title: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
isLocked: 0,
|
||||
},
|
||||
});
|
||||
|
||||
// mock API calls
|
||||
export const dashboardsListAndCreate = async (
|
||||
page: Page,
|
||||
@ -76,7 +170,8 @@ export const getTimeSeriesQueryData = async (
|
||||
page: Page,
|
||||
response: any,
|
||||
): Promise<void> => {
|
||||
await page.route(`**/${queryRangeApiEndpoint}`, (route) =>
|
||||
// eslint-disable-next-line sonarjs/no-identical-functions
|
||||
await page.route(`**/${queryRangeApiEndpoint}`, (route): any =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: JsonApplicationType,
|
||||
@ -84,97 +179,3 @@ export const getTimeSeriesQueryData = async (
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
export const insertWidgetIdInResponse = (widgetID: string) => {
|
||||
return {
|
||||
status: 'success',
|
||||
data: {
|
||||
id: 219,
|
||||
uuid: 'd697fddb-a771-4bb4-aa38-810f000ed96a',
|
||||
created_at: '2023-11-17T20:44:03.167646604Z',
|
||||
created_by: 'vikrant@signoz.io',
|
||||
updated_at: '2023-11-17T20:51:23.058536475Z',
|
||||
updated_by: 'vikrant@signoz.io',
|
||||
data: {
|
||||
description: 'Playwright Dashboard T',
|
||||
layout: [
|
||||
{
|
||||
h: 3,
|
||||
i: '9fbcf0db-1572-4572-bf6b-0a84dd10ed85',
|
||||
w: 6,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
],
|
||||
name: '',
|
||||
tags: [],
|
||||
title: 'Playwright Dashboard',
|
||||
variables: {},
|
||||
widgets: [
|
||||
{
|
||||
description: '',
|
||||
id: widgetID,
|
||||
isStacked: false,
|
||||
nullZeroValues: '',
|
||||
opacity: '',
|
||||
panelTypes: 'graph',
|
||||
query: {
|
||||
builder: {
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: '',
|
||||
id: '------',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: '',
|
||||
type: '',
|
||||
},
|
||||
aggregateOperator: 'count',
|
||||
dataSource: 'metrics',
|
||||
disabled: false,
|
||||
expression: 'A',
|
||||
filters: {
|
||||
items: [],
|
||||
op: 'AND',
|
||||
},
|
||||
groupBy: [],
|
||||
having: [],
|
||||
legend: '',
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
queryName: 'A',
|
||||
reduceTo: 'sum',
|
||||
stepInterval: 60,
|
||||
},
|
||||
],
|
||||
queryFormulas: [],
|
||||
},
|
||||
clickhouse_sql: [
|
||||
{
|
||||
disabled: false,
|
||||
legend: '',
|
||||
name: 'A',
|
||||
query: '',
|
||||
},
|
||||
],
|
||||
id: '6b4011e4-bcea-497d-81a9-0ee7816b679d',
|
||||
promql: [
|
||||
{
|
||||
disabled: false,
|
||||
legend: '',
|
||||
name: 'A',
|
||||
query: '',
|
||||
},
|
||||
],
|
||||
queryType: 'builder',
|
||||
},
|
||||
timePreferance: 'GLOBAL_TIME',
|
||||
title: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
isLocked: 0,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -56,7 +56,7 @@
|
||||
"limit": null,
|
||||
"orderBy": [],
|
||||
"queryName": "A",
|
||||
"reduceTo": "sum",
|
||||
"reduceTo": "avg",
|
||||
"stepInterval": 60
|
||||
}
|
||||
],
|
||||
|
@ -56,7 +56,7 @@
|
||||
"limit": null,
|
||||
"orderBy": [],
|
||||
"queryName": "A",
|
||||
"reduceTo": "sum",
|
||||
"reduceTo": "avg",
|
||||
"stepInterval": 60
|
||||
}
|
||||
],
|
||||
|
@ -40,7 +40,7 @@
|
||||
"order": "desc"
|
||||
}
|
||||
],
|
||||
"reduceTo": "sum"
|
||||
"reduceTo": "avg"
|
||||
}
|
||||
},
|
||||
"panelType": "table",
|
||||
|
2
go.mod
2
go.mod
@ -203,4 +203,4 @@ require (
|
||||
k8s.io/utils v0.0.0-20230711102312-30195339c3c7 // indirect
|
||||
)
|
||||
|
||||
replace github.com/prometheus/prometheus => github.com/SigNoz/prometheus v1.9.78
|
||||
replace github.com/prometheus/prometheus => github.com/SigNoz/prometheus v1.9.79
|
||||
|
6
go.sum
6
go.sum
@ -94,12 +94,10 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/SigNoz/govaluate v0.0.0-20220522085550-d19c08c206cb h1:bneLSKPf9YUSFmafKx32bynV6QrzViL/s+ZDvQxH1E4=
|
||||
github.com/SigNoz/govaluate v0.0.0-20220522085550-d19c08c206cb/go.mod h1:JznGDNg9x1cujDKa22RaQOimOvvEfy3nxzDGd8XDgmA=
|
||||
github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd h1:Bk43AsDYe0fhkbj57eGXx8H3ZJ4zhmQXBnrW523ktj8=
|
||||
github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd/go.mod h1:nxRcH/OEdM8QxzH37xkGzomr1O0JpYBRS6pwjsWW6Pc=
|
||||
github.com/SigNoz/prometheus v1.9.78 h1:bB3yuDrRzi/Mv00kWayR9DZbyjTuGfendSqISyDcXiY=
|
||||
github.com/SigNoz/prometheus v1.9.78/go.mod h1:MffmFu2qFILQrOHehx3D0XjYtaZMVfI+Ppeiv98x4Ww=
|
||||
github.com/SigNoz/prometheus v1.9.79 h1:RScpt9CUyOC4KQgzEUXRZ9lXHUdFT1eYAsFY3zlqPFM=
|
||||
github.com/SigNoz/prometheus v1.9.79/go.mod h1:MffmFu2qFILQrOHehx3D0XjYtaZMVfI+Ppeiv98x4Ww=
|
||||
github.com/SigNoz/signoz-otel-collector v0.88.12 h1:UwkVi1o2NY9gRgCLBtWVKr+UDxb4FaTs63Sb20qgf8w=
|
||||
github.com/SigNoz/signoz-otel-collector v0.88.12/go.mod h1:RH9OEjni6tkh9RgN/meSPxv3kykjcFscqMwJgbUAXmo=
|
||||
github.com/SigNoz/zap_otlp v0.1.0 h1:T7rRcFN87GavY8lDGZj0Z3Xv6OhJA6Pj3I9dNPmqvRc=
|
||||
|
@ -47,6 +47,7 @@ import (
|
||||
"go.signoz.io/signoz/pkg/query-service/app/logs"
|
||||
"go.signoz.io/signoz/pkg/query-service/app/services"
|
||||
"go.signoz.io/signoz/pkg/query-service/auth"
|
||||
"go.signoz.io/signoz/pkg/query-service/common"
|
||||
"go.signoz.io/signoz/pkg/query-service/constants"
|
||||
am "go.signoz.io/signoz/pkg/query-service/integrations/alertManager"
|
||||
"go.signoz.io/signoz/pkg/query-service/interfaces"
|
||||
@ -3974,7 +3975,7 @@ func (r *ClickHouseReader) GetMetricAggregateAttributes(ctx context.Context, req
|
||||
var rows driver.Rows
|
||||
var response v3.AggregateAttributeResponse
|
||||
|
||||
query = fmt.Sprintf("SELECT DISTINCT metric_name, type from %s.%s WHERE metric_name ILIKE $1", signozMetricDBName, signozTSTableNameV41Day)
|
||||
query = fmt.Sprintf("SELECT metric_name, type, is_monotonic, temporality FROM %s.%s WHERE metric_name ILIKE $1 GROUP BY metric_name, type, is_monotonic, temporality", signozMetricDBName, signozTSTableNameV41Day)
|
||||
if req.Limit != 0 {
|
||||
query = query + fmt.Sprintf(" LIMIT %d;", req.Limit)
|
||||
}
|
||||
@ -3986,11 +3987,18 @@ func (r *ClickHouseReader) GetMetricAggregateAttributes(ctx context.Context, req
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var metricName, typ string
|
||||
seen := make(map[string]struct{})
|
||||
|
||||
var metricName, typ, temporality string
|
||||
var isMonotonic bool
|
||||
for rows.Next() {
|
||||
if err := rows.Scan(&metricName, &typ); err != nil {
|
||||
if err := rows.Scan(&metricName, &typ, &isMonotonic, &temporality); err != nil {
|
||||
return nil, fmt.Errorf("error while scanning rows: %s", err.Error())
|
||||
}
|
||||
// Non-monotonic cumulative sums are treated as gauges
|
||||
if typ == "Sum" && !isMonotonic && temporality == string(v3.Cumulative) {
|
||||
typ = "Gauge"
|
||||
}
|
||||
// unlike traces/logs `tag`/`resource` type, the `Type` will be metric type
|
||||
key := v3.AttributeKey{
|
||||
Key: metricName,
|
||||
@ -3998,6 +4006,11 @@ func (r *ClickHouseReader) GetMetricAggregateAttributes(ctx context.Context, req
|
||||
Type: v3.AttributeKeyType(typ),
|
||||
IsColumn: true,
|
||||
}
|
||||
// remove duplicates
|
||||
if _, ok := seen[metricName+typ]; ok {
|
||||
continue
|
||||
}
|
||||
seen[metricName+typ] = struct{}{}
|
||||
response.AttributeKeys = append(response.AttributeKeys, key)
|
||||
}
|
||||
|
||||
@ -4012,11 +4025,11 @@ func (r *ClickHouseReader) GetMetricAttributeKeys(ctx context.Context, req *v3.F
|
||||
var response v3.FilterAttributeKeyResponse
|
||||
|
||||
// skips the internal attributes i.e attributes starting with __
|
||||
query = fmt.Sprintf("SELECT DISTINCT arrayJoin(tagKeys) as distinctTagKey from (SELECT DISTINCT(JSONExtractKeys(labels)) tagKeys from %s.%s WHERE metric_name=$1) WHERE distinctTagKey ILIKE $2 AND distinctTagKey NOT LIKE '\\_\\_%%'", signozMetricDBName, signozTSTableName)
|
||||
query = fmt.Sprintf("SELECT arrayJoin(tagKeys) AS distinctTagKey FROM (SELECT JSONExtractKeys(labels) AS tagKeys FROM %s.%s WHERE metric_name=$1 AND unix_milli >= $2 GROUP BY tagKeys) WHERE distinctTagKey ILIKE $3 AND distinctTagKey NOT LIKE '\\_\\_%%' GROUP BY distinctTagKey", signozMetricDBName, signozTSTableNameV41Day)
|
||||
if req.Limit != 0 {
|
||||
query = query + fmt.Sprintf(" LIMIT %d;", req.Limit)
|
||||
}
|
||||
rows, err = r.db.Query(ctx, query, req.AggregateAttribute, fmt.Sprintf("%%%s%%", req.SearchText))
|
||||
rows, err = r.db.Query(ctx, query, req.AggregateAttribute, common.PastDayRoundOff(), fmt.Sprintf("%%%s%%", req.SearchText))
|
||||
if err != nil {
|
||||
zap.S().Error(err)
|
||||
return nil, fmt.Errorf("error while executing query: %s", err.Error())
|
||||
@ -4047,11 +4060,11 @@ func (r *ClickHouseReader) GetMetricAttributeValues(ctx context.Context, req *v3
|
||||
var rows driver.Rows
|
||||
var attributeValues v3.FilterAttributeValueResponse
|
||||
|
||||
query = fmt.Sprintf("SELECT DISTINCT(JSONExtractString(labels, $1)) from %s.%s WHERE metric_name=$2 AND JSONExtractString(labels, $3) ILIKE $4", signozMetricDBName, signozTSTableName)
|
||||
query = fmt.Sprintf("SELECT JSONExtractString(labels, $1) AS tagValue FROM %s.%s WHERE metric_name=$2 AND JSONExtractString(labels, $3) ILIKE $4 AND unix_milli >= $5 GROUP BY tagValue", signozMetricDBName, signozTSTableNameV41Day)
|
||||
if req.Limit != 0 {
|
||||
query = query + fmt.Sprintf(" LIMIT %d;", req.Limit)
|
||||
}
|
||||
rows, err = r.db.Query(ctx, query, req.FilterAttributeKey, req.AggregateAttribute, req.FilterAttributeKey, fmt.Sprintf("%%%s%%", req.SearchText))
|
||||
rows, err = r.db.Query(ctx, query, req.FilterAttributeKey, req.AggregateAttribute, req.FilterAttributeKey, fmt.Sprintf("%%%s%%", req.SearchText), common.PastDayRoundOff())
|
||||
|
||||
if err != nil {
|
||||
zap.S().Error(err)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user