mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-17 13:21:32 +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_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.",
|
"dashboard_save_changes": "Your graph built with {{queryTag}} query will be saved. Press OK to confirm.",
|
||||||
"your_graph_build_with": "Your graph built with",
|
"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_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.",
|
"dashboard_save_changes": "Your graph built with {{queryTag}} query will be saved. Press OK to confirm.",
|
||||||
"your_graph_build_with": "Your graph built with",
|
"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 apiV2 = '/api/v2/';
|
||||||
export const apiV3 = '/api/v3/';
|
export const apiV3 = '/api/v3/';
|
||||||
|
export const apiV4 = '/api/v4/';
|
||||||
export const apiAlertManager = '/api/alertmanager';
|
export const apiAlertManager = '/api/alertmanager';
|
||||||
|
|
||||||
export default apiV1;
|
export default apiV1;
|
||||||
|
@ -9,7 +9,7 @@ import { ENVIRONMENT } from 'constants/env';
|
|||||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
|
|
||||||
import apiV1, { apiAlertManager, apiV2, apiV3 } from './apiV1';
|
import apiV1, { apiAlertManager, apiV2, apiV3, apiV4 } from './apiV1';
|
||||||
import { Logout } from './utils';
|
import { Logout } from './utils';
|
||||||
|
|
||||||
const interceptorsResponse = (
|
const interceptorsResponse = (
|
||||||
@ -114,6 +114,7 @@ ApiV2Instance.interceptors.request.use(interceptorsRequestResponse);
|
|||||||
export const ApiV3Instance = axios.create({
|
export const ApiV3Instance = axios.create({
|
||||||
baseURL: `${ENVIRONMENT.baseURL}${apiV3}`,
|
baseURL: `${ENVIRONMENT.baseURL}${apiV3}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
ApiV3Instance.interceptors.response.use(
|
ApiV3Instance.interceptors.response.use(
|
||||||
interceptorsResponse,
|
interceptorsResponse,
|
||||||
interceptorRejected,
|
interceptorRejected,
|
||||||
@ -121,6 +122,18 @@ ApiV3Instance.interceptors.response.use(
|
|||||||
ApiV3Instance.interceptors.request.use(interceptorsRequestResponse);
|
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(
|
AxiosAlertManagerInstance.interceptors.response.use(
|
||||||
interceptorsResponse,
|
interceptorsResponse,
|
||||||
interceptorRejected,
|
interceptorRejected,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ApiV3Instance as axios } from 'api';
|
import { ApiV3Instance, ApiV4Instance } from 'api';
|
||||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
@ -9,10 +9,23 @@ import {
|
|||||||
|
|
||||||
export const getMetricsQueryRange = async (
|
export const getMetricsQueryRange = async (
|
||||||
props: QueryRangePayload,
|
props: QueryRangePayload,
|
||||||
|
version: string,
|
||||||
signal: AbortSignal,
|
signal: AbortSignal,
|
||||||
): Promise<SuccessResponse<MetricRangePayloadV3> | ErrorResponse> => {
|
): Promise<SuccessResponse<MetricRangePayloadV3> | ErrorResponse> => {
|
||||||
try {
|
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 {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
|
@ -72,8 +72,6 @@ export default function LogsFormatOptionsMenu({
|
|||||||
setAddNewColumn(!addNewColumn);
|
setAddNewColumn(!addNewColumn);
|
||||||
};
|
};
|
||||||
|
|
||||||
// console.log('optionsMenuConfig', config);
|
|
||||||
|
|
||||||
const handleLinesPerRowChange = (maxLinesPerRow: number | null): void => {
|
const handleLinesPerRowChange = (maxLinesPerRow: number | null): void => {
|
||||||
if (
|
if (
|
||||||
maxLinesPerRow &&
|
maxLinesPerRow &&
|
||||||
@ -221,8 +219,6 @@ export default function LogsFormatOptionsMenu({
|
|||||||
className="column-name"
|
className="column-name"
|
||||||
key={value}
|
key={value}
|
||||||
onClick={(eve): void => {
|
onClick={(eve): void => {
|
||||||
console.log('coluimn name', label, value);
|
|
||||||
|
|
||||||
eve.stopPropagation();
|
eve.stopPropagation();
|
||||||
|
|
||||||
if (addColumn && addColumn?.onSelect) {
|
if (addColumn && addColumn?.onSelect) {
|
||||||
|
@ -13,3 +13,5 @@ export const SIGNOZ_UPGRADE_PLAN_URL =
|
|||||||
'https://upgrade.signoz.io/upgrade-from-app';
|
'https://upgrade.signoz.io/upgrade-from-app';
|
||||||
|
|
||||||
export const DASHBOARD_TIME_IN_DURATION = 'refreshInterval';
|
export const DASHBOARD_TIME_IN_DURATION = 'refreshInterval';
|
||||||
|
|
||||||
|
export const DEFAULT_ENTITY_VERSION = 'v3';
|
||||||
|
@ -36,6 +36,11 @@ import { v4 as uuid } from 'uuid';
|
|||||||
import {
|
import {
|
||||||
logsAggregateOperatorOptions,
|
logsAggregateOperatorOptions,
|
||||||
metricAggregateOperatorOptions,
|
metricAggregateOperatorOptions,
|
||||||
|
metricsGaugeAggregateOperatorOptions,
|
||||||
|
metricsGaugeSpaceAggregateOperatorOptions,
|
||||||
|
metricsHistogramSpaceAggregateOperatorOptions,
|
||||||
|
metricsSumAggregateOperatorOptions,
|
||||||
|
metricsSumSpaceAggregateOperatorOptions,
|
||||||
tracesAggregateOperatorOptions,
|
tracesAggregateOperatorOptions,
|
||||||
} from './queryBuilderOperators';
|
} from './queryBuilderOperators';
|
||||||
|
|
||||||
@ -74,6 +79,18 @@ export const mapOfOperators = {
|
|||||||
traces: tracesAggregateOperatorOptions,
|
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[]> = {
|
export const mapOfQueryFilters: Record<DataSource, QueryAdditionalFilter[]> = {
|
||||||
metrics: [
|
metrics: [
|
||||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||||
@ -148,6 +165,9 @@ export const initialQueryBuilderFormValues: IBuilderQuery = {
|
|||||||
queryName: createNewBuilderItemName({ existNames: [], sourceNames: alphabet }),
|
queryName: createNewBuilderItemName({ existNames: [], sourceNames: alphabet }),
|
||||||
aggregateOperator: MetricAggregateOperator.COUNT,
|
aggregateOperator: MetricAggregateOperator.COUNT,
|
||||||
aggregateAttribute: initialAutocompleteData,
|
aggregateAttribute: initialAutocompleteData,
|
||||||
|
timeAggregation: MetricAggregateOperator.RATE,
|
||||||
|
spaceAggregation: MetricAggregateOperator.SUM,
|
||||||
|
functions: [],
|
||||||
filters: { items: [], op: 'AND' },
|
filters: { items: [], op: 'AND' },
|
||||||
expression: createNewBuilderItemName({
|
expression: createNewBuilderItemName({
|
||||||
existNames: [],
|
existNames: [],
|
||||||
@ -160,7 +180,7 @@ export const initialQueryBuilderFormValues: IBuilderQuery = {
|
|||||||
orderBy: [],
|
orderBy: [],
|
||||||
groupBy: [],
|
groupBy: [],
|
||||||
legend: '',
|
legend: '',
|
||||||
reduceTo: 'sum',
|
reduceTo: 'avg',
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialQueryBuilderFormLogsValues: IBuilderQuery = {
|
const initialQueryBuilderFormLogsValues: IBuilderQuery = {
|
||||||
@ -268,6 +288,14 @@ export enum PANEL_TYPES {
|
|||||||
EMPTY_WIDGET = 'EMPTY_WIDGET',
|
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 type IQueryBuilderState = 'search';
|
||||||
|
|
||||||
export const QUERY_BUILDER_SEARCH_VALUES = {
|
export const QUERY_BUILDER_SEARCH_VALUES = {
|
||||||
|
@ -302,3 +302,126 @@ export const logsAggregateOperatorOptions: SelectOption<string, string>[] = [
|
|||||||
label: 'Rate_max',
|
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 = {
|
export const alertDefaults: AlertDef = {
|
||||||
alertType: AlertTypes.METRICS_BASED_ALERT,
|
alertType: AlertTypes.METRICS_BASED_ALERT,
|
||||||
|
version: 'v4',
|
||||||
condition: {
|
condition: {
|
||||||
compositeQuery: {
|
compositeQuery: {
|
||||||
builderQueries: {
|
builderQueries: {
|
||||||
|
@ -2,6 +2,7 @@ import { Form, Row } from 'antd';
|
|||||||
import FormAlertRules from 'container/FormAlertRules';
|
import FormAlertRules from 'container/FormAlertRules';
|
||||||
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
|
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||||
import { AlertDef } from 'types/api/alerts/def';
|
import { AlertDef } from 'types/api/alerts/def';
|
||||||
|
|
||||||
@ -20,6 +21,10 @@ function CreateRules(): JSX.Element {
|
|||||||
AlertTypes.METRICS_BASED_ALERT,
|
AlertTypes.METRICS_BASED_ALERT,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const location = useLocation();
|
||||||
|
const queryParams = new URLSearchParams(location.search);
|
||||||
|
const version = queryParams.get('version');
|
||||||
|
|
||||||
const compositeQuery = useGetCompositeQueryParam();
|
const compositeQuery = useGetCompositeQueryParam();
|
||||||
|
|
||||||
const [formInstance] = Form.useForm();
|
const [formInstance] = Form.useForm();
|
||||||
@ -37,7 +42,10 @@ function CreateRules(): JSX.Element {
|
|||||||
setInitValues(exceptionAlertDefaults);
|
setInitValues(exceptionAlertDefaults);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
setInitValues(alertDefaults);
|
setInitValues({
|
||||||
|
...alertDefaults,
|
||||||
|
version: version || 'v3',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -52,6 +60,7 @@ function CreateRules(): JSX.Element {
|
|||||||
if (alertType) {
|
if (alertType) {
|
||||||
onSelectType(alertType);
|
onSelectType(alertType);
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [compositeQuery]);
|
}, [compositeQuery]);
|
||||||
|
|
||||||
if (!initValues) {
|
if (!initValues) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import GridPanelSwitch from 'container/GridPanelSwitch';
|
import GridPanelSwitch from 'container/GridPanelSwitch';
|
||||||
import { getFormatNameByOptionId } from 'container/NewWidget/RightContainer/alertFomatCategories';
|
import { getFormatNameByOptionId } from 'container/NewWidget/RightContainer/alertFomatCategories';
|
||||||
@ -39,6 +40,7 @@ export interface ChartPreviewProps {
|
|||||||
yAxisUnit: string;
|
yAxisUnit: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
function ChartPreview({
|
function ChartPreview({
|
||||||
name,
|
name,
|
||||||
query,
|
query,
|
||||||
@ -94,6 +96,7 @@ function ChartPreview({
|
|||||||
allowSelectedIntervalForStepGen,
|
allowSelectedIntervalForStepGen,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
alertDef?.version || DEFAULT_ENTITY_VERSION,
|
||||||
{
|
{
|
||||||
queryKey: [
|
queryKey: [
|
||||||
'chartPreview',
|
'chartPreview',
|
||||||
|
@ -3,13 +3,16 @@ import './QuerySection.styles.scss';
|
|||||||
import { Button, Tabs, Tooltip } from 'antd';
|
import { Button, Tabs, Tooltip } from 'antd';
|
||||||
import { ALERTS_DATA_SOURCE_MAP } from 'constants/alerts';
|
import { ALERTS_DATA_SOURCE_MAP } from 'constants/alerts';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
|
import { QBShortcuts } from 'constants/shortcuts/QBShortcuts';
|
||||||
import { QueryBuilder } from 'container/QueryBuilder';
|
import { QueryBuilder } from 'container/QueryBuilder';
|
||||||
|
import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys';
|
||||||
import { Atom, Play, Terminal } from 'lucide-react';
|
import { Atom, Play, Terminal } from 'lucide-react';
|
||||||
import { useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||||
|
import { AlertDef } from 'types/api/alerts/def';
|
||||||
import { EQueryType } from 'types/common/dashboard';
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
@ -22,6 +25,7 @@ function QuerySection({
|
|||||||
setQueryCategory,
|
setQueryCategory,
|
||||||
alertType,
|
alertType,
|
||||||
runQuery,
|
runQuery,
|
||||||
|
alertDef,
|
||||||
panelType,
|
panelType,
|
||||||
}: QuerySectionProps): JSX.Element {
|
}: QuerySectionProps): JSX.Element {
|
||||||
// init namespace for translations
|
// init namespace for translations
|
||||||
@ -50,6 +54,10 @@ function QuerySection({
|
|||||||
queryVariant: 'static',
|
queryVariant: 'static',
|
||||||
initialDataSource: ALERTS_DATA_SOURCE_MAP[alertType],
|
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 => {
|
const renderTabs = (typ: AlertTypes): JSX.Element | null => {
|
||||||
switch (typ) {
|
switch (typ) {
|
||||||
case AlertTypes.TRACES_BASED_ALERT:
|
case AlertTypes.TRACES_BASED_ALERT:
|
||||||
@ -197,6 +216,7 @@ interface QuerySectionProps {
|
|||||||
setQueryCategory: (n: EQueryType) => void;
|
setQueryCategory: (n: EQueryType) => void;
|
||||||
alertType: AlertTypes;
|
alertType: AlertTypes;
|
||||||
runQuery: VoidFunction;
|
runQuery: VoidFunction;
|
||||||
|
alertDef: AlertDef;
|
||||||
panelType: PANEL_TYPES;
|
panelType: PANEL_TYPES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +304,7 @@ function FormAlertRules({
|
|||||||
panelType,
|
panelType,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const isAlertAvialable = useIsFeatureDisabled(
|
const isAlertAvailable = useIsFeatureDisabled(
|
||||||
FeatureKeys.QUERY_BUILDER_ALERTS,
|
FeatureKeys.QUERY_BUILDER_ALERTS,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -458,8 +458,8 @@ function FormAlertRules({
|
|||||||
|
|
||||||
const isAlertNameMissing = !formInstance.getFieldValue('alert');
|
const isAlertNameMissing = !formInstance.getFieldValue('alert');
|
||||||
|
|
||||||
const isAlertAvialableToSave =
|
const isAlertAvailableToSave =
|
||||||
isAlertAvialable &&
|
isAlertAvailable &&
|
||||||
currentQuery.queryType === EQueryType.QUERY_BUILDER &&
|
currentQuery.queryType === EQueryType.QUERY_BUILDER &&
|
||||||
alertType !== AlertTypes.METRICS_BASED_ALERT;
|
alertType !== AlertTypes.METRICS_BASED_ALERT;
|
||||||
|
|
||||||
@ -509,6 +509,7 @@ function FormAlertRules({
|
|||||||
setQueryCategory={onQueryCategoryChange}
|
setQueryCategory={onQueryCategoryChange}
|
||||||
alertType={alertType || AlertTypes.METRICS_BASED_ALERT}
|
alertType={alertType || AlertTypes.METRICS_BASED_ALERT}
|
||||||
runQuery={handleRunQuery}
|
runQuery={handleRunQuery}
|
||||||
|
alertDef={alertDef}
|
||||||
panelType={panelType || PANEL_TYPES.TIME_SERIES}
|
panelType={panelType || PANEL_TYPES.TIME_SERIES}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -521,7 +522,7 @@ function FormAlertRules({
|
|||||||
|
|
||||||
{renderBasicInfo()}
|
{renderBasicInfo()}
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
<Tooltip title={isAlertAvialableToSave ? MESSAGE.ALERT : ''}>
|
<Tooltip title={isAlertAvailableToSave ? MESSAGE.ALERT : ''}>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
loading={loading || false}
|
loading={loading || false}
|
||||||
type="primary"
|
type="primary"
|
||||||
@ -529,7 +530,7 @@ function FormAlertRules({
|
|||||||
icon={<SaveOutlined />}
|
icon={<SaveOutlined />}
|
||||||
disabled={
|
disabled={
|
||||||
isAlertNameMissing ||
|
isAlertNameMissing ||
|
||||||
isAlertAvialableToSave ||
|
isAlertAvailableToSave ||
|
||||||
!isChannelConfigurationValid
|
!isChannelConfigurationValid
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -6,6 +6,7 @@ import cx from 'classnames';
|
|||||||
import { ToggleGraphProps } from 'components/Graph/types';
|
import { ToggleGraphProps } from 'components/Graph/types';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import TimePreference from 'components/TimePreferenceDropDown';
|
import TimePreference from 'components/TimePreferenceDropDown';
|
||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import GridPanelSwitch from 'container/GridPanelSwitch';
|
import GridPanelSwitch from 'container/GridPanelSwitch';
|
||||||
import {
|
import {
|
||||||
@ -96,6 +97,7 @@ function FullView({
|
|||||||
globalSelectedInterval: globalSelectedTime,
|
globalSelectedInterval: globalSelectedTime,
|
||||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||||
},
|
},
|
||||||
|
selectedDashboard?.data?.version || DEFAULT_ENTITY_VERSION,
|
||||||
{
|
{
|
||||||
queryKey: `FullViewGetMetricsQueryRange-${selectedTime.enum}-${globalSelectedTime}-${widget.id}`,
|
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
|
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 { QueryParams } from 'constants/query';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||||
@ -39,6 +40,7 @@ function GridCardGraph({
|
|||||||
threshold,
|
threshold,
|
||||||
variables,
|
variables,
|
||||||
fillSpans = false,
|
fillSpans = false,
|
||||||
|
version,
|
||||||
}: GridCardGraphProps): JSX.Element {
|
}: GridCardGraphProps): JSX.Element {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [errorMessage, setErrorMessage] = useState<string>();
|
const [errorMessage, setErrorMessage] = useState<string>();
|
||||||
@ -132,6 +134,7 @@ function GridCardGraph({
|
|||||||
globalSelectedInterval,
|
globalSelectedInterval,
|
||||||
variables: getDashboardVariables(variables),
|
variables: getDashboardVariables(variables),
|
||||||
},
|
},
|
||||||
|
version || DEFAULT_ENTITY_VERSION,
|
||||||
{
|
{
|
||||||
queryKey: [
|
queryKey: [
|
||||||
maxTime,
|
maxTime,
|
||||||
@ -253,6 +256,7 @@ GridCardGraph.defaultProps = {
|
|||||||
isQueryEnabled: true,
|
isQueryEnabled: true,
|
||||||
threshold: undefined,
|
threshold: undefined,
|
||||||
headerMenuList: [MenuItemKeys.View],
|
headerMenuList: [MenuItemKeys.View],
|
||||||
|
version: 'v3',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(GridCardGraph);
|
export default memo(GridCardGraph);
|
||||||
|
@ -43,6 +43,7 @@ export interface GridCardGraphProps {
|
|||||||
isQueryEnabled: boolean;
|
isQueryEnabled: boolean;
|
||||||
variables?: Dashboard['data']['variables'];
|
variables?: Dashboard['data']['variables'];
|
||||||
fillSpans?: boolean;
|
fillSpans?: boolean;
|
||||||
|
version?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GetGraphVisibilityStateOnLegendClickProps {
|
export interface GetGraphVisibilityStateOnLegendClickProps {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.fullscreen-grid-container {
|
.fullscreen-grid-container {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
margin-top: 1rem;
|
margin: 8px -8px;
|
||||||
|
|
||||||
.react-grid-layout {
|
.react-grid-layout {
|
||||||
border: none !important;
|
border: none !important;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import './GridCardLayout.styles.scss';
|
import './GridCardLayout.styles.scss';
|
||||||
|
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
|
import { Tooltip } from 'antd';
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { themeColors } from 'constants/theme';
|
import { themeColors } from 'constants/theme';
|
||||||
@ -144,17 +145,19 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
<Button
|
<Tooltip title="Open in Full Screen">
|
||||||
loading={updateDashboardMutation.isLoading}
|
<Button
|
||||||
onClick={handle.enter}
|
className="periscope-btn"
|
||||||
icon={<FullscreenIcon size={16} />}
|
loading={updateDashboardMutation.isLoading}
|
||||||
disabled={updateDashboardMutation.isLoading}
|
onClick={handle.enter}
|
||||||
>
|
icon={<FullscreenIcon size={16} />}
|
||||||
{t('dashboard:full_view')}
|
disabled={updateDashboardMutation.isLoading}
|
||||||
</Button>
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
{!isDashboardLocked && addPanelPermission && (
|
{!isDashboardLocked && addPanelPermission && (
|
||||||
<Button
|
<Button
|
||||||
|
className="periscope-btn"
|
||||||
onClick={onAddPanelHandler}
|
onClick={onAddPanelHandler}
|
||||||
icon={<PlusOutlined />}
|
icon={<PlusOutlined />}
|
||||||
data-testid="add-panel"
|
data-testid="add-panel"
|
||||||
@ -201,6 +204,7 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
|
|||||||
headerMenuList={widgetActions}
|
headerMenuList={widgetActions}
|
||||||
variables={variables}
|
variables={variables}
|
||||||
fillSpans={currentWidget?.fillSpans}
|
fillSpans={currentWidget?.fillSpans}
|
||||||
|
version={selectedDashboard?.data?.version}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</CardContainer>
|
</CardContainer>
|
||||||
|
@ -80,7 +80,6 @@ export const ReactGridLayout = styled(ReactGridLayoutComponent)`
|
|||||||
export const ButtonContainer = styled(Space)`
|
export const ButtonContainer = styled(Space)`
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
margin-top: 1rem;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Button = styled(ButtonComponent)`
|
export const Button = styled(ButtonComponent)`
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { ToggleGraphProps } from 'components/Graph/types';
|
import { ToggleGraphProps } from 'components/Graph/types';
|
||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { getComponentForPanelType } from 'constants/panelTypes';
|
import { getComponentForPanelType } from 'constants/panelTypes';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { GRID_TABLE_CONFIG } from 'container/GridTableComponent/config';
|
import { GRID_TABLE_CONFIG } from 'container/GridTableComponent/config';
|
||||||
@ -50,11 +51,13 @@ const GridPanelSwitch = forwardRef<
|
|||||||
? {
|
? {
|
||||||
selectedLogsFields: selectedLogFields || [],
|
selectedLogsFields: selectedLogFields || [],
|
||||||
query,
|
query,
|
||||||
|
version: DEFAULT_ENTITY_VERSION, // As we don't support for Metrics, defaulting to v3
|
||||||
selectedTime,
|
selectedTime,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
selectedTracesFields: selectedTracesFields || [],
|
selectedTracesFields: selectedTracesFields || [],
|
||||||
query,
|
query,
|
||||||
|
version: DEFAULT_ENTITY_VERSION, // As we don't support for Metrics, defaulting to v3
|
||||||
selectedTime,
|
selectedTime,
|
||||||
},
|
},
|
||||||
[PANEL_TYPES.TRACE]: null,
|
[PANEL_TYPES.TRACE]: null,
|
||||||
|
@ -109,7 +109,6 @@ function DashboardsList(): JSX.Element {
|
|||||||
width: 30,
|
width: 30,
|
||||||
key: DynamicColumnsKey.CreatedAt,
|
key: DynamicColumnsKey.CreatedAt,
|
||||||
sorter: (a: Data, b: Data): number => {
|
sorter: (a: Data, b: Data): number => {
|
||||||
console.log({ a });
|
|
||||||
const prev = new Date(a.createdAt).getTime();
|
const prev = new Date(a.createdAt).getTime();
|
||||||
const next = new Date(b.createdAt).getTime();
|
const next = new Date(b.createdAt).getTime();
|
||||||
|
|
||||||
@ -211,6 +210,7 @@ function DashboardsList(): JSX.Element {
|
|||||||
ns: 'dashboard',
|
ns: 'dashboard',
|
||||||
}),
|
}),
|
||||||
uploadedGrafana: false,
|
uploadedGrafana: false,
|
||||||
|
version: 'v4',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.statusCode === 200) {
|
if (response.statusCode === 200) {
|
||||||
@ -304,6 +304,7 @@ function DashboardsList(): JSX.Element {
|
|||||||
loading={isFilteringDashboards}
|
loading={isFilteringDashboards}
|
||||||
style={{ marginBottom: 16, marginTop: 16 }}
|
style={{ marginBottom: 16, marginTop: 16 }}
|
||||||
defaultValue={searchString}
|
defaultValue={searchString}
|
||||||
|
autoFocus
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { LIVE_TAIL_GRAPH_INTERVAL } from 'constants/liveTail';
|
import { LIVE_TAIL_GRAPH_INTERVAL } from 'constants/liveTail';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import LogsExplorerChart from 'container/LogsExplorerChart';
|
import LogsExplorerChart from 'container/LogsExplorerChart';
|
||||||
@ -41,6 +42,7 @@ function LiveLogsListChart({
|
|||||||
const { data, isFetching } = useGetExplorerQueryRange(
|
const { data, isFetching } = useGetExplorerQueryRange(
|
||||||
listChartQuery,
|
listChartQuery,
|
||||||
PANEL_TYPES.TIME_SERIES,
|
PANEL_TYPES.TIME_SERIES,
|
||||||
|
DEFAULT_ENTITY_VERSION,
|
||||||
{
|
{
|
||||||
enabled: isConnectionOpen,
|
enabled: isConnectionOpen,
|
||||||
refetchInterval: LIVE_TAIL_GRAPH_INTERVAL,
|
refetchInterval: LIVE_TAIL_GRAPH_INTERVAL,
|
||||||
|
@ -62,6 +62,7 @@ function LogExplorerQuerySection({
|
|||||||
index: 0,
|
index: 0,
|
||||||
query,
|
query,
|
||||||
filterConfigs,
|
filterConfigs,
|
||||||
|
entityVersion: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const renderOrderBy = useCallback(
|
const renderOrderBy = useCallback(
|
||||||
@ -103,6 +104,7 @@ function LogExplorerQuerySection({
|
|||||||
config={{ initialDataSource: DataSource.LOGS, queryVariant: 'static' }}
|
config={{ initialDataSource: DataSource.LOGS, queryVariant: 'static' }}
|
||||||
filterConfigs={filterConfigs}
|
filterConfigs={filterConfigs}
|
||||||
queryComponents={queryComponents}
|
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 RawLogView from 'components/Logs/RawLogView';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
|
import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
|
||||||
import { useGetExplorerQueryRange } from 'hooks/queryBuilder/useGetExplorerQueryRange';
|
import { useGetExplorerQueryRange } from 'hooks/queryBuilder/useGetExplorerQueryRange';
|
||||||
@ -105,6 +106,7 @@ function LogsContextList({
|
|||||||
const { isError, isFetching } = useGetExplorerQueryRange(
|
const { isError, isFetching } = useGetExplorerQueryRange(
|
||||||
requestData,
|
requestData,
|
||||||
PANEL_TYPES.LIST,
|
PANEL_TYPES.LIST,
|
||||||
|
DEFAULT_ENTITY_VERSION,
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
keepPreviousData: true,
|
||||||
enabled: !!requestData,
|
enabled: !!requestData,
|
||||||
|
@ -3,6 +3,7 @@ import './LogsExplorerViews.styles.scss';
|
|||||||
|
|
||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
import LogsFormatOptionsMenu from 'components/LogsFormatOptionsMenu/LogsFormatOptionsMenu';
|
import LogsFormatOptionsMenu from 'components/LogsFormatOptionsMenu/LogsFormatOptionsMenu';
|
||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
import { AVAILABLE_EXPORT_PANEL_TYPES } from 'constants/panelTypes';
|
import { AVAILABLE_EXPORT_PANEL_TYPES } from 'constants/panelTypes';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
@ -189,13 +190,19 @@ function LogsExplorerViews({
|
|||||||
data: listChartData,
|
data: listChartData,
|
||||||
isFetching: isFetchingListChartData,
|
isFetching: isFetchingListChartData,
|
||||||
isLoading: isLoadingListChartData,
|
isLoading: isLoadingListChartData,
|
||||||
} = useGetExplorerQueryRange(listChartQuery, PANEL_TYPES.TIME_SERIES, {
|
} = useGetExplorerQueryRange(
|
||||||
enabled: !!listChartQuery && panelType === PANEL_TYPES.LIST,
|
listChartQuery,
|
||||||
});
|
PANEL_TYPES.TIME_SERIES,
|
||||||
|
DEFAULT_ENTITY_VERSION,
|
||||||
|
{
|
||||||
|
enabled: !!listChartQuery && panelType === PANEL_TYPES.LIST,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const { data, isLoading, isFetching, isError } = useGetExplorerQueryRange(
|
const { data, isLoading, isFetching, isError } = useGetExplorerQueryRange(
|
||||||
requestData,
|
requestData,
|
||||||
panelType,
|
panelType,
|
||||||
|
DEFAULT_ENTITY_VERSION,
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
keepPreviousData: true,
|
||||||
enabled: !isLimit && !!requestData,
|
enabled: !isLimit && !!requestData,
|
||||||
|
@ -1,80 +1,88 @@
|
|||||||
.logs-table {
|
.logs-table {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.resize-table {
|
.resize-table {
|
||||||
height: calc(92% - 5px);
|
height: calc(100% - 40px);
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
.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-header {
|
&::-webkit-scrollbar {
|
||||||
border-bottom: 0.5px solid var(--bg-slate-400);
|
width: 0.2rem;
|
||||||
}
|
height: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.ant-table-wrapper .ant-table-thead > tr > th {
|
.ant-table-wrapper .ant-table-tbody > tr > td {
|
||||||
font-family: Inter;
|
font-size: 13px;
|
||||||
color: var(--bg-vanilla-100);
|
font-style: normal;
|
||||||
background-color: transparent;
|
font-weight: 400;
|
||||||
border: none;
|
line-height: 18px;
|
||||||
font-size: 14px;
|
font-family: Inter;
|
||||||
font-style: normal;
|
.ant-typography {
|
||||||
font-weight: 600;
|
background-color: transparent;
|
||||||
line-height: 22px;
|
color: var(--bg-vanilla-100);
|
||||||
letter-spacing: 0.5px;
|
margin-bottom: 0;
|
||||||
padding: 8px;
|
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 {
|
.ant-table-wrapper .ant-table-header {
|
||||||
display: none;
|
border-bottom: 0.5px solid var(--bg-slate-400);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.controller {
|
.ant-table-wrapper .ant-table-thead > tr > th {
|
||||||
position: absolute;
|
font-family: Inter;
|
||||||
bottom: 5px;
|
color: var(--bg-vanilla-100);
|
||||||
right: 10px;
|
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 {
|
.lightMode {
|
||||||
.logs-table {
|
.logs-table {
|
||||||
.resize-table {
|
.resize-table {
|
||||||
.ant-table-wrapper .ant-table-tbody >tr >td {
|
.ant-table-wrapper .ant-table-tbody > tr > td {
|
||||||
background-color: var(--bg-vanilla-100);
|
background-color: var(--bg-vanilla-100);
|
||||||
.ant-typography {
|
.ant-typography {
|
||||||
color: var(--bg-ink-500);
|
color: var(--bg-ink-500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ant-table-wrapper .ant-table-thead > tr > th {
|
.ant-table-wrapper .ant-table-thead > tr > th {
|
||||||
background-color: var(--bg-vanilla-100);
|
background-color: var(--bg-vanilla-100);
|
||||||
color: var(--bg-ink-500);
|
color: var(--bg-ink-500);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-table-wrapper .ant-table-header {
|
.ant-table-wrapper .ant-table-header {
|
||||||
border-bottom: 0.5px solid var(--bg-vanilla-400);
|
border-bottom: 0.5px solid var(--bg-vanilla-400);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import { Table } from 'antd';
|
|||||||
import LogDetail from 'components/LogDetail';
|
import LogDetail from 'components/LogDetail';
|
||||||
import { VIEW_TYPES } from 'components/LogDetail/constants';
|
import { VIEW_TYPES } from 'components/LogDetail/constants';
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { OPERATORS, PANEL_TYPES } from 'constants/queryBuilder';
|
import { OPERATORS, PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||||
import Controls from 'container/Controls';
|
import Controls from 'container/Controls';
|
||||||
@ -106,6 +107,7 @@ function LogsPanelComponent({
|
|||||||
selectedTime: selectedTime?.enum || 'GLOBAL_TIME',
|
selectedTime: selectedTime?.enum || 'GLOBAL_TIME',
|
||||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||||
},
|
},
|
||||||
|
DEFAULT_ENTITY_VERSION,
|
||||||
{
|
{
|
||||||
queryKey: [
|
queryKey: [
|
||||||
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
||||||
|
@ -48,7 +48,7 @@ export const getQueryBuilderQueries = ({
|
|||||||
items: filterItems[index],
|
items: filterItems[index],
|
||||||
op: 'AND',
|
op: 'AND',
|
||||||
},
|
},
|
||||||
reduceTo: 'sum',
|
reduceTo: 'avg',
|
||||||
dataSource,
|
dataSource,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ export const getQueryBuilderQuerieswithFormula = ({
|
|||||||
aggregateAttribute: autocompleteData[index],
|
aggregateAttribute: autocompleteData[index],
|
||||||
queryName: alphabet[index],
|
queryName: alphabet[index],
|
||||||
expression: alphabet[index],
|
expression: alphabet[index],
|
||||||
reduceTo: 'sum',
|
reduceTo: 'avg',
|
||||||
filters: {
|
filters: {
|
||||||
items: additionalItems[index],
|
items: additionalItems[index],
|
||||||
op: 'AND',
|
op: 'AND',
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { topOperationMetricsDownloadOptions } from 'container/MetricsApplication/constant';
|
import { topOperationMetricsDownloadOptions } from 'container/MetricsApplication/constant';
|
||||||
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
|
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
|
||||||
@ -67,6 +68,7 @@ function TopOperationMetrics(): JSX.Element {
|
|||||||
globalSelectedInterval,
|
globalSelectedInterval,
|
||||||
variables: {},
|
variables: {},
|
||||||
},
|
},
|
||||||
|
DEFAULT_ENTITY_VERSION,
|
||||||
{
|
{
|
||||||
queryKey: [
|
queryKey: [
|
||||||
`GetMetricsQueryRange-${keyOperationWidget?.timePreferance}-${globalSelectedInterval}-${keyOperationWidget?.id}`,
|
`GetMetricsQueryRange-${keyOperationWidget?.timePreferance}-${globalSelectedInterval}-${keyOperationWidget?.id}`,
|
||||||
|
@ -2,12 +2,10 @@ import { SOMETHING_WENT_WRONG } from 'constants/api';
|
|||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import createQueryParams from 'lib/createQueryParams';
|
import createQueryParams from 'lib/createQueryParams';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { CSSProperties } from 'react';
|
|
||||||
import { LogsAggregatorOperator } from 'types/common/queryBuilder';
|
import { LogsAggregatorOperator } from 'types/common/queryBuilder';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
@ -20,8 +18,6 @@ import menuItems from './menuItems';
|
|||||||
import { Card, Container, Text } from './styles';
|
import { Card, Container, Text } from './styles';
|
||||||
|
|
||||||
function DashboardGraphSlider(): JSX.Element {
|
function DashboardGraphSlider(): JSX.Element {
|
||||||
const isDarkMode = useIsDarkMode();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
handleToggleDashboardSlider,
|
handleToggleDashboardSlider,
|
||||||
layouts,
|
layouts,
|
||||||
@ -47,6 +43,7 @@ function DashboardGraphSlider(): JSX.Element {
|
|||||||
description: data?.description || '',
|
description: data?.description || '',
|
||||||
name: data?.name || '',
|
name: data?.name || '',
|
||||||
tags: data?.tags || [],
|
tags: data?.tags || [],
|
||||||
|
version: data?.version || 'v3',
|
||||||
layout: [
|
layout: [
|
||||||
{
|
{
|
||||||
i: id,
|
i: id,
|
||||||
@ -146,13 +143,11 @@ function DashboardGraphSlider(): JSX.Element {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fillColor: CSSProperties['color'] = isDarkMode ? 'white' : 'black';
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
{menuItems.map(({ name, Icon, display }) => (
|
{menuItems.map(({ name, icon, display }) => (
|
||||||
<Card onClick={onClickHandler(name)} id={name} key={name}>
|
<Card onClick={onClickHandler(name)} id={name} key={name}>
|
||||||
<Icon fillColor={fillColor} />
|
{icon}
|
||||||
<Text>{display}</Text>
|
<Text>{display}</Text>
|
||||||
</Card>
|
</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;
|
display: flex;
|
||||||
justify-content: right;
|
justify-content: right;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
margin-bottom: 12px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Card = styled(CardComponent)`
|
export const Card = styled(CardComponent)`
|
||||||
min-height: 10vh;
|
min-height: 80px;
|
||||||
min-width: 120px;
|
min-width: 120px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
transition: transform 0.2s;
|
||||||
|
|
||||||
.ant-card-body {
|
.ant-card-body {
|
||||||
|
padding: 12px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
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 {
|
.dashboard-description {
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-line-clamp: 2; /* Show up to 2 lines */
|
-webkit-line-clamp: 2; /* Show up to 2 lines */
|
||||||
@ -5,3 +18,22 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
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, Tooltip } from 'antd';
|
||||||
import { Button } from 'antd';
|
import { Cog } from 'lucide-react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import DashboardSettingsContent from '../DashboardSettings';
|
import DashboardSettingsContent from '../DashboardSettings';
|
||||||
@ -18,14 +18,16 @@ function SettingsDrawer({ drawerTitle }: { drawerTitle: string }): JSX.Element {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Tooltip title="Configure" placement="left">
|
||||||
type="dashed"
|
<Button
|
||||||
onClick={showDrawer}
|
className="periscope-btn"
|
||||||
style={{ width: '100%' }}
|
onClick={showDrawer}
|
||||||
data-testid="show-drawer"
|
style={{ width: '100%' }}
|
||||||
>
|
data-testid="show-drawer"
|
||||||
<SettingOutlined /> Configure
|
icon={<Cog size={16} />}
|
||||||
</Button>
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
<DrawerContainer
|
<DrawerContainer
|
||||||
title={drawerTitle}
|
title={drawerTitle}
|
||||||
placement="right"
|
placement="right"
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import './Description.styles.scss';
|
import './Description.styles.scss';
|
||||||
|
|
||||||
import { LockFilled, ShareAltOutlined, UnlockFilled } from '@ant-design/icons';
|
import { LockFilled, UnlockFilled } from '@ant-design/icons';
|
||||||
import { Button, Card, Col, Row, Space, Tag, Tooltip, Typography } from 'antd';
|
import { Button, Card, Col, Row, Tag, Tooltip, Typography } from 'antd';
|
||||||
import useComponentPermission from 'hooks/useComponentPermission';
|
import useComponentPermission from 'hooks/useComponentPermission';
|
||||||
|
import { Share2 } from 'lucide-react';
|
||||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { DashboardData } from 'types/api/dashboard/getAll';
|
import { DashboardData } from 'types/api/dashboard/getAll';
|
||||||
@ -29,7 +29,6 @@ function DashboardDescription(): JSX.Element {
|
|||||||
|
|
||||||
const [openDashboardJSON, setOpenDashboardJSON] = useState<boolean>(false);
|
const [openDashboardJSON, setOpenDashboardJSON] = useState<boolean>(false);
|
||||||
|
|
||||||
const { t } = useTranslation('common');
|
|
||||||
const { user, role } = useSelector<AppState, AppReducer>((state) => state.app);
|
const { user, role } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
const [editDashboard] = useComponentPermission(['edit_dashboard'], role);
|
const [editDashboard] = useComponentPermission(['edit_dashboard'], role);
|
||||||
|
|
||||||
@ -48,7 +47,7 @@ function DashboardDescription(): JSX.Element {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card style={{ marginTop: '1rem' }}>
|
<Card className="dashboard-description-container">
|
||||||
<Row gutter={16}>
|
<Row gutter={16}>
|
||||||
<Col flex={1} span={9}>
|
<Col flex={1} span={9}>
|
||||||
<Typography.Title
|
<Typography.Title
|
||||||
@ -80,12 +79,12 @@ function DashboardDescription(): JSX.Element {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={14}>
|
||||||
<Row justify="end">
|
<Row justify="end">
|
||||||
<DashboardVariableSelection />
|
<DashboardVariableSelection />
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={3} style={{ textAlign: 'right' }}>
|
<Col span={1} style={{ textAlign: 'right' }}>
|
||||||
{selectedData && (
|
{selectedData && (
|
||||||
<ShareModal
|
<ShareModal
|
||||||
isJSONModalVisible={openDashboardJSON}
|
isJSONModalVisible={openDashboardJSON}
|
||||||
@ -94,18 +93,20 @@ function DashboardDescription(): JSX.Element {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Space direction="vertical">
|
<div className="dashboard-actions">
|
||||||
{!isDashboardLocked && editDashboard && (
|
{!isDashboardLocked && editDashboard && (
|
||||||
<SettingsDrawer drawerTitle={title} />
|
<SettingsDrawer drawerTitle={title} />
|
||||||
)}
|
)}
|
||||||
<Button
|
|
||||||
style={{ width: '100%' }}
|
<Tooltip title="Share" placement="left">
|
||||||
type="dashed"
|
<Button
|
||||||
onClick={onToggleHandler}
|
className="periscope-btn"
|
||||||
icon={<ShareAltOutlined />}
|
style={{ width: '100%' }}
|
||||||
>
|
onClick={onToggleHandler}
|
||||||
{t('share')}
|
icon={<Share2 size={16} />}
|
||||||
</Button>
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
{(isAuthor || role === USER_ROLES.ADMIN) && (
|
{(isAuthor || role === USER_ROLES.ADMIN) && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
placement="left"
|
placement="left"
|
||||||
@ -113,15 +114,13 @@ function DashboardDescription(): JSX.Element {
|
|||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
type="dashed"
|
className="periscope-btn"
|
||||||
onClick={handleLockDashboardToggle}
|
onClick={handleLockDashboardToggle}
|
||||||
icon={isDashboardLocked ? <LockFilled /> : <UnlockFilled />}
|
icon={isDashboardLocked ? <LockFilled /> : <UnlockFilled />}
|
||||||
>
|
/>
|
||||||
{isDashboardLocked ? 'Unlock' : 'Lock'}
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</Space>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -2,10 +2,13 @@ import './QuerySection.styles.scss';
|
|||||||
|
|
||||||
import { Button, Tabs, Tooltip, Typography } from 'antd';
|
import { Button, Tabs, Tooltip, Typography } from 'antd';
|
||||||
import TextToolTip from 'components/TextToolTip';
|
import TextToolTip from 'components/TextToolTip';
|
||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
|
import { QBShortcuts } from 'constants/shortcuts/QBShortcuts';
|
||||||
import { WidgetGraphProps } from 'container/NewWidget/types';
|
import { WidgetGraphProps } from 'container/NewWidget/types';
|
||||||
import { QueryBuilder } from 'container/QueryBuilder';
|
import { QueryBuilder } from 'container/QueryBuilder';
|
||||||
import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces';
|
import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces';
|
||||||
|
import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys';
|
||||||
import { useGetWidgetQueryRange } from 'hooks/queryBuilder/useGetWidgetQueryRange';
|
import { useGetWidgetQueryRange } from 'hooks/queryBuilder/useGetWidgetQueryRange';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
||||||
@ -18,7 +21,7 @@ import {
|
|||||||
getPreviousWidgets,
|
getPreviousWidgets,
|
||||||
getSelectedWidgetIndex,
|
getSelectedWidgetIndex,
|
||||||
} from 'providers/Dashboard/util';
|
} from 'providers/Dashboard/util';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
@ -36,6 +39,7 @@ function QuerySection({
|
|||||||
}: QueryProps): JSX.Element {
|
}: QueryProps): JSX.Element {
|
||||||
const { currentQuery, redirectWithQueryBuilderData } = useQueryBuilder();
|
const { currentQuery, redirectWithQueryBuilderData } = useQueryBuilder();
|
||||||
const urlQuery = useUrlQuery();
|
const urlQuery = useUrlQuery();
|
||||||
|
const { registerShortcut, deregisterShortcut } = useKeyboardHotkeys();
|
||||||
|
|
||||||
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
@ -47,10 +51,13 @@ function QuerySection({
|
|||||||
|
|
||||||
const { selectedDashboard, setSelectedDashboard } = useDashboard();
|
const { selectedDashboard, setSelectedDashboard } = useDashboard();
|
||||||
|
|
||||||
const getWidgetQueryRange = useGetWidgetQueryRange({
|
const getWidgetQueryRange = useGetWidgetQueryRange(
|
||||||
graphType: selectedGraph,
|
{
|
||||||
selectedTime: selectedTime.enum,
|
graphType: selectedGraph,
|
||||||
});
|
selectedTime: selectedTime.enum,
|
||||||
|
},
|
||||||
|
selectedDashboard?.data?.version || DEFAULT_ENTITY_VERSION,
|
||||||
|
);
|
||||||
|
|
||||||
const { widgets } = selectedDashboard?.data || {};
|
const { widgets } = selectedDashboard?.data || {};
|
||||||
|
|
||||||
@ -149,6 +156,7 @@ function QuerySection({
|
|||||||
<QueryBuilder
|
<QueryBuilder
|
||||||
panelType={PANEL_TYPES.LIST}
|
panelType={PANEL_TYPES.LIST}
|
||||||
filterConfigs={filterConfigs}
|
filterConfigs={filterConfigs}
|
||||||
|
version={selectedDashboard?.data?.version || 'v3'}
|
||||||
isListViewPanel
|
isListViewPanel
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@ -167,7 +175,11 @@ function QuerySection({
|
|||||||
),
|
),
|
||||||
tab: <Typography>Query Builder</Typography>,
|
tab: <Typography>Query Builder</Typography>,
|
||||||
children: (
|
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 (
|
return (
|
||||||
<div className="dashboard-navigation">
|
<div className="dashboard-navigation">
|
||||||
<Tabs
|
<Tabs
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Card, Typography } from 'antd';
|
import { Card, Typography } from 'antd';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { WidgetGraphProps } from 'container/NewWidget/types';
|
import { WidgetGraphProps } from 'container/NewWidget/types';
|
||||||
import { useGetWidgetQueryRange } from 'hooks/queryBuilder/useGetWidgetQueryRange';
|
import { useGetWidgetQueryRange } from 'hooks/queryBuilder/useGetWidgetQueryRange';
|
||||||
@ -32,10 +33,13 @@ function WidgetGraphContainer({
|
|||||||
|
|
||||||
const selectedWidget = widgets.find((e) => e.id === widgetId);
|
const selectedWidget = widgets.find((e) => e.id === widgetId);
|
||||||
|
|
||||||
const getWidgetQueryRange = useGetWidgetQueryRange({
|
const getWidgetQueryRange = useGetWidgetQueryRange(
|
||||||
graphType: getGraphType(selectedGraph),
|
{
|
||||||
selectedTime: selectedTime.enum,
|
graphType: getGraphType(selectedGraph),
|
||||||
});
|
selectedTime: selectedTime.enum,
|
||||||
|
},
|
||||||
|
selectedDashboard?.data?.version || DEFAULT_ENTITY_VERSION,
|
||||||
|
);
|
||||||
|
|
||||||
if (getWidgetQueryRange.data && selectedGraph === PANEL_TYPES.BAR) {
|
if (getWidgetQueryRange.data && selectedGraph === PANEL_TYPES.BAR) {
|
||||||
const sortedSeriesData = getSortedSeriesData(
|
const sortedSeriesData = getSortedSeriesData(
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { Card } from 'container/GridCardLayout/styles';
|
import { Card } from 'container/GridCardLayout/styles';
|
||||||
import { useGetWidgetQueryRange } from 'hooks/queryBuilder/useGetWidgetQueryRange';
|
import { useGetWidgetQueryRange } from 'hooks/queryBuilder/useGetWidgetQueryRange';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
@ -34,10 +35,13 @@ function WidgetGraph({
|
|||||||
|
|
||||||
const selectedWidget = widgets.find((e) => e.id === widgetId);
|
const selectedWidget = widgets.find((e) => e.id === widgetId);
|
||||||
|
|
||||||
const getWidgetQueryRange = useGetWidgetQueryRange({
|
const getWidgetQueryRange = useGetWidgetQueryRange(
|
||||||
graphType: getGraphType(selectedGraph),
|
{
|
||||||
selectedTime: selectedTime.enum,
|
graphType: getGraphType(selectedGraph),
|
||||||
});
|
selectedTime: selectedTime.enum,
|
||||||
|
},
|
||||||
|
selectedDashboard?.data?.version || DEFAULT_ENTITY_VERSION,
|
||||||
|
);
|
||||||
|
|
||||||
if (selectedWidget === undefined) {
|
if (selectedWidget === undefined) {
|
||||||
return <Card $panelType={selectedGraph}>Invalid widget</Card>;
|
return <Card $panelType={selectedGraph}>Invalid widget</Card>;
|
||||||
|
@ -5,7 +5,9 @@ import { SOMETHING_WENT_WRONG } from 'constants/api';
|
|||||||
import { FeatureKeys } from 'constants/features';
|
import { FeatureKeys } from 'constants/features';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
|
import { DashboardShortcuts } from 'constants/shortcuts/DashboardShortcuts';
|
||||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||||
|
import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import { MESSAGE, useIsFeatureDisabled } from 'hooks/useFeatureFlag';
|
import { MESSAGE, useIsFeatureDisabled } from 'hooks/useFeatureFlag';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
@ -18,7 +20,7 @@ import {
|
|||||||
getPreviousWidgets,
|
getPreviousWidgets,
|
||||||
getSelectedWidgetIndex,
|
getSelectedWidgetIndex,
|
||||||
} from 'providers/Dashboard/util';
|
} from 'providers/Dashboard/util';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { generatePath, useLocation, useParams } from 'react-router-dom';
|
import { generatePath, useLocation, useParams } from 'react-router-dom';
|
||||||
@ -53,6 +55,8 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
|||||||
|
|
||||||
const { t } = useTranslation(['dashboard']);
|
const { t } = useTranslation(['dashboard']);
|
||||||
|
|
||||||
|
const { registerShortcut, deregisterShortcut } = useKeyboardHotkeys();
|
||||||
|
|
||||||
const { currentQuery, stagedQuery } = useQueryBuilder();
|
const { currentQuery, stagedQuery } = useQueryBuilder();
|
||||||
|
|
||||||
const isQueryModified = useMemo(
|
const isQueryModified = useMemo(
|
||||||
@ -311,6 +315,17 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
|||||||
isNewTraceLogsAvailable,
|
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 (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
@ -414,7 +429,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
|||||||
<Typography>
|
<Typography>
|
||||||
{t('your_graph_build_with')}{' '}
|
{t('your_graph_build_with')}{' '}
|
||||||
<QueryTypeTag queryType={currentQuery.queryType} />
|
<QueryTypeTag queryType={currentQuery.queryType} />
|
||||||
{t('dashboar_ok_confirm')}
|
{t('dashboard_ok_confirm')}
|
||||||
</Typography>
|
</Typography>
|
||||||
) : (
|
) : (
|
||||||
<Typography>{t('dashboard_unsave_changes')} </Typography>
|
<Typography>{t('dashboard_unsave_changes')} </Typography>
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
CloseCircleTwoTone,
|
CloseCircleTwoTone,
|
||||||
LoadingOutlined,
|
LoadingOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import Header from 'container/OnboardingContainer/common/Header/Header';
|
import Header from 'container/OnboardingContainer/common/Header/Header';
|
||||||
import { useOnboardingContext } from 'container/OnboardingContainer/context/OnboardingContext';
|
import { useOnboardingContext } from 'container/OnboardingContainer/context/OnboardingContext';
|
||||||
@ -72,6 +73,9 @@ export default function LogsConnectionStatus(): JSX.Element {
|
|||||||
reduceTo: 'sum',
|
reduceTo: 'sum',
|
||||||
offset: 0,
|
offset: 0,
|
||||||
pageSize: 100,
|
pageSize: 100,
|
||||||
|
timeAggregation: '',
|
||||||
|
spaceAggregation: '',
|
||||||
|
functions: [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
queryFormulas: [],
|
queryFormulas: [],
|
||||||
@ -84,6 +88,7 @@ export default function LogsConnectionStatus(): JSX.Element {
|
|||||||
const { data, isFetching, error, isError } = useGetExplorerQueryRange(
|
const { data, isFetching, error, isError } = useGetExplorerQueryRange(
|
||||||
requestData,
|
requestData,
|
||||||
PANEL_TYPES.LIST,
|
PANEL_TYPES.LIST,
|
||||||
|
DEFAULT_ENTITY_VERSION,
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
keepPreviousData: true,
|
||||||
refetchInterval: pollingInterval,
|
refetchInterval: pollingInterval,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import './styles.scss';
|
import './styles.scss';
|
||||||
|
|
||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import {
|
import {
|
||||||
initialFilters,
|
initialFilters,
|
||||||
initialQueriesMap,
|
initialQueriesMap,
|
||||||
@ -26,12 +27,15 @@ function LogsCountInInterval({
|
|||||||
return q;
|
return q;
|
||||||
}, [filter]);
|
}, [filter]);
|
||||||
|
|
||||||
const result = useGetQueryRange({
|
const result = useGetQueryRange(
|
||||||
graphType: PANEL_TYPES.TABLE,
|
{
|
||||||
query,
|
graphType: PANEL_TYPES.TABLE,
|
||||||
selectedTime: 'GLOBAL_TIME',
|
query,
|
||||||
globalSelectedInterval: timeInterval,
|
selectedTime: 'GLOBAL_TIME',
|
||||||
});
|
globalSelectedInterval: timeInterval,
|
||||||
|
},
|
||||||
|
DEFAULT_ENTITY_VERSION,
|
||||||
|
);
|
||||||
|
|
||||||
if (!result.isFetched) {
|
if (!result.isFetched) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import {
|
import {
|
||||||
initialFilters,
|
initialFilters,
|
||||||
initialQueriesMap,
|
initialQueriesMap,
|
||||||
@ -42,12 +43,15 @@ const useSampleLogs = ({
|
|||||||
return q;
|
return q;
|
||||||
}, [count, filter]);
|
}, [count, filter]);
|
||||||
|
|
||||||
const response = useGetQueryRange({
|
const response = useGetQueryRange(
|
||||||
graphType: PANEL_TYPES.LIST,
|
{
|
||||||
query,
|
graphType: PANEL_TYPES.LIST,
|
||||||
selectedTime: 'GLOBAL_TIME',
|
query,
|
||||||
globalSelectedInterval: timeInterval,
|
selectedTime: 'GLOBAL_TIME',
|
||||||
});
|
globalSelectedInterval: timeInterval,
|
||||||
|
},
|
||||||
|
DEFAULT_ENTITY_VERSION,
|
||||||
|
);
|
||||||
|
|
||||||
const { isFetching: isLoading, data } = response;
|
const { isFetching: isLoading, data } = response;
|
||||||
|
|
||||||
|
@ -27,4 +27,6 @@ export type QueryBuilderProps = {
|
|||||||
filterConfigs?: Partial<FilterConfigs>;
|
filterConfigs?: Partial<FilterConfigs>;
|
||||||
queryComponents?: { renderOrderBy?: (props: OrderByFilterProps) => ReactNode };
|
queryComponents?: { renderOrderBy?: (props: OrderByFilterProps) => ReactNode };
|
||||||
isListViewPanel?: boolean;
|
isListViewPanel?: boolean;
|
||||||
|
showFunctions?: boolean;
|
||||||
|
version: string;
|
||||||
};
|
};
|
||||||
|
@ -25,6 +25,8 @@ export const QueryBuilder = memo(function QueryBuilder({
|
|||||||
filterConfigs = {},
|
filterConfigs = {},
|
||||||
queryComponents,
|
queryComponents,
|
||||||
isListViewPanel = false,
|
isListViewPanel = false,
|
||||||
|
showFunctions = false,
|
||||||
|
version,
|
||||||
}: QueryBuilderProps): JSX.Element {
|
}: QueryBuilderProps): JSX.Element {
|
||||||
const {
|
const {
|
||||||
currentQuery,
|
currentQuery,
|
||||||
@ -170,6 +172,8 @@ export const QueryBuilder = memo(function QueryBuilder({
|
|||||||
: listViewLogFilterConfigs
|
: listViewLogFilterConfigs
|
||||||
}
|
}
|
||||||
queryComponents={queryComponents}
|
queryComponents={queryComponents}
|
||||||
|
showFunctions={showFunctions}
|
||||||
|
version={version}
|
||||||
isListViewPanel
|
isListViewPanel
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -188,6 +192,8 @@ export const QueryBuilder = memo(function QueryBuilder({
|
|||||||
query={query}
|
query={query}
|
||||||
filterConfigs={filterConfigs}
|
filterConfigs={filterConfigs}
|
||||||
queryComponents={queryComponents}
|
queryComponents={queryComponents}
|
||||||
|
showFunctions={showFunctions}
|
||||||
|
version={version}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
))}
|
))}
|
||||||
|
@ -39,6 +39,7 @@ export function Formula({
|
|||||||
query,
|
query,
|
||||||
filterConfigs,
|
filterConfigs,
|
||||||
formula,
|
formula,
|
||||||
|
entityVersion: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const [isCollapse, setIsCollapsed] = useState(false);
|
const [isCollapse, setIsCollapsed] = useState(false);
|
||||||
@ -146,6 +147,7 @@ export function Formula({
|
|||||||
<Row gutter={[0, 15]}>
|
<Row gutter={[0, 15]}>
|
||||||
<QBEntityOptions
|
<QBEntityOptions
|
||||||
isCollapsed={isCollapse}
|
isCollapsed={isCollapse}
|
||||||
|
showFunctions={false}
|
||||||
entityType="formula"
|
entityType="formula"
|
||||||
entityData={formula}
|
entityData={formula}
|
||||||
onToggleVisibility={handleToggleDisableFormula}
|
onToggleVisibility={handleToggleDisableFormula}
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin: 4px 0;
|
margin: 4px 0;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
.left-col-items {
|
.left-col-items {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@ -21,6 +23,7 @@
|
|||||||
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
margin-left: -51px;
|
margin-left: -51px;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
|
||||||
@ -63,6 +66,10 @@
|
|||||||
color: var(--bg-sienna-400) !important;
|
color: var(--bg-sienna-400) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.options-group {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,80 +1,107 @@
|
|||||||
import './QBEntityOptions.styles.scss';
|
import './QBEntityOptions.styles.scss';
|
||||||
|
|
||||||
import { Button, Col } from 'antd';
|
import { Button } from 'antd';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
import { ChevronDown, ChevronRight, Eye, EyeOff, Trash2 } from 'lucide-react';
|
import { ChevronDown, ChevronRight, Eye, EyeOff, Trash2 } from 'lucide-react';
|
||||||
|
import {
|
||||||
|
IBuilderQuery,
|
||||||
|
QueryFunctionProps,
|
||||||
|
} from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
|
||||||
|
import QueryFunctions from '../QueryFunctions/QueryFunctions';
|
||||||
|
|
||||||
interface QBEntityOptionsProps {
|
interface QBEntityOptionsProps {
|
||||||
|
query?: IBuilderQuery;
|
||||||
|
isMetricsDataSource?: boolean;
|
||||||
|
showFunctions?: boolean;
|
||||||
isCollapsed: boolean;
|
isCollapsed: boolean;
|
||||||
entityType: string;
|
entityType: string;
|
||||||
entityData: any;
|
entityData: any;
|
||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
onToggleVisibility: () => void;
|
onToggleVisibility: () => void;
|
||||||
onCollapseEntity: () => void;
|
onCollapseEntity: () => void;
|
||||||
|
onQueryFunctionsUpdates?: (functions: QueryFunctionProps[]) => void;
|
||||||
showDeleteButton: boolean;
|
showDeleteButton: boolean;
|
||||||
isListViewPanel?: boolean;
|
isListViewPanel?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function QBEntityOptions({
|
export default function QBEntityOptions({
|
||||||
|
query,
|
||||||
|
isMetricsDataSource,
|
||||||
isCollapsed,
|
isCollapsed,
|
||||||
|
showFunctions,
|
||||||
entityType,
|
entityType,
|
||||||
entityData,
|
entityData,
|
||||||
onDelete,
|
onDelete,
|
||||||
onToggleVisibility,
|
onToggleVisibility,
|
||||||
onCollapseEntity,
|
onCollapseEntity,
|
||||||
showDeleteButton,
|
showDeleteButton,
|
||||||
isListViewPanel = false,
|
onQueryFunctionsUpdates,
|
||||||
|
isListViewPanel,
|
||||||
}: QBEntityOptionsProps): JSX.Element {
|
}: QBEntityOptionsProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Col span={24}>
|
<div className="qb-entity-options">
|
||||||
<div className="qb-entity-options">
|
<div className="left-col-items">
|
||||||
<div className="left-col-items">
|
<div className="options periscope-btn-group">
|
||||||
<div className="options periscope-btn-group">
|
<Button.Group className="options-group">
|
||||||
<Button.Group>
|
<Button
|
||||||
<Button
|
value="search"
|
||||||
value="search"
|
className="periscope-btn collapse"
|
||||||
className="periscope-btn collapse"
|
onClick={onCollapseEntity}
|
||||||
onClick={onCollapseEntity}
|
>
|
||||||
>
|
{isCollapsed ? <ChevronRight size={16} /> : <ChevronDown size={16} />}
|
||||||
{isCollapsed ? <ChevronRight size={16} /> : <ChevronDown size={16} />}
|
</Button>
|
||||||
</Button>
|
<Button
|
||||||
<Button
|
value="query-builder"
|
||||||
value="query-builder"
|
className="periscope-btn visibility-toggle"
|
||||||
className="periscope-btn visibility-toggle"
|
onClick={onToggleVisibility}
|
||||||
onClick={onToggleVisibility}
|
disabled={isListViewPanel}
|
||||||
disabled={isListViewPanel}
|
>
|
||||||
>
|
{entityData.disabled ? <EyeOff size={16} /> : <Eye size={16} />}
|
||||||
{entityData.disabled ? <EyeOff size={16} /> : <Eye size={16} />}
|
</Button>
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
className={cx(
|
|
||||||
'periscope-btn',
|
|
||||||
entityType === 'query' ? 'query-name' : 'formula-name',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{entityData.queryName}
|
|
||||||
</Button>
|
|
||||||
</Button.Group>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isCollapsed && (
|
<Button
|
||||||
<div className="title">
|
className={cx(
|
||||||
<span className="entityType"> {entityType} </span> -{' '}
|
'periscope-btn',
|
||||||
<span className="entityData"> {entityData.queryName} </span>
|
entityType === 'query' ? 'query-name' : 'formula-name',
|
||||||
</div>
|
)}
|
||||||
)}
|
>
|
||||||
|
{entityData.queryName}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{showFunctions &&
|
||||||
|
isMetricsDataSource &&
|
||||||
|
query &&
|
||||||
|
onQueryFunctionsUpdates && (
|
||||||
|
<QueryFunctions
|
||||||
|
queryFunctions={query.functions}
|
||||||
|
onChange={onQueryFunctionsUpdates}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Button.Group>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showDeleteButton && (
|
{isCollapsed && (
|
||||||
<Button className="periscope-btn ghost" onClick={onDelete}>
|
<div className="title">
|
||||||
<Trash2 size={14} />
|
<span className="entityType"> {entityType} </span> -{' '}
|
||||||
</Button>
|
<span className="entityData"> {entityData.queryName} </span>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
|
||||||
|
{showDeleteButton && (
|
||||||
|
<Button className="periscope-btn ghost" onClick={onDelete}>
|
||||||
|
<Trash2 size={14} />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
QBEntityOptions.defaultProps = {
|
QBEntityOptions.defaultProps = {
|
||||||
isListViewPanel: false,
|
isListViewPanel: false,
|
||||||
|
query: undefined,
|
||||||
|
isMetricsDataSource: false,
|
||||||
|
onQueryFunctionsUpdates: undefined,
|
||||||
|
showFunctions: false,
|
||||||
};
|
};
|
||||||
|
@ -7,4 +7,6 @@ export type QueryProps = {
|
|||||||
query: IBuilderQuery;
|
query: IBuilderQuery;
|
||||||
queryVariant: 'static' | 'dropdown';
|
queryVariant: 'static' | 'dropdown';
|
||||||
isListViewPanel?: boolean;
|
isListViewPanel?: boolean;
|
||||||
|
showFunctions?: boolean;
|
||||||
|
version: string;
|
||||||
} & Pick<QueryBuilderProps, 'filterConfigs' | 'queryComponents'>;
|
} & Pick<QueryBuilderProps, 'filterConfigs' | 'queryComponents'>;
|
||||||
|
@ -3,7 +3,7 @@ import './Query.styles.scss';
|
|||||||
|
|
||||||
import { Col, Input, Row } from 'antd';
|
import { Col, Input, Row } from 'antd';
|
||||||
// ** Constants
|
// ** Constants
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { ATTRIBUTE_TYPES, PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
// ** Components
|
// ** Components
|
||||||
import {
|
import {
|
||||||
@ -38,9 +38,11 @@ import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
|||||||
import { transformToUpperCase } from 'utils/transformToUpperCase';
|
import { transformToUpperCase } from 'utils/transformToUpperCase';
|
||||||
|
|
||||||
import QBEntityOptions from '../QBEntityOptions/QBEntityOptions';
|
import QBEntityOptions from '../QBEntityOptions/QBEntityOptions';
|
||||||
|
import SpaceAggregationOptions from '../SpaceAggregationOptions/SpaceAggregationOptions';
|
||||||
// ** Types
|
// ** Types
|
||||||
import { QueryProps } from './Query.interfaces';
|
import { QueryProps } from './Query.interfaces';
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
export const Query = memo(function Query({
|
export const Query = memo(function Query({
|
||||||
index,
|
index,
|
||||||
queryVariant,
|
queryVariant,
|
||||||
@ -48,6 +50,8 @@ export const Query = memo(function Query({
|
|||||||
filterConfigs,
|
filterConfigs,
|
||||||
queryComponents,
|
queryComponents,
|
||||||
isListViewPanel = false,
|
isListViewPanel = false,
|
||||||
|
showFunctions = false,
|
||||||
|
version,
|
||||||
}: QueryProps): JSX.Element {
|
}: QueryProps): JSX.Element {
|
||||||
const { panelType, currentQuery } = useQueryBuilder();
|
const { panelType, currentQuery } = useQueryBuilder();
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
@ -56,6 +60,7 @@ export const Query = memo(function Query({
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
operators,
|
operators,
|
||||||
|
spaceAggregationOptions,
|
||||||
isMetricsDataSource,
|
isMetricsDataSource,
|
||||||
isTracePanelType,
|
isTracePanelType,
|
||||||
listOfAdditionalFilters,
|
listOfAdditionalFilters,
|
||||||
@ -63,8 +68,16 @@ export const Query = memo(function Query({
|
|||||||
handleChangeQueryData,
|
handleChangeQueryData,
|
||||||
handleChangeDataSource,
|
handleChangeDataSource,
|
||||||
handleChangeOperator,
|
handleChangeOperator,
|
||||||
|
handleSpaceAggregationChange,
|
||||||
handleDeleteQuery,
|
handleDeleteQuery,
|
||||||
} = useQueryOperations({ index, query, filterConfigs, isListViewPanel });
|
handleQueryFunctionsUpdates,
|
||||||
|
} = useQueryOperations({
|
||||||
|
index,
|
||||||
|
query,
|
||||||
|
filterConfigs,
|
||||||
|
isListViewPanel,
|
||||||
|
entityVersion: version,
|
||||||
|
});
|
||||||
|
|
||||||
const handleChangeAggregateEvery = useCallback(
|
const handleChangeAggregateEvery = useCallback(
|
||||||
(value: IBuilderQuery['stepInterval']) => {
|
(value: IBuilderQuery['stepInterval']) => {
|
||||||
@ -192,13 +205,17 @@ export const Query = memo(function Query({
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={11}>
|
<Col span={24}>
|
||||||
<Row gutter={[11, 5]}>
|
<Row gutter={[11, 5]}>
|
||||||
<Col flex="5.93rem">
|
<Col flex="5.93rem">
|
||||||
<FilterLabel label="HAVING" />
|
<FilterLabel label="HAVING" />
|
||||||
</Col>
|
</Col>
|
||||||
<Col flex="1 1 12.5rem">
|
<Col flex="1 1 12.5rem">
|
||||||
<HavingFilter onChange={handleChangeHavingFilter} query={query} />
|
<HavingFilter
|
||||||
|
entityVersion={version}
|
||||||
|
onChange={handleChangeHavingFilter}
|
||||||
|
query={query}
|
||||||
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
@ -225,7 +242,11 @@ export const Query = memo(function Query({
|
|||||||
<FilterLabel label="HAVING" />
|
<FilterLabel label="HAVING" />
|
||||||
</Col>
|
</Col>
|
||||||
<Col flex="1 1 12.5rem">
|
<Col flex="1 1 12.5rem">
|
||||||
<HavingFilter onChange={handleChangeHavingFilter} query={query} />
|
<HavingFilter
|
||||||
|
onChange={handleChangeHavingFilter}
|
||||||
|
entityVersion={version}
|
||||||
|
query={query}
|
||||||
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
@ -257,7 +278,11 @@ export const Query = memo(function Query({
|
|||||||
<FilterLabel label="HAVING" />
|
<FilterLabel label="HAVING" />
|
||||||
</Col>
|
</Col>
|
||||||
<Col flex="1 1 12.5rem">
|
<Col flex="1 1 12.5rem">
|
||||||
<HavingFilter onChange={handleChangeHavingFilter} query={query} />
|
<HavingFilter
|
||||||
|
entityVersion={version}
|
||||||
|
onChange={handleChangeHavingFilter}
|
||||||
|
query={query}
|
||||||
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
@ -279,23 +304,33 @@ export const Query = memo(function Query({
|
|||||||
}, [
|
}, [
|
||||||
panelType,
|
panelType,
|
||||||
query,
|
query,
|
||||||
filterConfigs?.limit?.isHidden,
|
|
||||||
filterConfigs?.having?.isHidden,
|
|
||||||
handleChangeLimit,
|
handleChangeLimit,
|
||||||
|
version,
|
||||||
handleChangeHavingFilter,
|
handleChangeHavingFilter,
|
||||||
renderOrderByFilter,
|
renderOrderByFilter,
|
||||||
renderAggregateEveryFilter,
|
renderAggregateEveryFilter,
|
||||||
|
filterConfigs?.limit?.isHidden,
|
||||||
|
filterConfigs?.having?.isHidden,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const disableOperatorSelector =
|
||||||
|
!query?.aggregateAttribute.key || query?.aggregateAttribute.key === '';
|
||||||
|
|
||||||
|
const isVersionV4 = version && version === 'v4';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row gutter={[0, 12]}>
|
<Row gutter={[0, 12]}>
|
||||||
<QBEntityOptions
|
<QBEntityOptions
|
||||||
|
isMetricsDataSource={isMetricsDataSource}
|
||||||
|
showFunctions={(version && version === 'v4') || showFunctions || false}
|
||||||
isCollapsed={isCollapse}
|
isCollapsed={isCollapse}
|
||||||
entityType="query"
|
entityType="query"
|
||||||
entityData={query}
|
entityData={query}
|
||||||
onToggleVisibility={handleToggleDisableQuery}
|
onToggleVisibility={handleToggleDisableQuery}
|
||||||
onDelete={handleDeleteQuery}
|
onDelete={handleDeleteQuery}
|
||||||
onCollapseEntity={handleToggleCollapsQuery}
|
onCollapseEntity={handleToggleCollapsQuery}
|
||||||
|
query={query}
|
||||||
|
onQueryFunctionsUpdates={handleQueryFunctionsUpdates}
|
||||||
showDeleteButton={currentQuery.builder.queryData.length > 1}
|
showDeleteButton={currentQuery.builder.queryData.length > 1}
|
||||||
isListViewPanel={isListViewPanel}
|
isListViewPanel={isListViewPanel}
|
||||||
/>
|
/>
|
||||||
@ -322,23 +357,42 @@ export const Query = memo(function Query({
|
|||||||
{isMetricsDataSource && (
|
{isMetricsDataSource && (
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Row gutter={[11, 5]}>
|
<Row gutter={[11, 5]}>
|
||||||
<Col flex="5.93rem">
|
{version && version === 'v3' && (
|
||||||
<OperatorsSelect
|
<Col flex="5.93rem">
|
||||||
value={query.aggregateOperator}
|
<OperatorsSelect
|
||||||
onChange={handleChangeOperator}
|
value={query.aggregateOperator}
|
||||||
operators={operators}
|
onChange={handleChangeOperator}
|
||||||
/>
|
operators={operators}
|
||||||
</Col>
|
/>
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
|
|
||||||
<Col flex="auto">
|
<Col flex="auto">
|
||||||
<AggregatorFilter
|
<AggregatorFilter
|
||||||
onChange={handleChangeAggregatorAttribute}
|
onChange={handleChangeAggregatorAttribute}
|
||||||
query={query}
|
query={query}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</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>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
)}
|
)}
|
||||||
<Col flex="1 1 20rem">
|
|
||||||
|
<Col flex="1 1 40rem">
|
||||||
<Row gutter={[11, 5]}>
|
<Row gutter={[11, 5]}>
|
||||||
{isMetricsDataSource && (
|
{isMetricsDataSource && (
|
||||||
<Col>
|
<Col>
|
||||||
@ -379,16 +433,40 @@ export const Query = memo(function Query({
|
|||||||
</Col>
|
</Col>
|
||||||
)}
|
)}
|
||||||
{!isListViewPanel && (
|
{!isListViewPanel && (
|
||||||
<Col span={11} offset={isMetricsDataSource ? 0 : 2}>
|
<Col span={24}>
|
||||||
<Row gutter={[11, 5]}>
|
<Row gutter={[11, 5]}>
|
||||||
<Col flex="5.93rem">
|
<Col flex="5.93rem">
|
||||||
<FilterLabel
|
{isVersionV4 && isMetricsDataSource ? (
|
||||||
label={panelType === PANEL_TYPES.VALUE ? 'Reduce to' : 'Group by'}
|
<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>
|
||||||
|
|
||||||
<Col flex="1 1 12.5rem">
|
<Col flex="1 1 12.5rem">
|
||||||
{panelType === PANEL_TYPES.VALUE ? (
|
{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
|
<GroupByFilter
|
||||||
disabled={isMetricsDataSource && !query.aggregateAttribute.key}
|
disabled={isMetricsDataSource && !query.aggregateAttribute.key}
|
||||||
@ -397,6 +475,20 @@ export const Query = memo(function Query({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Col>
|
</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>
|
</Row>
|
||||||
</Col>
|
</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,
|
debouncedValue,
|
||||||
query.aggregateOperator,
|
query.aggregateOperator,
|
||||||
query.dataSource,
|
query.dataSource,
|
||||||
])?.payload.attributeKeys || [],
|
])?.payload?.attributeKeys || [],
|
||||||
[debouncedValue, query.aggregateOperator, query.dataSource, queryClient],
|
[debouncedValue, query.aggregateOperator, query.dataSource, queryClient],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Having, IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
import { Having, IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
|
||||||
export type HavingFilterProps = {
|
export type HavingFilterProps = {
|
||||||
|
entityVersion: string;
|
||||||
query: IBuilderQuery;
|
query: IBuilderQuery;
|
||||||
onChange: (having: Having[]) => void;
|
onChange: (having: Having[]) => void;
|
||||||
};
|
};
|
||||||
|
@ -22,6 +22,7 @@ import { getHavingObject, isValidHavingValue } from '../utils';
|
|||||||
import { HavingFilterProps } from './HavingFilter.interfaces';
|
import { HavingFilterProps } from './HavingFilter.interfaces';
|
||||||
|
|
||||||
export function HavingFilter({
|
export function HavingFilter({
|
||||||
|
entityVersion,
|
||||||
query,
|
query,
|
||||||
onChange,
|
onChange,
|
||||||
}: HavingFilterProps): JSX.Element {
|
}: HavingFilterProps): JSX.Element {
|
||||||
@ -48,10 +49,18 @@ export function HavingFilter({
|
|||||||
[query],
|
[query],
|
||||||
);
|
);
|
||||||
|
|
||||||
const columnName = useMemo(
|
const columnName = useMemo(() => {
|
||||||
() => `${query.aggregateOperator.toUpperCase()}(${aggregatorAttribute})`,
|
if (
|
||||||
[query, aggregatorAttribute],
|
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(
|
const aggregatorOptions: SelectOption<string, string>[] = useMemo(
|
||||||
() => [{ label: columnName, value: columnName }],
|
() => [{ label: columnName, value: columnName }],
|
||||||
@ -211,7 +220,7 @@ export function HavingFilter({
|
|||||||
disabled={isMetricsDataSource && !query.aggregateAttribute.key}
|
disabled={isMetricsDataSource && !query.aggregateAttribute.key}
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
notFoundContent={currentFormValue.value.length === 0 ? undefined : null}
|
notFoundContent={currentFormValue.value.length === 0 ? undefined : null}
|
||||||
placeholder="Count(operation) > 5"
|
placeholder="GroupBy(operation) > 5"
|
||||||
onDeselect={handleDeselect}
|
onDeselect={handleDeselect}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onSelect={handleSelect}
|
onSelect={handleSelect}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
// Constants
|
// Constants
|
||||||
import {
|
import {
|
||||||
HAVING_OPERATORS,
|
HAVING_OPERATORS,
|
||||||
@ -31,6 +32,7 @@ describe('Having filter behaviour', () => {
|
|||||||
<HavingFilter
|
<HavingFilter
|
||||||
query={initialQueryBuilderFormValuesMap.metrics}
|
query={initialQueryBuilderFormValuesMap.metrics}
|
||||||
onChange={mockFn}
|
onChange={mockFn}
|
||||||
|
entityVersion={DEFAULT_ENTITY_VERSION}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -49,6 +51,7 @@ describe('Having filter behaviour', () => {
|
|||||||
<HavingFilter
|
<HavingFilter
|
||||||
query={initialQueryBuilderFormValuesMap.metrics}
|
query={initialQueryBuilderFormValuesMap.metrics}
|
||||||
onChange={mockFn}
|
onChange={mockFn}
|
||||||
|
entityVersion={DEFAULT_ENTITY_VERSION}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -62,7 +65,11 @@ describe('Having filter behaviour', () => {
|
|||||||
test('Is having filter is enable', () => {
|
test('Is having filter is enable', () => {
|
||||||
const mockFn = jest.fn();
|
const mockFn = jest.fn();
|
||||||
const { unmount } = render(
|
const { unmount } = render(
|
||||||
<HavingFilter query={valueWithAttributeAndOperator} onChange={mockFn} />,
|
<HavingFilter
|
||||||
|
query={valueWithAttributeAndOperator}
|
||||||
|
onChange={mockFn}
|
||||||
|
entityVersion={DEFAULT_ENTITY_VERSION}
|
||||||
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
const input = screen.getByRole('combobox');
|
const input = screen.getByRole('combobox');
|
||||||
@ -80,7 +87,11 @@ describe('Having filter behaviour', () => {
|
|||||||
const optionTestTitle = 'havingOption';
|
const optionTestTitle = 'havingOption';
|
||||||
|
|
||||||
const { unmount } = render(
|
const { unmount } = render(
|
||||||
<HavingFilter query={valueWithAttributeAndOperator} onChange={onChange} />,
|
<HavingFilter
|
||||||
|
query={valueWithAttributeAndOperator}
|
||||||
|
onChange={onChange}
|
||||||
|
entityVersion={DEFAULT_ENTITY_VERSION}
|
||||||
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
// get input
|
// get input
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import {
|
import './QueryBuilderSearch.styles.scss';
|
||||||
SelectOptionContainer,
|
|
||||||
TagContainer,
|
import { Tooltip } from 'antd';
|
||||||
TagLabel,
|
|
||||||
TagValue,
|
import { TagContainer, TagLabel, TagValue } from './style';
|
||||||
} from './style';
|
|
||||||
import { getOptionType } from './utils';
|
import { getOptionType } from './utils';
|
||||||
|
|
||||||
function OptionRenderer({
|
function OptionRenderer({
|
||||||
@ -16,21 +15,25 @@ function OptionRenderer({
|
|||||||
return (
|
return (
|
||||||
<span className="option">
|
<span className="option">
|
||||||
{optionType ? (
|
{optionType ? (
|
||||||
<SelectOptionContainer>
|
<Tooltip title={`${value}`} placement="topLeft">
|
||||||
<div className="option-value">{value}</div>
|
<div className="selectOptionContainer">
|
||||||
<div className="option-meta-data-container">
|
<div className="option-value">{value}</div>
|
||||||
<TagContainer>
|
<div className="option-meta-data-container">
|
||||||
<TagLabel>Type: </TagLabel>
|
<TagContainer>
|
||||||
<TagValue>{optionType}</TagValue>
|
<TagLabel>Type: </TagLabel>
|
||||||
</TagContainer>
|
<TagValue>{optionType}</TagValue>
|
||||||
<TagContainer>
|
</TagContainer>
|
||||||
<TagLabel>Data type: </TagLabel>
|
<TagContainer>
|
||||||
<TagValue>{dataType}</TagValue>
|
<TagLabel>Data type: </TagLabel>
|
||||||
</TagContainer>
|
<TagValue>{dataType}</TagValue>
|
||||||
|
</TagContainer>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</SelectOptionContainer>
|
</Tooltip>
|
||||||
) : (
|
) : (
|
||||||
<span>{label}</span>
|
<Tooltip title={label} placement="topLeft">
|
||||||
|
<span>{label}</span>
|
||||||
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</span>
|
</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 {
|
.lightMode {
|
||||||
.query-builder-search {
|
.query-builder-search {
|
||||||
.ant-select-dropdown {
|
.ant-select-dropdown {
|
||||||
|
@ -16,19 +16,11 @@ export const StyledCheckOutlined = styled(CheckOutlined)`
|
|||||||
float: right;
|
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)`
|
export const TagContainer = styled(Tag)`
|
||||||
&&& {
|
&&& {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 0.3rem 0.3rem;
|
padding: 0.1rem 0.2rem;
|
||||||
font-weight: 400;
|
font-weight: 300;
|
||||||
font-size: 0.6rem;
|
font-size: 0.6rem;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { WarningFilled } from '@ant-design/icons';
|
import { WarningFilled } from '@ant-design/icons';
|
||||||
import { Flex, Typography } from 'antd';
|
import { Flex, Typography } from 'antd';
|
||||||
import { ResizeTable } from 'components/ResizeTable';
|
import { ResizeTable } from 'components/ResizeTable';
|
||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { MAX_RPS_LIMIT } from 'constants/global';
|
import { MAX_RPS_LIMIT } from 'constants/global';
|
||||||
import ResourceAttributesFilter from 'container/ResourceAttributesFilter';
|
import ResourceAttributesFilter from 'container/ResourceAttributesFilter';
|
||||||
import { useGetQueriesRange } from 'hooks/queryBuilder/useGetQueriesRange';
|
import { useGetQueriesRange } from 'hooks/queryBuilder/useGetQueriesRange';
|
||||||
@ -35,22 +36,26 @@ function ServiceMetricTable({
|
|||||||
const { data: licenseData, isFetching } = useLicense();
|
const { data: licenseData, isFetching } = useLicense();
|
||||||
const isCloudUserVal = isCloudUser();
|
const isCloudUserVal = isCloudUser();
|
||||||
|
|
||||||
const queries = useGetQueriesRange(queryRangeRequestData, {
|
const queries = useGetQueriesRange(
|
||||||
queryKey: [
|
queryRangeRequestData,
|
||||||
`GetMetricsQueryRange-${queryRangeRequestData[0].selectedTime}-${globalSelectedInterval}`,
|
DEFAULT_ENTITY_VERSION,
|
||||||
maxTime,
|
{
|
||||||
minTime,
|
queryKey: [
|
||||||
globalSelectedInterval,
|
`GetMetricsQueryRange-${queryRangeRequestData[0].selectedTime}-${globalSelectedInterval}`,
|
||||||
],
|
maxTime,
|
||||||
keepPreviousData: true,
|
minTime,
|
||||||
enabled: true,
|
globalSelectedInterval,
|
||||||
refetchOnMount: false,
|
],
|
||||||
onError: (error) => {
|
keepPreviousData: true,
|
||||||
notifications.error({
|
enabled: true,
|
||||||
message: error.message,
|
refetchOnMount: false,
|
||||||
});
|
onError: (error) => {
|
||||||
|
notifications.error({
|
||||||
|
message: error.message,
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
);
|
||||||
|
|
||||||
const isLoading = queries.some((query) => query.isLoading);
|
const isLoading = queries.some((query) => query.isLoading);
|
||||||
const services: ServicesList[] = useMemo(
|
const services: ServicesList[] = useMemo(
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||||
@ -49,6 +50,7 @@ function TimeSeriesViewContainer({
|
|||||||
dataSource,
|
dataSource,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
DEFAULT_ENTITY_VERSION,
|
||||||
{
|
{
|
||||||
queryKey: [
|
queryKey: [
|
||||||
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
||||||
|
@ -49,13 +49,16 @@ export const getTraceToLogsQuery = (
|
|||||||
limit: null,
|
limit: null,
|
||||||
aggregateAttribute: initialAutocompleteData,
|
aggregateAttribute: initialAutocompleteData,
|
||||||
aggregateOperator: LogsAggregatorOperator.NOOP,
|
aggregateOperator: LogsAggregatorOperator.NOOP,
|
||||||
|
timeAggregation: '',
|
||||||
|
spaceAggregation: '',
|
||||||
|
functions: [],
|
||||||
expression: 'A',
|
expression: 'A',
|
||||||
groupBy: [],
|
groupBy: [],
|
||||||
having: [],
|
having: [],
|
||||||
legend: '',
|
legend: '',
|
||||||
orderBy: [],
|
orderBy: [],
|
||||||
queryName: 'A',
|
queryName: 'A',
|
||||||
reduceTo: 'min',
|
reduceTo: 'avg',
|
||||||
stepInterval: getStep({
|
stepInterval: getStep({
|
||||||
start: minTime,
|
start: minTime,
|
||||||
end: maxTime,
|
end: maxTime,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { ResizeTable } from 'components/ResizeTable';
|
import { ResizeTable } from 'components/ResizeTable';
|
||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
@ -62,6 +63,7 @@ function ListView(): JSX.Element {
|
|||||||
selectColumns: options?.selectColumns,
|
selectColumns: options?.selectColumns,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
DEFAULT_ENTITY_VERSION,
|
||||||
{
|
{
|
||||||
queryKey: [
|
queryKey: [
|
||||||
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
||||||
|
@ -53,6 +53,7 @@ function QuerySection(): JSX.Element {
|
|||||||
}}
|
}}
|
||||||
filterConfigs={filterConfigs}
|
filterConfigs={filterConfigs}
|
||||||
queryComponents={queryComponents}
|
queryComponents={queryComponents}
|
||||||
|
version="v3" // setting this to v3 as we this is rendered in logs explorer
|
||||||
actions={
|
actions={
|
||||||
<ButtonWrapper>
|
<ButtonWrapper>
|
||||||
<Button onClick={handleRunQuery} type="primary">
|
<Button onClick={handleRunQuery} type="primary">
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Space } from 'antd';
|
import { Space } from 'antd';
|
||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||||
import { QueryTable } from 'container/QueryTable';
|
import { QueryTable } from 'container/QueryTable';
|
||||||
@ -27,6 +28,7 @@ function TableView(): JSX.Element {
|
|||||||
dataSource: 'traces',
|
dataSource: 'traces',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
DEFAULT_ENTITY_VERSION,
|
||||||
{
|
{
|
||||||
queryKey: [
|
queryKey: [
|
||||||
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import { ResizeTable } from 'components/ResizeTable';
|
import { ResizeTable } from 'components/ResizeTable';
|
||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||||
@ -41,6 +42,7 @@ function TracesView(): JSX.Element {
|
|||||||
pagination: paginationQueryData,
|
pagination: paginationQueryData,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
DEFAULT_ENTITY_VERSION,
|
||||||
{
|
{
|
||||||
queryKey: [
|
queryKey: [
|
||||||
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
||||||
|
@ -1,65 +1,73 @@
|
|||||||
.traces-table {
|
.traces-table {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.resize-table {
|
.resize-table {
|
||||||
height: calc(90% - 5px);
|
height: calc(100% - 40px);
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-table-wrapper .ant-table-thead > tr > th {
|
&::-webkit-scrollbar {
|
||||||
font-family: Inter;
|
width: 0.2rem;
|
||||||
color: var(--bg-vanilla-100);
|
height: 0.2rem;
|
||||||
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 {
|
.ant-table-wrapper .ant-table-tbody > tr > td {
|
||||||
display: none;
|
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 {
|
.ant-table-wrapper .ant-table-thead > tr > th {
|
||||||
position: absolute;
|
font-family: Inter;
|
||||||
bottom: 5px;
|
color: var(--bg-vanilla-100);
|
||||||
right: 10px;
|
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 {
|
.lightMode {
|
||||||
.traces-table {
|
.traces-table {
|
||||||
.resize-table {
|
.resize-table {
|
||||||
.ant-table-wrapper .ant-table-tbody >tr >td {
|
.ant-table-wrapper .ant-table-tbody > tr > td {
|
||||||
background-color: var(--bg-vanilla-100);
|
background-color: var(--bg-vanilla-100);
|
||||||
color: var(--bg-ink-500);
|
color: var(--bg-ink-500);
|
||||||
border-color: rgba(0, 0, 0, 0.06);
|
border-color: rgba(0, 0, 0, 0.06);
|
||||||
}
|
}
|
||||||
.ant-table-wrapper .ant-table-thead > tr > th {
|
.ant-table-wrapper .ant-table-thead > tr > th {
|
||||||
background-color: var(--bg-vanilla-300);
|
background-color: var(--bg-vanilla-300);
|
||||||
color: var(--bg-ink-500);
|
color: var(--bg-ink-500);
|
||||||
border-color: rgba(0, 0, 0, 0.06);
|
border-color: rgba(0, 0, 0, 0.06);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
|||||||
function TracesTableComponent({
|
function TracesTableComponent({
|
||||||
selectedTracesFields,
|
selectedTracesFields,
|
||||||
query,
|
query,
|
||||||
|
version,
|
||||||
selectedTime,
|
selectedTime,
|
||||||
}: TracesTableComponentProps): JSX.Element {
|
}: TracesTableComponentProps): JSX.Element {
|
||||||
const { selectedTime: globalSelectedTime, maxTime, minTime } = useSelector<
|
const { selectedTime: globalSelectedTime, maxTime, minTime } = useSelector<
|
||||||
@ -59,6 +60,7 @@ function TracesTableComponent({
|
|||||||
},
|
},
|
||||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||||
},
|
},
|
||||||
|
version,
|
||||||
{
|
{
|
||||||
queryKey: [
|
queryKey: [
|
||||||
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
||||||
@ -160,6 +162,7 @@ function TracesTableComponent({
|
|||||||
export type TracesTableComponentProps = {
|
export type TracesTableComponentProps = {
|
||||||
selectedTracesFields: Widgets['selectedTracesFields'];
|
selectedTracesFields: Widgets['selectedTracesFields'];
|
||||||
query: Query;
|
query: Query;
|
||||||
|
version: string;
|
||||||
selectedTime?: timePreferance;
|
selectedTime?: timePreferance;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -72,6 +72,9 @@ function KeyboardHotkeysProvider({
|
|||||||
shortcutKey = shortcutKey + isAltKey + isShiftKey + isMetaKey;
|
shortcutKey = shortcutKey + isAltKey + isShiftKey + isMetaKey;
|
||||||
|
|
||||||
if (shortcuts.current[shortcutKey]) {
|
if (shortcuts.current[shortcutKey]) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
|
||||||
shortcuts.current[shortcutKey]();
|
shortcuts.current[shortcutKey]();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { getQueryRangeFormat } from 'api/dashboard/queryRangeFormat';
|
import { getQueryRangeFormat } from 'api/dashboard/queryRangeFormat';
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
@ -45,7 +46,9 @@ const useCreateAlerts = (widget?: Widgets): VoidFunction => {
|
|||||||
history.push(
|
history.push(
|
||||||
`${ROUTES.ALERTS_NEW}?${QueryParams.compositeQuery}=${encodeURIComponent(
|
`${ROUTES.ALERTS_NEW}?${QueryParams.compositeQuery}=${encodeURIComponent(
|
||||||
JSON.stringify(updatedQuery),
|
JSON.stringify(updatedQuery),
|
||||||
)}&${QueryParams.panelTypes}=${widget.panelTypes}`,
|
)}&${QueryParams.panelTypes}=${widget.panelTypes}&version=${
|
||||||
|
selectedDashboard?.data.version || DEFAULT_ENTITY_VERSION
|
||||||
|
}`,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: () => {
|
||||||
@ -59,6 +62,7 @@ const useCreateAlerts = (widget?: Widgets): VoidFunction => {
|
|||||||
notifications,
|
notifications,
|
||||||
queryRangeMutation,
|
queryRangeMutation,
|
||||||
selectedDashboard?.data.variables,
|
selectedDashboard?.data.variables,
|
||||||
|
selectedDashboard?.data.version,
|
||||||
widget,
|
widget,
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
@ -15,6 +15,7 @@ import { useQueryBuilder } from './useQueryBuilder';
|
|||||||
export const useGetExplorerQueryRange = (
|
export const useGetExplorerQueryRange = (
|
||||||
requestData: Query | null,
|
requestData: Query | null,
|
||||||
panelType: PANEL_TYPES | null,
|
panelType: PANEL_TYPES | null,
|
||||||
|
version: string,
|
||||||
options?: UseQueryOptions<SuccessResponse<MetricRangePayloadProps>, Error>,
|
options?: UseQueryOptions<SuccessResponse<MetricRangePayloadProps>, Error>,
|
||||||
params?: Record<string, unknown>,
|
params?: Record<string, unknown>,
|
||||||
isDependentOnQB = true,
|
isDependentOnQB = true,
|
||||||
@ -47,6 +48,7 @@ export const useGetExplorerQueryRange = (
|
|||||||
query: requestData || initialQueriesMap.metrics,
|
query: requestData || initialQueriesMap.metrics,
|
||||||
params,
|
params,
|
||||||
},
|
},
|
||||||
|
version,
|
||||||
{
|
{
|
||||||
...options,
|
...options,
|
||||||
retry: false,
|
retry: false,
|
||||||
|
@ -15,6 +15,7 @@ import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
|||||||
|
|
||||||
export const useGetQueriesRange = (
|
export const useGetQueriesRange = (
|
||||||
requestData: GetQueryResultsProps[],
|
requestData: GetQueryResultsProps[],
|
||||||
|
version: string,
|
||||||
options: UseQueryOptions<SuccessResponse<MetricRangePayloadProps>, Error>,
|
options: UseQueryOptions<SuccessResponse<MetricRangePayloadProps>, Error>,
|
||||||
): UseQueryResult<SuccessResponse<MetricRangePayloadProps>, Error>[] => {
|
): UseQueryResult<SuccessResponse<MetricRangePayloadProps>, Error>[] => {
|
||||||
const queryKey = useMemo(() => {
|
const queryKey = useMemo(() => {
|
||||||
@ -26,7 +27,7 @@ export const useGetQueriesRange = (
|
|||||||
|
|
||||||
const queryData = requestData.map((request, index) => ({
|
const queryData = requestData.map((request, index) => ({
|
||||||
queryFn: async (): Promise<SuccessResponse<MetricRangePayloadProps>> =>
|
queryFn: async (): Promise<SuccessResponse<MetricRangePayloadProps>> =>
|
||||||
GetMetricQueryRange(request),
|
GetMetricQueryRange(request, version),
|
||||||
...options,
|
...options,
|
||||||
queryKey: [...queryKey, index] as QueryKey,
|
queryKey: [...queryKey, index] as QueryKey,
|
||||||
}));
|
}));
|
||||||
|
@ -11,10 +11,15 @@ import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
|||||||
|
|
||||||
type UseGetQueryRange = (
|
type UseGetQueryRange = (
|
||||||
requestData: GetQueryResultsProps,
|
requestData: GetQueryResultsProps,
|
||||||
|
version: string,
|
||||||
options?: UseQueryOptions<SuccessResponse<MetricRangePayloadProps>, Error>,
|
options?: UseQueryOptions<SuccessResponse<MetricRangePayloadProps>, Error>,
|
||||||
) => UseQueryResult<SuccessResponse<MetricRangePayloadProps>, Error>;
|
) => UseQueryResult<SuccessResponse<MetricRangePayloadProps>, Error>;
|
||||||
|
|
||||||
export const useGetQueryRange: UseGetQueryRange = (requestData, options) => {
|
export const useGetQueryRange: UseGetQueryRange = (
|
||||||
|
requestData,
|
||||||
|
version,
|
||||||
|
options,
|
||||||
|
) => {
|
||||||
const newRequestData: GetQueryResultsProps = useMemo(
|
const newRequestData: GetQueryResultsProps = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...requestData,
|
...requestData,
|
||||||
@ -39,7 +44,8 @@ export const useGetQueryRange: UseGetQueryRange = (requestData, options) => {
|
|||||||
}, [options?.queryKey, newRequestData]);
|
}, [options?.queryKey, newRequestData]);
|
||||||
|
|
||||||
return useQuery<SuccessResponse<MetricRangePayloadProps>, Error>({
|
return useQuery<SuccessResponse<MetricRangePayloadProps>, Error>({
|
||||||
queryFn: async ({ signal }) => GetMetricQueryRange(newRequestData, signal),
|
queryFn: async ({ signal }) =>
|
||||||
|
GetMetricQueryRange(requestData, version, signal),
|
||||||
...options,
|
...options,
|
||||||
queryKey,
|
queryKey,
|
||||||
});
|
});
|
||||||
|
@ -18,6 +18,7 @@ export const useGetWidgetQueryRange = (
|
|||||||
graphType,
|
graphType,
|
||||||
selectedTime,
|
selectedTime,
|
||||||
}: Pick<GetQueryResultsProps, 'graphType' | 'selectedTime'>,
|
}: Pick<GetQueryResultsProps, 'graphType' | 'selectedTime'>,
|
||||||
|
version: string,
|
||||||
options?: UseQueryOptions<SuccessResponse<MetricRangePayloadProps>, Error>,
|
options?: UseQueryOptions<SuccessResponse<MetricRangePayloadProps>, Error>,
|
||||||
): UseQueryResult<SuccessResponse<MetricRangePayloadProps>, Error> => {
|
): UseQueryResult<SuccessResponse<MetricRangePayloadProps>, Error> => {
|
||||||
const { selectedTime: globalSelectedInterval } = useSelector<
|
const { selectedTime: globalSelectedInterval } = useSelector<
|
||||||
@ -37,6 +38,7 @@ export const useGetWidgetQueryRange = (
|
|||||||
query: stagedQuery || initialQueriesMap.metrics,
|
query: stagedQuery || initialQueriesMap.metrics,
|
||||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||||
},
|
},
|
||||||
|
version,
|
||||||
{
|
{
|
||||||
enabled: !!stagedQuery,
|
enabled: !!stagedQuery,
|
||||||
retry: false,
|
retry: false,
|
||||||
|
@ -1,16 +1,23 @@
|
|||||||
import { LEGEND } from 'constants/global';
|
import { LEGEND } from 'constants/global';
|
||||||
import {
|
import {
|
||||||
|
ATTRIBUTE_TYPES,
|
||||||
initialAutocompleteData,
|
initialAutocompleteData,
|
||||||
initialQueryBuilderFormValuesMap,
|
initialQueryBuilderFormValuesMap,
|
||||||
mapOfFormulaToFilters,
|
mapOfFormulaToFilters,
|
||||||
mapOfQueryFilters,
|
mapOfQueryFilters,
|
||||||
PANEL_TYPES,
|
PANEL_TYPES,
|
||||||
} from 'constants/queryBuilder';
|
} from 'constants/queryBuilder';
|
||||||
|
import {
|
||||||
|
metricsGaugeSpaceAggregateOperatorOptions,
|
||||||
|
metricsHistogramSpaceAggregateOperatorOptions,
|
||||||
|
metricsSumSpaceAggregateOperatorOptions,
|
||||||
|
} from 'constants/queryBuilderOperators';
|
||||||
import {
|
import {
|
||||||
listViewInitialLogQuery,
|
listViewInitialLogQuery,
|
||||||
listViewInitialTraceQuery,
|
listViewInitialTraceQuery,
|
||||||
} from 'container/NewDashboard/ComponentsSlider/constants';
|
} from 'container/NewDashboard/ComponentsSlider/constants';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
|
import { getMetricsOperatorsByAttributeType } from 'lib/newQueryBuilder/getMetricsOperatorsByAttributeType';
|
||||||
import { getOperatorsBySourceAndPanelType } from 'lib/newQueryBuilder/getOperatorsBySourceAndPanelType';
|
import { getOperatorsBySourceAndPanelType } from 'lib/newQueryBuilder/getOperatorsBySourceAndPanelType';
|
||||||
import { findDataTypeOfOperator } from 'lib/query/findDataTypeOfOperator';
|
import { findDataTypeOfOperator } from 'lib/query/findDataTypeOfOperator';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
@ -18,13 +25,14 @@ import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteRe
|
|||||||
import {
|
import {
|
||||||
IBuilderFormula,
|
IBuilderFormula,
|
||||||
IBuilderQuery,
|
IBuilderQuery,
|
||||||
|
QueryFunctionProps,
|
||||||
} from 'types/api/queryBuilder/queryBuilderData';
|
} from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import {
|
import {
|
||||||
HandleChangeFormulaData,
|
HandleChangeFormulaData,
|
||||||
HandleChangeQueryData,
|
HandleChangeQueryData,
|
||||||
UseQueryOperations,
|
UseQueryOperations,
|
||||||
} from 'types/common/operations.types';
|
} 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 { SelectOption } from 'types/common/select';
|
||||||
import { getFormatedLegend } from 'utils/getFormatedLegend';
|
import { getFormatedLegend } from 'utils/getFormatedLegend';
|
||||||
|
|
||||||
@ -34,6 +42,7 @@ export const useQueryOperations: UseQueryOperations = ({
|
|||||||
filterConfigs,
|
filterConfigs,
|
||||||
formula,
|
formula,
|
||||||
isListViewPanel = false,
|
isListViewPanel = false,
|
||||||
|
entityVersion,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
handleSetQueryData,
|
handleSetQueryData,
|
||||||
@ -46,6 +55,9 @@ export const useQueryOperations: UseQueryOperations = ({
|
|||||||
} = useQueryBuilder();
|
} = useQueryBuilder();
|
||||||
|
|
||||||
const [operators, setOperators] = useState<SelectOption<string, string>[]>([]);
|
const [operators, setOperators] = useState<SelectOption<string, string>[]>([]);
|
||||||
|
const [spaceAggregationOptions, setSpaceAggregationOptions] = useState<
|
||||||
|
SelectOption<string, string>[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
const { dataSource, aggregateOperator } = query;
|
const { dataSource, aggregateOperator } = query;
|
||||||
|
|
||||||
@ -104,6 +116,7 @@ export const useQueryOperations: UseQueryOperations = ({
|
|||||||
const newQuery: IBuilderQuery = {
|
const newQuery: IBuilderQuery = {
|
||||||
...query,
|
...query,
|
||||||
aggregateOperator: value,
|
aggregateOperator: value,
|
||||||
|
timeAggregation: value,
|
||||||
having: [],
|
having: [],
|
||||||
limit: null,
|
limit: null,
|
||||||
...(shouldResetAggregateAttribute
|
...(shouldResetAggregateAttribute
|
||||||
@ -116,6 +129,52 @@ export const useQueryOperations: UseQueryOperations = ({
|
|||||||
[index, query, handleSetQueryData],
|
[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(
|
const handleChangeAggregatorAttribute = useCallback(
|
||||||
(value: BaseAutocompleteData): void => {
|
(value: BaseAutocompleteData): void => {
|
||||||
const newQuery: IBuilderQuery = {
|
const newQuery: IBuilderQuery = {
|
||||||
@ -124,9 +183,31 @@ export const useQueryOperations: UseQueryOperations = ({
|
|||||||
having: [],
|
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);
|
handleSetQueryData(index, newQuery);
|
||||||
},
|
},
|
||||||
[index, query, handleSetQueryData],
|
[
|
||||||
|
query,
|
||||||
|
entityVersion,
|
||||||
|
handleSetQueryData,
|
||||||
|
index,
|
||||||
|
handleMetricAggregateAtributeTypes,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChangeDataSource = useCallback(
|
const handleChangeDataSource = useCallback(
|
||||||
@ -203,6 +284,21 @@ export const useQueryOperations: UseQueryOperations = ({
|
|||||||
[formula, handleSetFormulaData, index],
|
[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 isMetricsDataSource = query.dataSource === DataSource.METRICS;
|
||||||
|
|
||||||
const isTracePanelType = panelType === PANEL_TYPES.TRACE;
|
const isTracePanelType = panelType === PANEL_TYPES.TRACE;
|
||||||
@ -210,15 +306,26 @@ export const useQueryOperations: UseQueryOperations = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialDataSource && dataSource !== initialDataSource) return;
|
if (initialDataSource && dataSource !== initialDataSource) return;
|
||||||
|
|
||||||
const initialOperators = getOperatorsBySourceAndPanelType({
|
if (
|
||||||
dataSource,
|
dataSource === DataSource.METRICS &&
|
||||||
panelType: panelType || PANEL_TYPES.TIME_SERIES,
|
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);
|
setOperators(initialOperators);
|
||||||
}, [dataSource, initialDataSource, panelType, operators]);
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [dataSource, initialDataSource, panelType, operators, entityVersion]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const additionalFilters = getNewListOfAdditionalFilters(dataSource, true);
|
const additionalFilters = getNewListOfAdditionalFilters(dataSource, true);
|
||||||
@ -236,13 +343,16 @@ export const useQueryOperations: UseQueryOperations = ({
|
|||||||
isTracePanelType,
|
isTracePanelType,
|
||||||
isMetricsDataSource,
|
isMetricsDataSource,
|
||||||
operators,
|
operators,
|
||||||
|
spaceAggregationOptions,
|
||||||
listOfAdditionalFilters,
|
listOfAdditionalFilters,
|
||||||
handleChangeOperator,
|
handleChangeOperator,
|
||||||
|
handleSpaceAggregationChange,
|
||||||
handleChangeAggregatorAttribute,
|
handleChangeAggregatorAttribute,
|
||||||
handleChangeDataSource,
|
handleChangeDataSource,
|
||||||
handleDeleteQuery,
|
handleDeleteQuery,
|
||||||
handleChangeQueryData,
|
handleChangeQueryData,
|
||||||
listOfAdditionalFormulaFilters,
|
listOfAdditionalFormulaFilters,
|
||||||
handleChangeFormulaData,
|
handleChangeFormulaData,
|
||||||
|
handleQueryFunctionsUpdates,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import {
|
import {
|
||||||
initialQueryBuilderFormValues,
|
initialQueryBuilderFormValues,
|
||||||
@ -126,6 +127,7 @@ export const useLogsData = ({
|
|||||||
const { data, isFetching } = useGetExplorerQueryRange(
|
const { data, isFetching } = useGetExplorerQueryRange(
|
||||||
requestData,
|
requestData,
|
||||||
panelType,
|
panelType,
|
||||||
|
DEFAULT_ENTITY_VERSION,
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
keepPreviousData: true,
|
||||||
enabled: !isLimit && !!requestData,
|
enabled: !isLimit && !!requestData,
|
||||||
|
@ -18,11 +18,16 @@ import { prepareQueryRangePayload } from './prepareQueryRangePayload';
|
|||||||
|
|
||||||
export async function GetMetricQueryRange(
|
export async function GetMetricQueryRange(
|
||||||
props: GetQueryResultsProps,
|
props: GetQueryResultsProps,
|
||||||
|
version: string,
|
||||||
signal?: AbortSignal,
|
signal?: AbortSignal,
|
||||||
): Promise<SuccessResponse<MetricRangePayloadProps>> {
|
): Promise<SuccessResponse<MetricRangePayloadProps>> {
|
||||||
const { legendMap, queryPayload } = prepareQueryRangePayload(props);
|
const { legendMap, queryPayload } = prepareQueryRangePayload(props);
|
||||||
|
|
||||||
const response = await getMetricsQueryRange(queryPayload, signal);
|
const response = await getMetricsQueryRange(
|
||||||
|
queryPayload,
|
||||||
|
version || 'v3',
|
||||||
|
signal,
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode >= 400) {
|
if (response.statusCode >= 400) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -144,8 +144,6 @@ export const parseQuery = (queryString) => {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(parsedRaw);
|
|
||||||
return 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;
|
value: number;
|
||||||
tooltipValue: string;
|
tooltipValue: string;
|
||||||
textContent: string;
|
textContent: string;
|
||||||
|
queryName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateTooltipContent = (
|
const generateTooltipContent = (
|
||||||
@ -35,6 +36,7 @@ const generateTooltipContent = (
|
|||||||
|
|
||||||
let tooltipTitle = '';
|
let tooltipTitle = '';
|
||||||
const formattedData: Record<string, UplotTooltipDataProps> = {};
|
const formattedData: Record<string, UplotTooltipDataProps> = {};
|
||||||
|
const duplicatedLegendLabels: Record<string, true> = {};
|
||||||
|
|
||||||
function sortTooltipContentBasedOnValue(
|
function sortTooltipContentBasedOnValue(
|
||||||
tooltipDataObj: Record<string, UplotTooltipDataProps>,
|
tooltipDataObj: Record<string, UplotTooltipDataProps>,
|
||||||
@ -57,8 +59,29 @@ const generateTooltipContent = (
|
|||||||
|
|
||||||
const color = generateColor(label, themeColors.chartcolors);
|
const color = generateColor(label, themeColors.chartcolors);
|
||||||
|
|
||||||
|
let tooltipItemLabel = label;
|
||||||
|
|
||||||
if (Number.isFinite(value)) {
|
if (Number.isFinite(value)) {
|
||||||
const tooltipValue = getToolTipValue(value, yAxisUnit);
|
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 = {
|
const dataObj = {
|
||||||
show: item.show || false,
|
show: item.show || false,
|
||||||
@ -69,11 +92,13 @@ const generateTooltipContent = (
|
|||||||
focus: item?._focus || false,
|
focus: item?._focus || false,
|
||||||
value,
|
value,
|
||||||
tooltipValue,
|
tooltipValue,
|
||||||
textContent: `${label} : ${tooltipValue}`,
|
queryName,
|
||||||
|
textContent: `${tooltipItemLabel} : ${tooltipValue}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
tooltipCount += 1;
|
tooltipCount += 1;
|
||||||
formattedData[label] = dataObj;
|
|
||||||
|
formattedData[tooltipItemLabel] = dataObj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -224,7 +224,6 @@ const appReducer = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
case UPDATE_USER_FLAG: {
|
case UPDATE_USER_FLAG: {
|
||||||
console.log('herei n update user flag');
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
userFlags: { ...state.userFlags, ...action.payload.flags },
|
userFlags: { ...state.userFlags, ...action.payload.flags },
|
||||||
|
@ -22,6 +22,7 @@ export interface AlertDef {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
preferredChannels?: string[];
|
preferredChannels?: string[];
|
||||||
broadcastToAll?: boolean;
|
broadcastToAll?: boolean;
|
||||||
|
version?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RuleCondition {
|
export interface RuleCondition {
|
||||||
|
@ -4,6 +4,7 @@ export type Props =
|
|||||||
| {
|
| {
|
||||||
title: Dashboard['data']['title'];
|
title: Dashboard['data']['title'];
|
||||||
uploadedGrafana: boolean;
|
uploadedGrafana: boolean;
|
||||||
|
version?: string;
|
||||||
}
|
}
|
||||||
| { DashboardData: DashboardData; uploadedGrafana: boolean };
|
| { DashboardData: DashboardData; uploadedGrafana: boolean };
|
||||||
|
|
||||||
|
@ -62,6 +62,7 @@ export interface DashboardData {
|
|||||||
title: string;
|
title: string;
|
||||||
layout?: Layout[];
|
layout?: Layout[];
|
||||||
variables: Record<string, IDashboardVariable>;
|
variables: Record<string, IDashboardVariable>;
|
||||||
|
version?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IBaseWidget {
|
export interface IBaseWidget {
|
||||||
|
@ -47,12 +47,20 @@ export type OrderByPayload = {
|
|||||||
order: string;
|
order: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface QueryFunctionProps {
|
||||||
|
name: string;
|
||||||
|
args: string[];
|
||||||
|
}
|
||||||
|
|
||||||
// Type for query builder
|
// Type for query builder
|
||||||
export type IBuilderQuery = {
|
export type IBuilderQuery = {
|
||||||
queryName: string;
|
queryName: string;
|
||||||
dataSource: DataSource;
|
dataSource: DataSource;
|
||||||
aggregateOperator: string;
|
aggregateOperator: string;
|
||||||
aggregateAttribute: BaseAutocompleteData;
|
aggregateAttribute: BaseAutocompleteData;
|
||||||
|
timeAggregation: string;
|
||||||
|
spaceAggregation?: string;
|
||||||
|
functions: QueryFunctionProps[];
|
||||||
filters: TagFilter;
|
filters: TagFilter;
|
||||||
groupBy: BaseAutocompleteData[];
|
groupBy: BaseAutocompleteData[];
|
||||||
expression: string;
|
expression: string;
|
||||||
|
@ -4,6 +4,7 @@ import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteRe
|
|||||||
import {
|
import {
|
||||||
IBuilderFormula,
|
IBuilderFormula,
|
||||||
IBuilderQuery,
|
IBuilderQuery,
|
||||||
|
QueryFunctionProps,
|
||||||
} from 'types/api/queryBuilder/queryBuilderData';
|
} from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
@ -13,6 +14,7 @@ type UseQueryOperationsParams = Pick<QueryProps, 'index' | 'query'> &
|
|||||||
Pick<QueryBuilderProps, 'filterConfigs'> & {
|
Pick<QueryBuilderProps, 'filterConfigs'> & {
|
||||||
formula?: IBuilderFormula;
|
formula?: IBuilderFormula;
|
||||||
isListViewPanel?: boolean;
|
isListViewPanel?: boolean;
|
||||||
|
entityVersion: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type HandleChangeQueryData = <
|
export type HandleChangeQueryData = <
|
||||||
@ -37,12 +39,15 @@ export type UseQueryOperations = (
|
|||||||
isTracePanelType: boolean;
|
isTracePanelType: boolean;
|
||||||
isMetricsDataSource: boolean;
|
isMetricsDataSource: boolean;
|
||||||
operators: SelectOption<string, string>[];
|
operators: SelectOption<string, string>[];
|
||||||
|
spaceAggregationOptions: SelectOption<string, string>[];
|
||||||
listOfAdditionalFilters: string[];
|
listOfAdditionalFilters: string[];
|
||||||
handleChangeOperator: (value: string) => void;
|
handleChangeOperator: (value: string) => void;
|
||||||
|
handleSpaceAggregationChange: (value: string) => void;
|
||||||
handleChangeAggregatorAttribute: (value: BaseAutocompleteData) => void;
|
handleChangeAggregatorAttribute: (value: BaseAutocompleteData) => void;
|
||||||
handleChangeDataSource: (newSource: DataSource) => void;
|
handleChangeDataSource: (newSource: DataSource) => void;
|
||||||
handleDeleteQuery: () => void;
|
handleDeleteQuery: () => void;
|
||||||
handleChangeQueryData: HandleChangeQueryData;
|
handleChangeQueryData: HandleChangeQueryData;
|
||||||
handleChangeFormulaData: HandleChangeFormulaData;
|
handleChangeFormulaData: HandleChangeFormulaData;
|
||||||
|
handleQueryFunctionsUpdates: (functions: QueryFunctionProps[]) => void;
|
||||||
listOfAdditionalFormulaFilters: string[];
|
listOfAdditionalFormulaFilters: string[];
|
||||||
};
|
};
|
||||||
|
@ -92,6 +92,8 @@ export enum MetricAggregateOperator {
|
|||||||
HIST_QUANTILE_90 = 'hist_quantile_90',
|
HIST_QUANTILE_90 = 'hist_quantile_90',
|
||||||
HIST_QUANTILE_95 = 'hist_quantile_95',
|
HIST_QUANTILE_95 = 'hist_quantile_95',
|
||||||
HIST_QUANTILE_99 = 'hist_quantile_99',
|
HIST_QUANTILE_99 = 'hist_quantile_99',
|
||||||
|
INCREASE = 'increase',
|
||||||
|
LATEST = 'latest',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum TracesAggregatorOperator {
|
export enum TracesAggregatorOperator {
|
||||||
@ -142,6 +144,24 @@ export enum LogsAggregatorOperator {
|
|||||||
RATE_MAX = 'rate_max',
|
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 =
|
export type PanelTypeKeys =
|
||||||
| 'TIME_SERIES'
|
| 'TIME_SERIES'
|
||||||
| 'VALUE'
|
| 'VALUE'
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Page } from '@playwright/test';
|
import { Page } from '@playwright/test';
|
||||||
|
|
||||||
import { JsonApplicationType } from '../fixtures/constant';
|
import { JsonApplicationType } from '../fixtures/constant';
|
||||||
|
|
||||||
// API endpoints
|
// API endpoints
|
||||||
@ -41,6 +42,99 @@ export const timeSeriesGraphName = 'Time1';
|
|||||||
|
|
||||||
let widgetsId: string;
|
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
|
// mock API calls
|
||||||
export const dashboardsListAndCreate = async (
|
export const dashboardsListAndCreate = async (
|
||||||
page: Page,
|
page: Page,
|
||||||
@ -76,7 +170,8 @@ export const getTimeSeriesQueryData = async (
|
|||||||
page: Page,
|
page: Page,
|
||||||
response: any,
|
response: any,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
await page.route(`**/${queryRangeApiEndpoint}`, (route) =>
|
// eslint-disable-next-line sonarjs/no-identical-functions
|
||||||
|
await page.route(`**/${queryRangeApiEndpoint}`, (route): any =>
|
||||||
route.fulfill({
|
route.fulfill({
|
||||||
status: 200,
|
status: 200,
|
||||||
contentType: JsonApplicationType,
|
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,
|
"limit": null,
|
||||||
"orderBy": [],
|
"orderBy": [],
|
||||||
"queryName": "A",
|
"queryName": "A",
|
||||||
"reduceTo": "sum",
|
"reduceTo": "avg",
|
||||||
"stepInterval": 60
|
"stepInterval": 60
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
"limit": null,
|
"limit": null,
|
||||||
"orderBy": [],
|
"orderBy": [],
|
||||||
"queryName": "A",
|
"queryName": "A",
|
||||||
"reduceTo": "sum",
|
"reduceTo": "avg",
|
||||||
"stepInterval": 60
|
"stepInterval": 60
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
"order": "desc"
|
"order": "desc"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"reduceTo": "sum"
|
"reduceTo": "avg"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"panelType": "table",
|
"panelType": "table",
|
||||||
|
2
go.mod
2
go.mod
@ -203,4 +203,4 @@ require (
|
|||||||
k8s.io/utils v0.0.0-20230711102312-30195339c3c7 // indirect
|
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/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 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
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 h1:Bk43AsDYe0fhkbj57eGXx8H3ZJ4zhmQXBnrW523ktj8=
|
||||||
github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd/go.mod h1:nxRcH/OEdM8QxzH37xkGzomr1O0JpYBRS6pwjsWW6Pc=
|
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.79 h1:RScpt9CUyOC4KQgzEUXRZ9lXHUdFT1eYAsFY3zlqPFM=
|
||||||
github.com/SigNoz/prometheus v1.9.78/go.mod h1:MffmFu2qFILQrOHehx3D0XjYtaZMVfI+Ppeiv98x4Ww=
|
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 h1:UwkVi1o2NY9gRgCLBtWVKr+UDxb4FaTs63Sb20qgf8w=
|
||||||
github.com/SigNoz/signoz-otel-collector v0.88.12/go.mod h1:RH9OEjni6tkh9RgN/meSPxv3kykjcFscqMwJgbUAXmo=
|
github.com/SigNoz/signoz-otel-collector v0.88.12/go.mod h1:RH9OEjni6tkh9RgN/meSPxv3kykjcFscqMwJgbUAXmo=
|
||||||
github.com/SigNoz/zap_otlp v0.1.0 h1:T7rRcFN87GavY8lDGZj0Z3Xv6OhJA6Pj3I9dNPmqvRc=
|
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/logs"
|
||||||
"go.signoz.io/signoz/pkg/query-service/app/services"
|
"go.signoz.io/signoz/pkg/query-service/app/services"
|
||||||
"go.signoz.io/signoz/pkg/query-service/auth"
|
"go.signoz.io/signoz/pkg/query-service/auth"
|
||||||
|
"go.signoz.io/signoz/pkg/query-service/common"
|
||||||
"go.signoz.io/signoz/pkg/query-service/constants"
|
"go.signoz.io/signoz/pkg/query-service/constants"
|
||||||
am "go.signoz.io/signoz/pkg/query-service/integrations/alertManager"
|
am "go.signoz.io/signoz/pkg/query-service/integrations/alertManager"
|
||||||
"go.signoz.io/signoz/pkg/query-service/interfaces"
|
"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 rows driver.Rows
|
||||||
var response v3.AggregateAttributeResponse
|
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 {
|
if req.Limit != 0 {
|
||||||
query = query + fmt.Sprintf(" LIMIT %d;", req.Limit)
|
query = query + fmt.Sprintf(" LIMIT %d;", req.Limit)
|
||||||
}
|
}
|
||||||
@ -3986,11 +3987,18 @@ func (r *ClickHouseReader) GetMetricAggregateAttributes(ctx context.Context, req
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var metricName, typ string
|
seen := make(map[string]struct{})
|
||||||
|
|
||||||
|
var metricName, typ, temporality string
|
||||||
|
var isMonotonic bool
|
||||||
for rows.Next() {
|
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())
|
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
|
// unlike traces/logs `tag`/`resource` type, the `Type` will be metric type
|
||||||
key := v3.AttributeKey{
|
key := v3.AttributeKey{
|
||||||
Key: metricName,
|
Key: metricName,
|
||||||
@ -3998,6 +4006,11 @@ func (r *ClickHouseReader) GetMetricAggregateAttributes(ctx context.Context, req
|
|||||||
Type: v3.AttributeKeyType(typ),
|
Type: v3.AttributeKeyType(typ),
|
||||||
IsColumn: true,
|
IsColumn: true,
|
||||||
}
|
}
|
||||||
|
// remove duplicates
|
||||||
|
if _, ok := seen[metricName+typ]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[metricName+typ] = struct{}{}
|
||||||
response.AttributeKeys = append(response.AttributeKeys, key)
|
response.AttributeKeys = append(response.AttributeKeys, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4012,11 +4025,11 @@ func (r *ClickHouseReader) GetMetricAttributeKeys(ctx context.Context, req *v3.F
|
|||||||
var response v3.FilterAttributeKeyResponse
|
var response v3.FilterAttributeKeyResponse
|
||||||
|
|
||||||
// skips the internal attributes i.e attributes starting with __
|
// 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 {
|
if req.Limit != 0 {
|
||||||
query = query + fmt.Sprintf(" LIMIT %d;", req.Limit)
|
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 {
|
if err != nil {
|
||||||
zap.S().Error(err)
|
zap.S().Error(err)
|
||||||
return nil, fmt.Errorf("error while executing query: %s", err.Error())
|
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 rows driver.Rows
|
||||||
var attributeValues v3.FilterAttributeValueResponse
|
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 {
|
if req.Limit != 0 {
|
||||||
query = query + fmt.Sprintf(" LIMIT %d;", req.Limit)
|
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 {
|
if err != nil {
|
||||||
zap.S().Error(err)
|
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