mirror of
https://github.com/SigNoz/signoz.git
synced 2026-06-03 23:50:33 +01:00
Compare commits
3 Commits
issue_5131
...
nv/perses-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9abf8a6188 | ||
|
|
259ad8a760 | ||
|
|
95b98a50af |
@@ -190,7 +190,7 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:v0.127.0
|
||||
image: signoz/signoz:v0.126.1
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
# - "6060:6060" # pprof port
|
||||
@@ -213,7 +213,7 @@ services:
|
||||
retries: 3
|
||||
otel-collector:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:v0.144.5
|
||||
image: signoz/signoz-otel-collector:v0.144.4
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
command:
|
||||
@@ -241,7 +241,7 @@ services:
|
||||
replicas: 3
|
||||
signoz-telemetrystore-migrator:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:v0.144.5
|
||||
image: signoz/signoz-otel-collector:v0.144.4
|
||||
environment:
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
|
||||
|
||||
@@ -117,7 +117,7 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:v0.127.0
|
||||
image: signoz/signoz:v0.126.1
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
volumes:
|
||||
@@ -139,7 +139,7 @@ services:
|
||||
retries: 3
|
||||
otel-collector:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:v0.144.5
|
||||
image: signoz/signoz-otel-collector:v0.144.4
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
command:
|
||||
@@ -167,7 +167,7 @@ services:
|
||||
replicas: 3
|
||||
signoz-telemetrystore-migrator:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:v0.144.5
|
||||
image: signoz/signoz-otel-collector:v0.144.4
|
||||
environment:
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
|
||||
|
||||
@@ -181,7 +181,7 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:${VERSION:-v0.127.0}
|
||||
image: signoz/signoz:${VERSION:-v0.126.1}
|
||||
container_name: signoz
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
@@ -204,7 +204,7 @@ services:
|
||||
retries: 3
|
||||
otel-collector:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.5}
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.4}
|
||||
container_name: signoz-otel-collector
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
@@ -229,7 +229,7 @@ services:
|
||||
- "4318:4318" # OTLP HTTP receiver
|
||||
signoz-telemetrystore-migrator:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.5}
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.4}
|
||||
container_name: signoz-telemetrystore-migrator
|
||||
environment:
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
|
||||
@@ -109,7 +109,7 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:${VERSION:-v0.127.0}
|
||||
image: signoz/signoz:${VERSION:-v0.126.1}
|
||||
container_name: signoz
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
@@ -132,7 +132,7 @@ services:
|
||||
retries: 3
|
||||
otel-collector:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.5}
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.4}
|
||||
container_name: signoz-otel-collector
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
@@ -157,7 +157,7 @@ services:
|
||||
- "4318:4318" # OTLP HTTP receiver
|
||||
signoz-telemetrystore-migrator:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.5}
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.4}
|
||||
container_name: signoz-telemetrystore-migrator
|
||||
environment:
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
|
||||
@@ -2380,6 +2380,26 @@ components:
|
||||
repeatVariable:
|
||||
type: string
|
||||
type: object
|
||||
DashboardLink:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
renderVariables:
|
||||
type: boolean
|
||||
targetBlank:
|
||||
type: boolean
|
||||
tooltip:
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
type: object
|
||||
DashboardPanelDisplay:
|
||||
properties:
|
||||
description:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
type: object
|
||||
DashboardTextVariableSpec:
|
||||
properties:
|
||||
constant:
|
||||
@@ -2510,7 +2530,7 @@ components:
|
||||
type: array
|
||||
links:
|
||||
items:
|
||||
$ref: '#/components/schemas/V1Link'
|
||||
$ref: '#/components/schemas/DashboardLink'
|
||||
type: array
|
||||
panels:
|
||||
additionalProperties:
|
||||
@@ -2647,8 +2667,8 @@ components:
|
||||
type: object
|
||||
DashboardtypesLayout:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/DashboardtypesLayoutEnvelopeGithubComPersesPersesPkgModelApiV1DashboardGridLayoutSpec'
|
||||
DashboardtypesLayoutEnvelopeGithubComPersesPersesPkgModelApiV1DashboardGridLayoutSpec:
|
||||
- $ref: '#/components/schemas/DashboardtypesLayoutEnvelopeGithubComPersesSpecGoDashboardGridLayoutSpec'
|
||||
DashboardtypesLayoutEnvelopeGithubComPersesSpecGoDashboardGridLayoutSpec:
|
||||
properties:
|
||||
kind:
|
||||
enum:
|
||||
@@ -2731,7 +2751,7 @@ components:
|
||||
DashboardtypesPanel:
|
||||
properties:
|
||||
kind:
|
||||
type: string
|
||||
$ref: '#/components/schemas/DashboardtypesPanelKind'
|
||||
spec:
|
||||
$ref: '#/components/schemas/DashboardtypesPanelSpec'
|
||||
type: object
|
||||
@@ -2742,6 +2762,10 @@ components:
|
||||
unit:
|
||||
type: string
|
||||
type: object
|
||||
DashboardtypesPanelKind:
|
||||
enum:
|
||||
- Panel
|
||||
type: string
|
||||
DashboardtypesPanelPlugin:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesTimeSeriesPanelSpec'
|
||||
@@ -2848,10 +2872,10 @@ components:
|
||||
DashboardtypesPanelSpec:
|
||||
properties:
|
||||
display:
|
||||
$ref: '#/components/schemas/V1PanelDisplay'
|
||||
$ref: '#/components/schemas/DashboardPanelDisplay'
|
||||
links:
|
||||
items:
|
||||
$ref: '#/components/schemas/V1Link'
|
||||
$ref: '#/components/schemas/DashboardLink'
|
||||
type: array
|
||||
plugin:
|
||||
$ref: '#/components/schemas/DashboardtypesPanelPlugin'
|
||||
@@ -2910,10 +2934,18 @@ components:
|
||||
DashboardtypesQuery:
|
||||
properties:
|
||||
kind:
|
||||
type: string
|
||||
$ref: '#/components/schemas/DashboardtypesQueryKind'
|
||||
spec:
|
||||
$ref: '#/components/schemas/DashboardtypesQuerySpec'
|
||||
type: object
|
||||
DashboardtypesQueryKind:
|
||||
enum:
|
||||
- scalar
|
||||
- time_series
|
||||
- raw
|
||||
- raw_stream
|
||||
- trace
|
||||
type: string
|
||||
DashboardtypesQueryPlugin:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesBuilderQuerySpec'
|
||||
@@ -3157,8 +3189,8 @@ components:
|
||||
DashboardtypesVariable:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/DashboardtypesVariableEnvelopeGithubComSigNozSignozPkgTypesDashboardtypesListVariableSpec'
|
||||
- $ref: '#/components/schemas/DashboardtypesVariableEnvelopeGithubComPersesPersesPkgModelApiV1DashboardTextVariableSpec'
|
||||
DashboardtypesVariableEnvelopeGithubComPersesPersesPkgModelApiV1DashboardTextVariableSpec:
|
||||
- $ref: '#/components/schemas/DashboardtypesVariableEnvelopeGithubComPersesSpecGoDashboardTextVariableSpec'
|
||||
DashboardtypesVariableEnvelopeGithubComPersesSpecGoDashboardTextVariableSpec:
|
||||
properties:
|
||||
kind:
|
||||
enum:
|
||||
@@ -7176,26 +7208,6 @@ components:
|
||||
required:
|
||||
- id
|
||||
type: object
|
||||
V1Link:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
renderVariables:
|
||||
type: boolean
|
||||
targetBlank:
|
||||
type: boolean
|
||||
tooltip:
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
type: object
|
||||
V1PanelDisplay:
|
||||
properties:
|
||||
description:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
type: object
|
||||
VariableDefaultValue:
|
||||
type: object
|
||||
VariableDisplay:
|
||||
|
||||
@@ -3089,6 +3089,40 @@ export interface DashboardGridLayoutSpecDTO {
|
||||
repeatVariable?: string;
|
||||
}
|
||||
|
||||
export interface DashboardLinkDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
renderVariables?: boolean;
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
targetBlank?: boolean;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
tooltip?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export interface DashboardPanelDisplayDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export interface VariableDisplayDTO {
|
||||
/**
|
||||
* @type string
|
||||
@@ -3786,40 +3820,9 @@ export type DashboardtypesDashboardSpecDTODatasources = {
|
||||
[key: string]: DashboardtypesDatasourceSpecDTO;
|
||||
};
|
||||
|
||||
export interface V1PanelDisplayDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
name?: string;
|
||||
export enum DashboardtypesPanelKindDTO {
|
||||
Panel = 'Panel',
|
||||
}
|
||||
|
||||
export interface V1LinkDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
renderVariables?: boolean;
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
targetBlank?: boolean;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
tooltip?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export enum DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesTimeSeriesPanelSpecDTOKind {
|
||||
'signoz/TimeSeriesPanel' = 'signoz/TimeSeriesPanel',
|
||||
}
|
||||
@@ -4061,6 +4064,13 @@ export type DashboardtypesPanelPluginDTO =
|
||||
| DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesHistogramPanelSpecDTO
|
||||
| DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesListPanelSpecDTO;
|
||||
|
||||
export enum DashboardtypesQueryKindDTO {
|
||||
scalar = 'scalar',
|
||||
time_series = 'time_series',
|
||||
raw = 'raw',
|
||||
raw_stream = 'raw_stream',
|
||||
trace = 'trace',
|
||||
}
|
||||
export enum DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesBuilderQuerySpecDTOKind {
|
||||
'signoz/BuilderQuery' = 'signoz/BuilderQuery',
|
||||
}
|
||||
@@ -4365,19 +4375,16 @@ export interface DashboardtypesQuerySpecDTO {
|
||||
}
|
||||
|
||||
export interface DashboardtypesQueryDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
kind?: string;
|
||||
kind?: DashboardtypesQueryKindDTO;
|
||||
spec?: DashboardtypesQuerySpecDTO;
|
||||
}
|
||||
|
||||
export interface DashboardtypesPanelSpecDTO {
|
||||
display?: V1PanelDisplayDTO;
|
||||
display?: DashboardPanelDisplayDTO;
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
links?: V1LinkDTO[];
|
||||
links?: DashboardLinkDTO[];
|
||||
plugin?: DashboardtypesPanelPluginDTO;
|
||||
/**
|
||||
* @type array
|
||||
@@ -4386,10 +4393,7 @@ export interface DashboardtypesPanelSpecDTO {
|
||||
}
|
||||
|
||||
export interface DashboardtypesPanelDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
kind?: string;
|
||||
kind?: DashboardtypesPanelKindDTO;
|
||||
spec?: DashboardtypesPanelSpecDTO;
|
||||
}
|
||||
|
||||
@@ -4403,20 +4407,20 @@ export type DashboardtypesDashboardSpecDTOPanelsAnyOf = {
|
||||
export type DashboardtypesDashboardSpecDTOPanels =
|
||||
DashboardtypesDashboardSpecDTOPanelsAnyOf | null;
|
||||
|
||||
export enum DashboardtypesLayoutEnvelopeGithubComPersesPersesPkgModelApiV1DashboardGridLayoutSpecDTOKind {
|
||||
export enum DashboardtypesLayoutEnvelopeGithubComPersesSpecGoDashboardGridLayoutSpecDTOKind {
|
||||
Grid = 'Grid',
|
||||
}
|
||||
export interface DashboardtypesLayoutEnvelopeGithubComPersesPersesPkgModelApiV1DashboardGridLayoutSpecDTO {
|
||||
export interface DashboardtypesLayoutEnvelopeGithubComPersesSpecGoDashboardGridLayoutSpecDTO {
|
||||
/**
|
||||
* @enum Grid
|
||||
* @type string
|
||||
*/
|
||||
kind: DashboardtypesLayoutEnvelopeGithubComPersesPersesPkgModelApiV1DashboardGridLayoutSpecDTOKind;
|
||||
kind: DashboardtypesLayoutEnvelopeGithubComPersesSpecGoDashboardGridLayoutSpecDTOKind;
|
||||
spec: DashboardGridLayoutSpecDTO;
|
||||
}
|
||||
|
||||
export type DashboardtypesLayoutDTO =
|
||||
DashboardtypesLayoutEnvelopeGithubComPersesPersesPkgModelApiV1DashboardGridLayoutSpecDTO;
|
||||
DashboardtypesLayoutEnvelopeGithubComPersesSpecGoDashboardGridLayoutSpecDTO;
|
||||
|
||||
export enum DashboardtypesVariableEnvelopeGithubComSigNozSignozPkgTypesDashboardtypesListVariableSpecDTOKind {
|
||||
ListVariable = 'ListVariable',
|
||||
@@ -4520,21 +4524,21 @@ export interface DashboardtypesVariableEnvelopeGithubComSigNozSignozPkgTypesDash
|
||||
spec: DashboardtypesListVariableSpecDTO;
|
||||
}
|
||||
|
||||
export enum DashboardtypesVariableEnvelopeGithubComPersesPersesPkgModelApiV1DashboardTextVariableSpecDTOKind {
|
||||
export enum DashboardtypesVariableEnvelopeGithubComPersesSpecGoDashboardTextVariableSpecDTOKind {
|
||||
TextVariable = 'TextVariable',
|
||||
}
|
||||
export interface DashboardtypesVariableEnvelopeGithubComPersesPersesPkgModelApiV1DashboardTextVariableSpecDTO {
|
||||
export interface DashboardtypesVariableEnvelopeGithubComPersesSpecGoDashboardTextVariableSpecDTO {
|
||||
/**
|
||||
* @enum TextVariable
|
||||
* @type string
|
||||
*/
|
||||
kind: DashboardtypesVariableEnvelopeGithubComPersesPersesPkgModelApiV1DashboardTextVariableSpecDTOKind;
|
||||
kind: DashboardtypesVariableEnvelopeGithubComPersesSpecGoDashboardTextVariableSpecDTOKind;
|
||||
spec: DashboardTextVariableSpecDTO;
|
||||
}
|
||||
|
||||
export type DashboardtypesVariableDTO =
|
||||
| DashboardtypesVariableEnvelopeGithubComSigNozSignozPkgTypesDashboardtypesListVariableSpecDTO
|
||||
| DashboardtypesVariableEnvelopeGithubComPersesPersesPkgModelApiV1DashboardTextVariableSpecDTO;
|
||||
| DashboardtypesVariableEnvelopeGithubComPersesSpecGoDashboardTextVariableSpecDTO;
|
||||
|
||||
export interface DashboardtypesDashboardSpecDTO {
|
||||
/**
|
||||
@@ -4553,7 +4557,7 @@ export interface DashboardtypesDashboardSpecDTO {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
links?: V1LinkDTO[];
|
||||
links?: DashboardLinkDTO[];
|
||||
/**
|
||||
* @type object,null
|
||||
*/
|
||||
|
||||
@@ -349,7 +349,7 @@ function convertV5DataByType(
|
||||
*/
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
export function convertV5ResponseToLegacy(
|
||||
v5Response: SuccessResponse<MetricRangePayloadV5, QueryRangeRequestV5>,
|
||||
v5Response: SuccessResponse<MetricRangePayloadV5>,
|
||||
legendMap: Record<string, string>,
|
||||
formatForWeb?: boolean,
|
||||
): SuccessResponse<MetricRangePayloadV3> & { warning?: Warning } {
|
||||
@@ -357,7 +357,7 @@ export function convertV5ResponseToLegacy(
|
||||
const v5Data = payload?.data;
|
||||
|
||||
const aggregationPerQuery =
|
||||
params?.compositeQuery?.queries
|
||||
(params as QueryRangeRequestV5)?.compositeQuery?.queries
|
||||
?.filter((query) => query.type === 'builder_query')
|
||||
.reduce(
|
||||
(acc, query) => {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { MetricQueryRangeSuccessResponse } from 'types/api/metrics/getQueryRange';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
|
||||
import { usePanelContextMenu } from '../usePanelContextMenu';
|
||||
|
||||
@@ -46,7 +47,10 @@ const mockWidget = { id: 'w-1', query: {} } as unknown as Widgets;
|
||||
const mockQueryResponse = {
|
||||
data: undefined,
|
||||
isLoading: false,
|
||||
} as unknown as UseQueryResult<MetricQueryRangeSuccessResponse, Error>;
|
||||
} as unknown as UseQueryResult<
|
||||
SuccessResponse<MetricRangePayloadProps, unknown>,
|
||||
Error
|
||||
>;
|
||||
|
||||
describe('usePanelContextMenu', () => {
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -10,13 +10,17 @@ import {
|
||||
PopoverPosition,
|
||||
useCoordinates,
|
||||
} from 'periscope/components/ContextMenu';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { MetricQueryRangeSuccessResponse } from 'types/api/metrics/getQueryRange';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
interface UseTimeSeriesContextMenuParams {
|
||||
widget: Widgets;
|
||||
queryResponse: UseQueryResult<MetricQueryRangeSuccessResponse, Error>;
|
||||
queryResponse: UseQueryResult<
|
||||
SuccessResponse<MetricRangePayloadProps, unknown>,
|
||||
Error
|
||||
>;
|
||||
enableDrillDown?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,9 @@ import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
||||
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
|
||||
import { IDashboardVariables } from 'providers/Dashboard/store/dashboardVariables/dashboardVariablesStoreTypes';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import {
|
||||
MetricQueryRangeSuccessResponse,
|
||||
MetricRangePayloadProps,
|
||||
} from 'types/api/metrics/getQueryRange';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { QueryData } from 'types/api/widgets/getQuery';
|
||||
import uPlot from 'uplot';
|
||||
|
||||
@@ -23,7 +21,10 @@ export interface GraphVisibilityLegendEntryProps {
|
||||
|
||||
export interface WidgetGraphComponentProps {
|
||||
widget: Widgets;
|
||||
queryResponse: UseQueryResult<MetricQueryRangeSuccessResponse, Error>;
|
||||
queryResponse: UseQueryResult<
|
||||
SuccessResponse<MetricRangePayloadProps, unknown>,
|
||||
Error
|
||||
>;
|
||||
errorMessage: string | undefined;
|
||||
version?: string;
|
||||
threshold?: ReactNode;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { ContextLinksData, Widgets } from 'types/api/dashboard/getAll';
|
||||
import { MetricQueryRangeSuccessResponse } from 'types/api/metrics/getQueryRange';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import uPlot from 'uplot';
|
||||
|
||||
export type GridValueComponentProps = {
|
||||
@@ -12,7 +13,10 @@ export type GridValueComponentProps = {
|
||||
thresholds?: ThresholdProps[];
|
||||
// Context menu related props
|
||||
widget?: Widgets;
|
||||
queryResponse?: UseQueryResult<MetricQueryRangeSuccessResponse, Error>;
|
||||
queryResponse?: UseQueryResult<
|
||||
SuccessResponse<MetricRangePayloadProps, unknown>,
|
||||
Error
|
||||
>;
|
||||
contextLinks?: ContextLinksData;
|
||||
enableDrillDown?: boolean;
|
||||
};
|
||||
|
||||
@@ -28,8 +28,9 @@ import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||
import GetMinMax from 'lib/getMinMax';
|
||||
import getTimeString from 'lib/getTimeString';
|
||||
import { UpdateTimeInterval } from 'store/actions';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { MetricQueryRangeSuccessResponse } from 'types/api/metrics/getQueryRange';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
function WidgetGraph({
|
||||
@@ -201,7 +202,10 @@ function WidgetGraph({
|
||||
|
||||
interface WidgetGraphProps {
|
||||
selectedWidget: Widgets;
|
||||
queryResponse: UseQueryResult<MetricQueryRangeSuccessResponse, Error>;
|
||||
queryResponse: UseQueryResult<
|
||||
SuccessResponse<MetricRangePayloadProps, unknown>,
|
||||
Error
|
||||
>;
|
||||
setRequestData: Dispatch<SetStateAction<GetQueryResultsProps>>;
|
||||
selectedGraph: PANEL_TYPES;
|
||||
enableDrillDown?: boolean;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { QueryRangeRequestV5 } from 'api/v5/v5';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { Column, QueryData, QueryDataV3 } from 'types/api/widgets/getQuery';
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
export function populateMultipleResults(
|
||||
responseData: SuccessResponse<MetricRangePayloadProps, QueryRangeRequestV5>,
|
||||
): SuccessResponse<MetricRangePayloadProps, QueryRangeRequestV5> {
|
||||
responseData: SuccessResponse<MetricRangePayloadProps, unknown>,
|
||||
): SuccessResponse<MetricRangePayloadProps, unknown> {
|
||||
const queryResults = responseData?.payload?.data?.newResult?.data?.result;
|
||||
const allFormattedResults: QueryData[] = [];
|
||||
|
||||
@@ -67,19 +66,17 @@ export function populateMultipleResults(
|
||||
}
|
||||
|
||||
// Create a copy instead of mutating the original
|
||||
const updatedResponseData: SuccessResponse<
|
||||
MetricRangePayloadProps,
|
||||
QueryRangeRequestV5
|
||||
> = {
|
||||
...responseData,
|
||||
payload: {
|
||||
...responseData.payload,
|
||||
data: {
|
||||
...responseData.payload.data,
|
||||
result: allFormattedResults,
|
||||
const updatedResponseData: SuccessResponse<MetricRangePayloadProps, unknown> =
|
||||
{
|
||||
...responseData,
|
||||
payload: {
|
||||
...responseData.payload,
|
||||
data: {
|
||||
...responseData.payload.data,
|
||||
result: allFormattedResults,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
return updatedResponseData;
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ import {
|
||||
getSelectedWidgetIndex,
|
||||
} from 'providers/Dashboard/util';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import {
|
||||
ColumnUnit,
|
||||
ContextLinksData,
|
||||
@@ -60,7 +61,7 @@ import {
|
||||
} from 'types/api/dashboard/getAll';
|
||||
import { Props } from 'types/api/dashboard/update';
|
||||
import { IField } from 'types/api/logs/fields';
|
||||
import { MetricQueryRangeSuccessResponse } from 'types/api/metrics/getQueryRange';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
@@ -397,7 +398,7 @@ function NewWidget({
|
||||
|
||||
// State to hold query response for sharing between left and right containers
|
||||
const [queryResponse, setQueryResponse] = useState<
|
||||
UseQueryResult<MetricQueryRangeSuccessResponse, Error>
|
||||
UseQueryResult<SuccessResponse<MetricRangePayloadProps, unknown>, Error>
|
||||
>(null as any);
|
||||
|
||||
// request data should be handled by the parent and the child components should consume the same
|
||||
|
||||
@@ -2,8 +2,9 @@ import { Dispatch, SetStateAction } from 'react';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||
import { SuccessResponse, Warning } from 'types/api';
|
||||
import { Dashboard, Widgets } from 'types/api/dashboard/getAll';
|
||||
import { MetricQueryRangeSuccessResponse } from 'types/api/metrics/getQueryRange';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
|
||||
import { timePreferance } from './RightContainer/timeItems';
|
||||
|
||||
@@ -28,7 +29,9 @@ export interface WidgetGraphProps {
|
||||
setRequestData: Dispatch<SetStateAction<GetQueryResultsProps>>;
|
||||
isLoadingPanelData: boolean;
|
||||
setQueryResponse?: Dispatch<
|
||||
SetStateAction<UseQueryResult<MetricQueryRangeSuccessResponse, Error>>
|
||||
SetStateAction<
|
||||
UseQueryResult<SuccessResponse<MetricRangePayloadProps, unknown>, Error>
|
||||
>
|
||||
>;
|
||||
enableDrillDown?: boolean;
|
||||
dashboardData: Dashboard | undefined;
|
||||
@@ -36,7 +39,12 @@ export interface WidgetGraphProps {
|
||||
}
|
||||
|
||||
export type WidgetGraphContainerProps = {
|
||||
queryResponse: UseQueryResult<MetricQueryRangeSuccessResponse, Error>;
|
||||
queryResponse: UseQueryResult<
|
||||
SuccessResponse<MetricRangePayloadProps, unknown> & {
|
||||
warning?: Warning;
|
||||
},
|
||||
Error
|
||||
>;
|
||||
setRequestData: Dispatch<SetStateAction<GetQueryResultsProps>>;
|
||||
selectedGraph: PANEL_TYPES;
|
||||
selectedWidget: Widgets;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import GridTableComponent from 'container/GridTableComponent';
|
||||
import { GRID_TABLE_CONFIG } from 'container/GridTableComponent/config';
|
||||
import { QueryRangeRequestV5 } from 'types/api/v5/queryRange';
|
||||
|
||||
import { PanelWrapperProps } from './panelWrapper.types';
|
||||
|
||||
@@ -19,7 +20,7 @@ function TablePanelWrapper({
|
||||
(queryResponse.data?.payload?.data?.result?.[0] as any)?.table || [];
|
||||
const { thresholds } = widget;
|
||||
|
||||
const queryRangeRequest = queryResponse.data?.params;
|
||||
const queryRangeRequest = queryResponse.data?.params as QueryRangeRequestV5;
|
||||
|
||||
return (
|
||||
<GridTableComponent
|
||||
|
||||
@@ -2,8 +2,9 @@ import { useMemo } from 'react';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import useDashboardVarConfig from 'container/QueryTable/Drilldown/useDashboardVarConfig';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { ContextLinksData } from 'types/api/dashboard/getAll';
|
||||
import { MetricQueryRangeSuccessResponse } from 'types/api/metrics/getQueryRange';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { getTimeRange } from 'utils/getTimeRange';
|
||||
|
||||
@@ -43,7 +44,10 @@ const useAggregateDrilldown = ({
|
||||
aggregateData: AggregateData | null;
|
||||
contextLinks?: ContextLinksData;
|
||||
panelType?: PANEL_TYPES;
|
||||
queryRange?: UseQueryResult<MetricQueryRangeSuccessResponse, Error>;
|
||||
queryRange?: UseQueryResult<
|
||||
SuccessResponse<MetricRangePayloadProps, unknown>,
|
||||
Error
|
||||
>;
|
||||
}): {
|
||||
aggregateDrilldownConfig: {
|
||||
header?: string | React.ReactNode;
|
||||
|
||||
@@ -2,8 +2,9 @@ import { useMemo } from 'react';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { ContextLinksData } from 'types/api/dashboard/getAll';
|
||||
import { MetricQueryRangeSuccessResponse } from 'types/api/metrics/getQueryRange';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import { isValidQueryName } from './drilldownUtils';
|
||||
@@ -19,7 +20,10 @@ interface UseGraphContextMenuProps {
|
||||
setSubMenu: (subMenu: string) => void;
|
||||
contextLinks?: ContextLinksData;
|
||||
panelType?: PANEL_TYPES;
|
||||
queryRange?: UseQueryResult<MetricQueryRangeSuccessResponse, Error>;
|
||||
queryRange?: UseQueryResult<
|
||||
SuccessResponse<MetricRangePayloadProps, unknown>,
|
||||
Error
|
||||
>;
|
||||
}
|
||||
|
||||
export function useGraphContextMenu({
|
||||
|
||||
@@ -7,7 +7,8 @@ import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { MetricQueryRangeSuccessResponse } from 'types/api/metrics/getQueryRange';
|
||||
import { SuccessResponse, Warning } from 'types/api';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
@@ -18,13 +19,16 @@ export const useGetExplorerQueryRange = (
|
||||
requestData: Query | null,
|
||||
panelType: PANEL_TYPES | null,
|
||||
version: string,
|
||||
options?: UseQueryOptions<MetricQueryRangeSuccessResponse, Error>,
|
||||
options?: UseQueryOptions<SuccessResponse<MetricRangePayloadProps>, Error>,
|
||||
params?: Record<string, unknown>,
|
||||
isDependentOnQB = true,
|
||||
keyRef?: MutableRefObject<any>,
|
||||
headers?: Record<string, string>,
|
||||
selectedTimeInterval?: GetQueryResultsProps['globalSelectedInterval'],
|
||||
): UseQueryResult<MetricQueryRangeSuccessResponse, Error> => {
|
||||
): UseQueryResult<
|
||||
SuccessResponse<MetricRangePayloadProps> & { warning?: Warning },
|
||||
Error
|
||||
> => {
|
||||
const { isEnabledQuery } = useQueryBuilder();
|
||||
const {
|
||||
selectedTime: globalSelectedInterval,
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
IClickHouseQuery,
|
||||
IPromQLQuery,
|
||||
} from '../queryBuilder/queryBuilderData';
|
||||
import { ExecStats, QueryRangeRequestV5 } from '../v5/queryRange';
|
||||
import { ExecStats } from '../v5/queryRange';
|
||||
import { QueryData, QueryDataV3 } from '../widgets/getQuery';
|
||||
|
||||
export type QueryRangePayload = {
|
||||
@@ -39,16 +39,11 @@ export interface MetricRangePayloadProps {
|
||||
meta?: ExecStats;
|
||||
}
|
||||
|
||||
/** Query range success response. `params` is the request that produced the
|
||||
* payload; `warning` and `meta` are lifted from the payload to the top level
|
||||
* by `getQueryResults.ts` for consumer convenience. */
|
||||
export interface MetricQueryRangeSuccessResponse extends SuccessResponse<
|
||||
/** Query range success response including optional warning and meta */
|
||||
export type MetricQueryRangeSuccessResponse = SuccessResponse<
|
||||
MetricRangePayloadProps,
|
||||
QueryRangeRequestV5
|
||||
> {
|
||||
warning?: Warning;
|
||||
meta?: ExecStats;
|
||||
}
|
||||
unknown
|
||||
> & { warning?: Warning; meta?: ExecStats };
|
||||
|
||||
export interface MetricRangePayloadV3 {
|
||||
data: {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import getStartEndRangeTime from 'lib/getStartEndRangeTime';
|
||||
import store from 'store';
|
||||
import { MetricQueryRangeSuccessResponse } from 'types/api/metrics/getQueryRange';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { QueryRangeRequestV5 } from 'types/api/v5/queryRange';
|
||||
|
||||
export const getTimeRangeFromQueryRangeRequest = (
|
||||
@@ -27,9 +28,13 @@ export const getTimeRangeFromQueryRangeRequest = (
|
||||
};
|
||||
|
||||
export const getTimeRange = (
|
||||
widgetQueryRange?: UseQueryResult<MetricQueryRangeSuccessResponse, Error>,
|
||||
widgetQueryRange?: UseQueryResult<
|
||||
SuccessResponse<MetricRangePayloadProps, unknown>,
|
||||
Error
|
||||
>,
|
||||
): Record<string, number> => {
|
||||
const widgetParams = widgetQueryRange?.data?.params;
|
||||
const widgetParams =
|
||||
(widgetQueryRange?.data?.params as QueryRangeRequestV5) || null;
|
||||
|
||||
return getTimeRangeFromQueryRangeRequest(widgetParams);
|
||||
};
|
||||
|
||||
7
go.mod
7
go.mod
@@ -42,7 +42,7 @@ require (
|
||||
github.com/openfga/api/proto v0.0.0-20260319214821-f153694bfc20
|
||||
github.com/openfga/language/pkg/go v0.2.1
|
||||
github.com/opentracing/opentracing-go v1.2.0
|
||||
github.com/perses/perses v0.53.1
|
||||
github.com/perses/spec v0.1.2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/alertmanager v0.31.1
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
@@ -137,9 +137,8 @@ require (
|
||||
github.com/huandu/go-clone v1.7.3 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/muhlemmer/gu v0.3.1 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/perses/common v0.30.2 // indirect
|
||||
github.com/nxadm/tail v1.4.11 // indirect
|
||||
github.com/prometheus/client_golang/exp v0.0.0-20260325093428-d8591d0db856 // indirect
|
||||
github.com/puzpuzpuz/xsync/v4 v4.4.0 // indirect
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.15.1 // indirect
|
||||
@@ -150,8 +149,6 @@ require (
|
||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||
github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/zitadel/oidc/v3 v3.45.4 // indirect
|
||||
github.com/zitadel/schema v1.3.2 // indirect
|
||||
go.opentelemetry.io/collector/client v1.54.0 // indirect
|
||||
go.opentelemetry.io/collector/config/configoptional v1.50.0 // indirect
|
||||
go.opentelemetry.io/collector/config/configretry v1.50.0 // indirect
|
||||
|
||||
16
go.sum
16
go.sum
@@ -330,6 +330,7 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
@@ -830,8 +831,6 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
github.com/muhlemmer/gu v0.3.1 h1:7EAqmFrW7n3hETvuAdmFmn4hS8W+z3LgKtrnow+YzNM=
|
||||
github.com/muhlemmer/gu v0.3.1/go.mod h1:YHtHR+gxM+bKEIIs7Hmi9sPT3ZDUvTN/i88wQpZkrdM=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
@@ -841,8 +840,6 @@ github.com/natefinch/wrap v0.2.0 h1:IXzc/pw5KqxJv55gV0lSOcKHYuEZPGbQrOOXr/bamRk=
|
||||
github.com/natefinch/wrap v0.2.0/go.mod h1:6gMHlAl12DwYEfKP3TkuykYUfLSEAvHw67itm4/KAS8=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/nexucis/lamenv v0.5.2 h1:tK/u3XGhCq9qIoVNcXsK9LZb8fKopm0A5weqSRvHd7M=
|
||||
github.com/nexucis/lamenv v0.5.2/go.mod h1:HusJm6ltmmT7FMG8A750mOLuME6SHCsr2iFYxp5fFi0=
|
||||
github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk=
|
||||
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
|
||||
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
|
||||
@@ -905,10 +902,8 @@ github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko
|
||||
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/perses/common v0.30.2 h1:RAiVxUpX76lTCb4X7pfcXSvYdXQmZwKi4oDKAEO//u0=
|
||||
github.com/perses/common v0.30.2/go.mod h1:DFtur1QPah2/ChXbKKhw7djYdwNgz27s5fPKpiK0Xao=
|
||||
github.com/perses/perses v0.53.1 h1:9VY/6p9QWrZwPSV7qiwTMSOsgcB37Lb1AXKT0ORXc6I=
|
||||
github.com/perses/perses v0.53.1/go.mod h1:ro8fsgBkHYOdrL/MV+fdP9mflKzYCy/+gcbxiaReI/A=
|
||||
github.com/perses/spec v0.1.2 h1:yGoygcR3ZusuGDCmRMwsVXCMvMwi1qZndKV6NYNreEw=
|
||||
github.com/perses/spec v0.1.2/go.mod h1:NoGI5jmGwRdkdPgyYSZJTBL4/Py+dqIPKS2QV8NOvGE=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4/v4 v4.1.25 h1:kocOqRffaIbU5djlIBr7Wh+cx82C0vtFb0fOurZHqD0=
|
||||
github.com/pierrec/lz4/v4 v4.1.25/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
|
||||
@@ -1169,10 +1164,6 @@ github.com/zeebo/assert v1.3.1 h1:vukIABvugfNMZMQO1ABsyQDJDTVQbn+LWSMy1ol1h6A=
|
||||
github.com/zeebo/assert v1.3.1/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||
github.com/zitadel/oidc/v3 v3.45.4 h1:GKyWaPRVQ8sCu9XgJ3NgNGtG52FzwVJpzXjIUG2+YrI=
|
||||
github.com/zitadel/oidc/v3 v3.45.4/go.mod h1:XALmFXS9/kSom9B6uWin1yJ2WTI/E4Ti5aXJdewAVEs=
|
||||
github.com/zitadel/schema v1.3.2 h1:gfJvt7dOMfTmxzhscZ9KkapKo3Nei3B6cAxjav+lyjI=
|
||||
github.com/zitadel/schema v1.3.2/go.mod h1:IZmdfF9Wu62Zu6tJJTH3UsArevs3Y4smfJIj3L8fzxw=
|
||||
go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v2 v2.305.4/go.mod h1:Ud+VUwIi9/uQHOMA+4ekToJ12lTxlv0zB/+DHwTGEbU=
|
||||
@@ -1617,6 +1608,7 @@ golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
|
||||
@@ -54,7 +54,7 @@ func (c Config) Validate() error {
|
||||
if c.MaxConcurrentQueries <= 0 {
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "max_concurrent_queries must be positive, got %v", c.MaxConcurrentQueries)
|
||||
}
|
||||
if c.SkipResourceFingerprint.Enabled && c.SkipResourceFingerprint.Threshold <= 0 {
|
||||
if c.SkipResourceFingerprint.Enabled && c.SkipResourceFingerprint.Threshold == 0 {
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "skip_resource_fingerprint.threshold must be > 0 when enabled")
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -716,22 +716,6 @@ func aggOrderBy(k qbtypes.OrderBy, q qbtypes.QueryBuilderQuery[qbtypes.LogAggreg
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// maybeAttachResourceFilter decides whether to pre-filter on the resource table.
|
||||
//
|
||||
// The resource table maps resource attributes (e.g. service.name) to fingerprints.
|
||||
// When it helps, we look up the matching fingerprints there first and feed them to the
|
||||
// main query as a CTE, so the main table only scans those fingerprints.
|
||||
//
|
||||
// There are three outcomes:
|
||||
//
|
||||
// 1. Skip it — the filter has nothing we can pre-resolve. This happens when the filter
|
||||
// is empty,no resource filter, or when its resource conditions sit under an OR (e.g.
|
||||
// `name='GET' OR service.name='abc'`), because then we can't reduce to a fixed set
|
||||
// of fingerprints. The main query filters on resource attributes inline instead.
|
||||
// 2. Skip it — too many fingerprints match (over the configured threshold), so the CTE
|
||||
// would not be selective enough to be worth it. Again, filter inline on the main table.
|
||||
// 3. Use it — attach the matching fingerprints as the __resource_filter CTE and join
|
||||
// the main table on resource_fingerprint.
|
||||
func (b *logQueryStatementBuilder) maybeAttachResourceFilter(
|
||||
ctx context.Context,
|
||||
sb *sqlbuilder.SelectBuilder,
|
||||
|
||||
@@ -1230,7 +1230,7 @@ func TestSkipResourceFingerprintLogs(t *testing.T) {
|
||||
mockStore := telemetrystoretest.New(telemetrystore.Config{}, ®exQueryMatcher{})
|
||||
mock := mockStore.Mock()
|
||||
|
||||
mock.ExpectQueryRow(`SELECT uniq\(fingerprint\) FROM signoz_logs\.distributed_logs_v2_resource`).
|
||||
mock.ExpectQueryRow(`SELECT count\(\) FROM \(SELECT fingerprint FROM signoz_logs\.distributed_logs_v2_resource`).
|
||||
WillReturnRow(cmock.NewRow([]cmock.ColumnType{
|
||||
{Name: "count", Type: "UInt64"},
|
||||
}, []any{uint64(2)}))
|
||||
@@ -1250,7 +1250,7 @@ func TestSkipResourceFingerprintLogs(t *testing.T) {
|
||||
mockStore := telemetrystoretest.New(telemetrystore.Config{}, ®exQueryMatcher{})
|
||||
mock := mockStore.Mock()
|
||||
|
||||
mock.ExpectQueryRow(`SELECT uniq\(fingerprint\) FROM signoz_logs\.distributed_logs_v2_resource`).
|
||||
mock.ExpectQueryRow(`SELECT count\(\) FROM \(SELECT fingerprint FROM signoz_logs\.distributed_logs_v2_resource`).
|
||||
WillReturnRow(cmock.NewRow([]cmock.ColumnType{
|
||||
{Name: "count", Type: "UInt64"},
|
||||
}, []any{threshold}))
|
||||
|
||||
@@ -98,7 +98,17 @@ func (b *resourceFilterStatementBuilder[T]) Build(
|
||||
query qbtypes.QueryBuilderQuery[T],
|
||||
variables map[string]qbtypes.VariableItem,
|
||||
) (*qbtypes.Statement, error) {
|
||||
q, isNoOp, err := b.buildQuery(ctx, start, end, "fingerprint", query, variables)
|
||||
q := sqlbuilder.NewSelectBuilder()
|
||||
q.Select("fingerprint")
|
||||
q.From(fmt.Sprintf("%s.%s", b.dbName, b.tableName))
|
||||
|
||||
keySelectors := b.getKeySelectors(query)
|
||||
keys, _, err := b.metadataStore.GetKeysMulti(ctx, keySelectors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isNoOp, err := b.addConditions(ctx, q, start, end, query, keys, variables)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -117,37 +127,8 @@ func (b *resourceFilterStatementBuilder[T]) Build(
|
||||
}, nil
|
||||
}
|
||||
|
||||
// buildQuery selects selectExpr from the resource table and applies the filter and
|
||||
// time conditions. isNoOp is true when the filter resolves to no resource conditions.
|
||||
func (b *resourceFilterStatementBuilder[T]) buildQuery(
|
||||
ctx context.Context,
|
||||
start, end uint64,
|
||||
selectExpr string,
|
||||
query qbtypes.QueryBuilderQuery[T],
|
||||
variables map[string]qbtypes.VariableItem,
|
||||
) (*sqlbuilder.SelectBuilder, bool, error) {
|
||||
q := sqlbuilder.NewSelectBuilder()
|
||||
q.Select(selectExpr)
|
||||
q.From(fmt.Sprintf("%s.%s", b.dbName, b.tableName))
|
||||
|
||||
keySelectors := b.getKeySelectors(query)
|
||||
keys, _, err := b.metadataStore.GetKeysMulti(ctx, keySelectors)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
isNoOp, err := b.addConditions(ctx, q, start, end, query, keys, variables)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return q, isNoOp, nil
|
||||
}
|
||||
|
||||
// BuildCount returns a statement that counts the distinct fingerprints matching
|
||||
// the resource filter. Returns (nil, nil) when the filter is a no-op.
|
||||
//
|
||||
// It uses uniq() rather than count() over a GROUP BY: uniq's approximation is well
|
||||
// within tolerance for a threshold check and is ~2x faster with far less memory.
|
||||
func (b *resourceFilterStatementBuilder[T]) BuildCount(
|
||||
ctx context.Context,
|
||||
start uint64,
|
||||
@@ -155,18 +136,13 @@ func (b *resourceFilterStatementBuilder[T]) BuildCount(
|
||||
query qbtypes.QueryBuilderQuery[T],
|
||||
variables map[string]qbtypes.VariableItem,
|
||||
) (*qbtypes.Statement, error) {
|
||||
q, isNoOp, err := b.buildQuery(ctx, start, end, "uniq(fingerprint)", query, variables)
|
||||
if err != nil {
|
||||
inner, err := b.Build(ctx, start, end, qbtypes.RequestTypeRaw, query, variables)
|
||||
if err != nil || inner == nil {
|
||||
return nil, err
|
||||
}
|
||||
if isNoOp {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
stmt, args := q.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
return &qbtypes.Statement{
|
||||
Query: stmt,
|
||||
Args: args,
|
||||
Query: fmt.Sprintf("SELECT count() FROM (%s)", inner.Query),
|
||||
Args: inner.Args,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -818,22 +818,6 @@ func aggOrderBy(k qbtypes.OrderBy, q qbtypes.QueryBuilderQuery[qbtypes.TraceAggr
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// maybeAttachResourceFilter decides whether to pre-filter on the resource table.
|
||||
//
|
||||
// The resource table maps resource attributes (e.g. service.name) to fingerprints.
|
||||
// When it helps, we look up the matching fingerprints there first and feed them to the
|
||||
// main query as a CTE, so the main table only scans those fingerprints.
|
||||
//
|
||||
// There are three outcomes:
|
||||
//
|
||||
// 1. Skip it — the filter has nothing we can pre-resolve. This happens when the filter
|
||||
// is empty,no resource filter, or when its resource conditions sit under an OR (e.g.
|
||||
// `name='GET' OR service.name='abc'`), because then we can't reduce to a fixed set
|
||||
// of fingerprints. The main query filters on resource attributes inline instead.
|
||||
// 2. Skip it — too many fingerprints match (over the configured threshold), so the CTE
|
||||
// would not be selective enough to be worth it. Again, filter inline on the main table.
|
||||
// 3. Use it — attach the matching fingerprints as the __resource_filter CTE and join
|
||||
// the main table on resource_fingerprint.
|
||||
func (b *traceQueryStatementBuilder) maybeAttachResourceFilter(
|
||||
ctx context.Context,
|
||||
sb *sqlbuilder.SelectBuilder,
|
||||
|
||||
@@ -1629,7 +1629,7 @@ func TestSkipResourceFingerprint(t *testing.T) {
|
||||
|
||||
// Only the count query runs against the telemetry store; the CTE
|
||||
// itself is embedded as SQL in the main query (no extra round trip).
|
||||
mock.ExpectQueryRow(`SELECT uniq\(fingerprint\) FROM signoz_traces\.distributed_traces_v3_resource`).
|
||||
mock.ExpectQueryRow(`SELECT count\(\) FROM \(SELECT fingerprint FROM signoz_traces\.distributed_traces_v3_resource`).
|
||||
WillReturnRow(cmock.NewRow([]cmock.ColumnType{
|
||||
{Name: "count", Type: "UInt64"},
|
||||
}, []any{uint64(2)}))
|
||||
@@ -1649,7 +1649,7 @@ func TestSkipResourceFingerprint(t *testing.T) {
|
||||
mockStore := telemetrystoretest.New(telemetrystore.Config{}, ®exQueryMatcher{})
|
||||
mock := mockStore.Mock()
|
||||
|
||||
mock.ExpectQueryRow(`SELECT uniq\(fingerprint\) FROM signoz_traces\.distributed_traces_v3_resource`).
|
||||
mock.ExpectQueryRow(`SELECT count\(\) FROM \(SELECT fingerprint FROM signoz_traces\.distributed_traces_v3_resource`).
|
||||
WillReturnRow(cmock.NewRow([]cmock.ColumnType{
|
||||
{Name: "count", Type: "UInt64"},
|
||||
}, []any{threshold}))
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/types/coretypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/tagtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/perses/perses/pkg/model/api/v1/common"
|
||||
"github.com/perses/spec/go/common"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
)
|
||||
|
||||
|
||||
@@ -9,8 +9,9 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/types/coretypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/tagtypes"
|
||||
qb "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/perses/perses/pkg/model/api/v1/common"
|
||||
"github.com/perses/spec/go/common"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -43,7 +44,7 @@ func newTestDashboardV2(t *testing.T, orgID valuer.UUID, source Source) *Dashboa
|
||||
},
|
||||
Queries: []Query{
|
||||
{
|
||||
Kind: "TimeSeriesQuery",
|
||||
Kind: QueryKind(qb.RequestTypeTimeSeries),
|
||||
Spec: QuerySpec{
|
||||
Plugin: QueryPlugin{
|
||||
Kind: QueryKindPromQL,
|
||||
|
||||
@@ -8,12 +8,12 @@ import (
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
qb "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
v1 "github.com/perses/perses/pkg/model/api/v1"
|
||||
"github.com/perses/perses/pkg/model/api/v1/common"
|
||||
"github.com/perses/spec/go/common"
|
||||
"github.com/perses/spec/go/dashboard"
|
||||
)
|
||||
|
||||
// DashboardSpec is the SigNoz dashboard v2 spec shape. It mirrors
|
||||
// v1.DashboardSpec (Perses) field-for-field, except every common.Plugin
|
||||
// dashboard.Spec (Perses) field-for-field, except every common.Plugin
|
||||
// occurrence is replaced with a typed SigNoz plugin whose OpenAPI schema is a
|
||||
// per-site discriminated oneOf.
|
||||
type DashboardSpec struct {
|
||||
@@ -24,7 +24,7 @@ type DashboardSpec struct {
|
||||
Layouts []Layout `json:"layouts"`
|
||||
Duration common.DurationString `json:"duration"`
|
||||
RefreshInterval common.DurationString `json:"refreshInterval,omitempty"`
|
||||
Links []v1.Link `json:"links,omitempty"`
|
||||
Links []dashboard.Link `json:"links,omitempty"`
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════
|
||||
|
||||
@@ -133,6 +133,21 @@ func TestInvalidateUnknownPluginKind(t *testing.T) {
|
||||
}`,
|
||||
wantContain: "NonExistentPanel",
|
||||
},
|
||||
{
|
||||
name: "unknown panel envelope kind",
|
||||
data: `{
|
||||
"panels": {
|
||||
"p1": {
|
||||
"kind": "Row",
|
||||
"spec": {
|
||||
"plugin": {"kind": "signoz/TimeSeriesPanel", "spec": {}}
|
||||
}
|
||||
}
|
||||
},
|
||||
"layouts": []
|
||||
}`,
|
||||
wantContain: "unknown panel kind",
|
||||
},
|
||||
{
|
||||
name: "unknown query plugin",
|
||||
data: `{
|
||||
@@ -142,7 +157,7 @@ func TestInvalidateUnknownPluginKind(t *testing.T) {
|
||||
"spec": {
|
||||
"plugin": {"kind": "signoz/TimeSeriesPanel", "spec": {}},
|
||||
"queries": [{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"kind": "time_series",
|
||||
"spec": {
|
||||
"plugin": {"kind": "FakeQueryPlugin", "spec": {}}
|
||||
}
|
||||
@@ -154,6 +169,48 @@ func TestInvalidateUnknownPluginKind(t *testing.T) {
|
||||
}`,
|
||||
wantContain: "FakeQueryPlugin",
|
||||
},
|
||||
{
|
||||
name: "unknown query envelope kind",
|
||||
data: `{
|
||||
"panels": {
|
||||
"p1": {
|
||||
"kind": "Panel",
|
||||
"spec": {
|
||||
"plugin": {"kind": "signoz/TimeSeriesPanel", "spec": {}},
|
||||
"queries": [{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"spec": {
|
||||
"plugin": {"kind": "signoz/BuilderQuery", "spec": {"name": "A", "signal": "metrics"}}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"layouts": []
|
||||
}`,
|
||||
wantContain: "unknown query kind",
|
||||
},
|
||||
{
|
||||
name: "empty query envelope kind",
|
||||
data: `{
|
||||
"panels": {
|
||||
"p1": {
|
||||
"kind": "Panel",
|
||||
"spec": {
|
||||
"plugin": {"kind": "signoz/TimeSeriesPanel", "spec": {}},
|
||||
"queries": [{
|
||||
"kind": "",
|
||||
"spec": {
|
||||
"plugin": {"kind": "signoz/BuilderQuery", "spec": {"name": "A", "signal": "metrics"}}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"layouts": []
|
||||
}`,
|
||||
wantContain: "unknown query kind",
|
||||
},
|
||||
{
|
||||
name: "unknown variable plugin",
|
||||
data: `{
|
||||
@@ -246,7 +303,7 @@ func TestRejectUnknownFieldsInPluginSpec(t *testing.T) {
|
||||
"spec": {
|
||||
"plugin": {"kind": "signoz/TimeSeriesPanel", "spec": {}},
|
||||
"queries": [{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"kind": "time_series",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "signoz/PromQLQuery",
|
||||
@@ -324,7 +381,7 @@ func TestInvalidateWrongFieldTypeInPluginSpec(t *testing.T) {
|
||||
"spec": {
|
||||
"plugin": {"kind": "signoz/TimeSeriesPanel", "spec": {}},
|
||||
"queries": [{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"kind": "time_series",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "signoz/PromQLQuery",
|
||||
@@ -389,7 +446,7 @@ func TestInvalidateBadPanelSpecValues(t *testing.T) {
|
||||
"spec": {}
|
||||
},
|
||||
"queries": [{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"kind": "time_series",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "signoz/BuilderQuery",
|
||||
@@ -620,8 +677,8 @@ func TestInvalidatePanelWithMultipleDirectQueries(t *testing.T) {
|
||||
"spec": {
|
||||
"plugin": {"kind": "signoz/TimeSeriesPanel", "spec": {}},
|
||||
"queries": [
|
||||
{"kind": "TimeSeriesQuery", "spec": {"plugin": {"kind": "signoz/BuilderQuery", "spec": {"name": "A", "signal": "metrics"}}}},
|
||||
{"kind": "TimeSeriesQuery", "spec": {"plugin": {"kind": "signoz/BuilderQuery", "spec": {"name": "B", "signal": "metrics"}}}}
|
||||
{"kind": "time_series", "spec": {"plugin": {"kind": "signoz/BuilderQuery", "spec": {"name": "A", "signal": "metrics"}}}},
|
||||
{"kind": "time_series", "spec": {"plugin": {"kind": "signoz/BuilderQuery", "spec": {"name": "B", "signal": "metrics"}}}}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -738,7 +795,7 @@ func TestTimeSeriesPanelDefaults(t *testing.T) {
|
||||
"kind": "signoz/TimeSeriesPanel",
|
||||
"spec": {}
|
||||
},
|
||||
"queries": [{"kind": "TimeSeriesQuery", "spec": {"plugin": {"kind": "signoz/PromQLQuery", "spec": {"name": "A", "query": "up"}}}}]
|
||||
"queries": [{"kind": "time_series", "spec": {"plugin": {"kind": "signoz/PromQLQuery", "spec": {"name": "A", "query": "up"}}}}]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -786,7 +843,7 @@ func TestNumberPanelDefaults(t *testing.T) {
|
||||
"kind": "signoz/NumberPanel",
|
||||
"spec": {"thresholds": [{"value": 100, "color": "Red"}]}
|
||||
},
|
||||
"queries": [{"kind": "TimeSeriesQuery", "spec": {"plugin": {"kind": "signoz/PromQLQuery", "spec": {"name": "A", "query": "up"}}}}]
|
||||
"queries": [{"kind": "time_series", "spec": {"plugin": {"kind": "signoz/PromQLQuery", "spec": {"name": "A", "query": "up"}}}}]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -847,7 +904,7 @@ func TestStorageRoundTrip(t *testing.T) {
|
||||
"kind": "signoz/TimeSeriesPanel",
|
||||
"spec": {}
|
||||
},
|
||||
"queries": [{"kind": "TimeSeriesQuery", "spec": {"plugin": {"kind": "signoz/PromQLQuery", "spec": {"name": "A", "query": "up"}}}}]
|
||||
"queries": [{"kind": "time_series", "spec": {"plugin": {"kind": "signoz/PromQLQuery", "spec": {"name": "A", "query": "up"}}}}]
|
||||
}
|
||||
},
|
||||
"p2": {
|
||||
@@ -857,7 +914,7 @@ func TestStorageRoundTrip(t *testing.T) {
|
||||
"kind": "signoz/NumberPanel",
|
||||
"spec": {"thresholds": [{"value": 100, "color": "Red"}]}
|
||||
},
|
||||
"queries": [{"kind": "TimeSeriesQuery", "spec": {"plugin": {"kind": "signoz/PromQLQuery", "spec": {"name": "A", "query": "up"}}}}]
|
||||
"queries": [{"kind": "time_series", "spec": {"plugin": {"kind": "signoz/PromQLQuery", "spec": {"name": "A", "query": "up"}}}}]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1062,7 +1119,7 @@ func TestPanelTypeQueryTypeCompatibility(t *testing.T) {
|
||||
return []byte(`{
|
||||
"panels": {"p1": {"kind": "Panel", "spec": {
|
||||
"plugin": {"kind": "` + panelKind + `", "spec": {}},
|
||||
"queries": [{"kind": "TimeSeriesQuery", "spec": {"plugin": {"kind": "` + queryKind + `", "spec": ` + querySpec + `}}}]
|
||||
"queries": [{"kind": "time_series", "spec": {"plugin": {"kind": "` + queryKind + `", "spec": ` + querySpec + `}}}]
|
||||
}}},
|
||||
"layouts": []
|
||||
}`)
|
||||
@@ -1071,7 +1128,7 @@ func TestPanelTypeQueryTypeCompatibility(t *testing.T) {
|
||||
return []byte(`{
|
||||
"panels": {"p1": {"kind": "Panel", "spec": {
|
||||
"plugin": {"kind": "` + panelKind + `", "spec": {}},
|
||||
"queries": [{"kind": "TimeSeriesQuery", "spec": {"plugin": {"kind": "signoz/CompositeQuery", "spec": {
|
||||
"queries": [{"kind": "time_series", "spec": {"plugin": {"kind": "signoz/CompositeQuery", "spec": {
|
||||
"queries": [{"type": "` + subType + `", "spec": ` + subSpec + `}]
|
||||
}}}}]
|
||||
}}},
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
v1 "github.com/perses/perses/pkg/model/api/v1"
|
||||
"github.com/perses/perses/pkg/model/api/v1/dashboard"
|
||||
"github.com/perses/spec/go/dashboard"
|
||||
"github.com/perses/spec/go/datasource"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -22,12 +22,12 @@ func TestDashboardSpecMatchesPerses(t *testing.T) {
|
||||
ours reflect.Type
|
||||
perses reflect.Type
|
||||
}{
|
||||
{"DashboardSpec", typeOf[DashboardSpec](), typeOf[v1.DashboardSpec]()},
|
||||
{"Panel", typeOf[Panel](), typeOf[v1.Panel]()},
|
||||
{"PanelSpec", typeOf[PanelSpec](), typeOf[v1.PanelSpec]()},
|
||||
{"Query", typeOf[Query](), typeOf[v1.Query]()},
|
||||
{"QuerySpec", typeOf[QuerySpec](), typeOf[v1.QuerySpec]()},
|
||||
{"DatasourceSpec", typeOf[DatasourceSpec](), typeOf[v1.DatasourceSpec]()},
|
||||
{"DashboardSpec", typeOf[DashboardSpec](), typeOf[dashboard.Spec]()},
|
||||
{"Panel", typeOf[Panel](), typeOf[dashboard.Panel]()},
|
||||
{"PanelSpec", typeOf[PanelSpec](), typeOf[dashboard.PanelSpec]()},
|
||||
{"Query", typeOf[Query](), typeOf[dashboard.Query]()},
|
||||
{"QuerySpec", typeOf[QuerySpec](), typeOf[dashboard.QuerySpec]()},
|
||||
{"DatasourceSpec", typeOf[DatasourceSpec](), typeOf[datasource.Spec]()},
|
||||
{"Variable", typeOf[Variable](), typeOf[dashboard.Variable]()},
|
||||
{"ListVariableSpec", typeOf[ListVariableSpec](), typeOf[dashboard.ListVariableSpec]()},
|
||||
{"Layout", typeOf[Layout](), typeOf[dashboard.Layout]()},
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package dashboardtypes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"maps"
|
||||
"slices"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
v1 "github.com/perses/perses/pkg/model/api/v1"
|
||||
"github.com/perses/perses/pkg/model/api/v1/common"
|
||||
"github.com/perses/perses/pkg/model/api/v1/dashboard"
|
||||
"github.com/perses/perses/pkg/model/api/v1/variable"
|
||||
qb "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/perses/spec/go/common"
|
||||
"github.com/perses/spec/go/dashboard"
|
||||
"github.com/perses/spec/go/dashboard/variable"
|
||||
"github.com/swaggest/jsonschema-go"
|
||||
)
|
||||
|
||||
@@ -27,15 +29,36 @@ type DatasourceSpec struct {
|
||||
// ══════════════════════════════════════════════
|
||||
|
||||
type Panel struct {
|
||||
Kind string `json:"kind"`
|
||||
Kind PanelKind `json:"kind"`
|
||||
Spec PanelSpec `json:"spec"`
|
||||
}
|
||||
|
||||
// PanelKind is the panel envelope discriminator. Perses leaves it a free
|
||||
// string; SigNoz locks it to the single valid value.
|
||||
type PanelKind string
|
||||
|
||||
const PanelKindPanel PanelKind = "Panel"
|
||||
|
||||
// Enum surfaces the allowed value in the generated OpenAPI schema.
|
||||
func (PanelKind) Enum() []any { return []any{PanelKindPanel} }
|
||||
|
||||
func (k *PanelKind) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return errors.WrapInvalidInputf(err, ErrCodeDashboardInvalidInput, "invalid panel kind")
|
||||
}
|
||||
if PanelKind(s) != PanelKindPanel {
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "unknown panel kind %q; allowed values: %s", s, allowedValuesForKind([]PanelKind{PanelKindPanel}))
|
||||
}
|
||||
*k = PanelKind(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
type PanelSpec struct {
|
||||
Display *v1.PanelDisplay `json:"display,omitempty"`
|
||||
Plugin PanelPlugin `json:"plugin"`
|
||||
Queries []Query `json:"queries,omitempty"`
|
||||
Links []v1.Link `json:"links,omitempty"`
|
||||
Display *dashboard.PanelDisplay `json:"display,omitempty"`
|
||||
Plugin PanelPlugin `json:"plugin"`
|
||||
Queries []Query `json:"queries,omitempty"`
|
||||
Links []dashboard.Link `json:"links,omitempty"`
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════
|
||||
@@ -43,10 +66,29 @@ type PanelSpec struct {
|
||||
// ══════════════════════════════════════════════
|
||||
|
||||
type Query struct {
|
||||
Kind string `json:"kind"`
|
||||
Kind QueryKind `json:"kind"`
|
||||
Spec QuerySpec `json:"spec"`
|
||||
}
|
||||
|
||||
type QueryKind qb.RequestType
|
||||
|
||||
func (QueryKind) Enum() []any { return (qb.RequestType{}).Enum() }
|
||||
|
||||
func (k *QueryKind) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return errors.WrapInvalidInputf(err, ErrCodeDashboardInvalidInput, "invalid query kind")
|
||||
}
|
||||
rt := qb.RequestType{String: valuer.NewString(s)}
|
||||
for _, allowed := range (qb.RequestType{}).Enum() {
|
||||
if allowed == rt {
|
||||
*k = QueryKind(rt)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "unknown query kind %q; allowed values: %s", s, "`scalar`, `time_series`, `raw`, `raw_stream`, `trace`")
|
||||
}
|
||||
|
||||
type QuerySpec struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Plugin QueryPlugin `json:"plugin"`
|
||||
|
||||
24
pkg/types/dashboardtypes/testdata/perses.json
vendored
24
pkg/types/dashboardtypes/testdata/perses.json
vendored
@@ -132,7 +132,7 @@
|
||||
],
|
||||
"queries": [
|
||||
{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"kind": "time_series",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "signoz/BuilderQuery",
|
||||
@@ -191,7 +191,7 @@
|
||||
},
|
||||
"queries": [
|
||||
{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"kind": "time_series",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "signoz/CompositeQuery",
|
||||
@@ -269,7 +269,7 @@
|
||||
},
|
||||
"queries": [
|
||||
{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"kind": "time_series",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "signoz/BuilderQuery",
|
||||
@@ -334,7 +334,7 @@
|
||||
},
|
||||
"queries": [
|
||||
{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"kind": "time_series",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "signoz/BuilderQuery",
|
||||
@@ -373,7 +373,7 @@
|
||||
},
|
||||
"queries": [
|
||||
{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"kind": "time_series",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "signoz/BuilderQuery",
|
||||
@@ -418,7 +418,7 @@
|
||||
},
|
||||
"queries": [
|
||||
{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"kind": "time_series",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "signoz/BuilderQuery",
|
||||
@@ -476,7 +476,7 @@
|
||||
},
|
||||
"queries": [
|
||||
{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"kind": "time_series",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "signoz/ClickHouseSQL",
|
||||
@@ -517,7 +517,7 @@
|
||||
},
|
||||
"queries": [
|
||||
{
|
||||
"kind": "LogQuery",
|
||||
"kind": "raw",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "signoz/BuilderQuery",
|
||||
@@ -571,7 +571,7 @@
|
||||
},
|
||||
"queries": [
|
||||
{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"kind": "time_series",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "signoz/BuilderQuery",
|
||||
@@ -610,7 +610,7 @@
|
||||
},
|
||||
"queries": [
|
||||
{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"kind": "time_series",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "signoz/CompositeQuery",
|
||||
@@ -681,7 +681,7 @@
|
||||
},
|
||||
"queries": [
|
||||
{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"kind": "time_series",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "signoz/CompositeQuery",
|
||||
@@ -722,7 +722,7 @@
|
||||
},
|
||||
"queries": [
|
||||
{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"kind": "time_series",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "signoz/PromQLQuery",
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
},
|
||||
"queries": [
|
||||
{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"kind": "time_series",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "signoz/BuilderQuery",
|
||||
@@ -85,7 +85,7 @@
|
||||
},
|
||||
"queries": [
|
||||
{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"kind": "time_series",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "signoz/BuilderQuery",
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
"""
|
||||
Transparency check for the skip_resource_fingerprint optimization (traces and logs).
|
||||
|
||||
The optimization changes how a query's resource conditions are resolved depending on
|
||||
how selective they are, but must change only ClickHouse performance, never the rows.
|
||||
Each test runs the same query against the primary instance (optimization on,
|
||||
threshold=2) and `signoz_fingerprint` (optimization off) and asserts the responses
|
||||
are identical, covering all three resolver outcomes:
|
||||
|
||||
- CTE: a filter matching fewer fingerprints than the threshold resolves through the
|
||||
fingerprint CTE.
|
||||
- Fallback: a filter matching at or above the threshold pushes resource conditions
|
||||
onto the main spans/logs table instead.
|
||||
- No-op: a filter with no resource conditions to pre-resolve (no resource field, or
|
||||
resource fields only under an OR) filters inline on the main table.
|
||||
At or above the configured fingerprint threshold the optimization pushes resource
|
||||
conditions onto the main spans/logs table instead of the fingerprint CTE. That
|
||||
rewrite must change ClickHouse performance, never the rows: each test runs the same
|
||||
query against the primary instance (optimization on, threshold=2) and
|
||||
`signoz_fingerprint` (optimization off) and asserts the responses are identical.
|
||||
"""
|
||||
|
||||
from collections.abc import Callable
|
||||
@@ -72,122 +65,6 @@ def test_skip_resource_fingerprint_traces_fallback_matches_fingerprint(
|
||||
assert_identical_query_response(optimized, fingerprint)
|
||||
|
||||
|
||||
def test_skip_resource_fingerprint_traces_cte_matches_fingerprint(
|
||||
signoz: types.SigNoz,
|
||||
signoz_fingerprint: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
insert_traces: Callable[[list[Traces]], None],
|
||||
) -> None:
|
||||
"""A < 2-fingerprint filter resolves through the fingerprint CTE; rows must match the baseline."""
|
||||
now = datetime.now(tz=UTC).replace(second=0, microsecond=0)
|
||||
|
||||
# One service shares the env (1 fingerprint < threshold 2 -> CTE); two spans on it
|
||||
# exercise dedup. A second service in a different env must be excluded.
|
||||
env = {"deployment.environment": "skip-cte"}
|
||||
insert_traces(
|
||||
[
|
||||
Traces(timestamp=now - timedelta(seconds=10), resources={"service.name": "skip-cte-svc-a", **env}),
|
||||
Traces(timestamp=now - timedelta(seconds=9), resources={"service.name": "skip-cte-svc-a", **env}),
|
||||
Traces(timestamp=now - timedelta(seconds=8), resources={"service.name": "skip-cte-other", "deployment.environment": "skip-cte-other-env"}),
|
||||
]
|
||||
)
|
||||
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
query = BuilderQuery(
|
||||
signal="traces",
|
||||
limit=50,
|
||||
order=[OrderBy(TelemetryFieldKey("timestamp"), "asc")],
|
||||
filter_expression="deployment.environment = 'skip-cte'",
|
||||
select_fields=[TelemetryFieldKey("service.name", "string", "resource")],
|
||||
).to_dict()
|
||||
|
||||
start_ms = int((datetime.now(tz=UTC) - timedelta(minutes=5)).timestamp() * 1000)
|
||||
end_ms = int(datetime.now(tz=UTC).timestamp() * 1000)
|
||||
optimized = make_query_request(signoz, token, start_ms=start_ms, end_ms=end_ms, request_type="raw", queries=[query])
|
||||
fingerprint = make_query_request(signoz_fingerprint, token, start_ms=start_ms, end_ms=end_ms, request_type="raw", queries=[query])
|
||||
|
||||
assert len(get_rows(optimized)) == 2
|
||||
assert_identical_query_response(optimized, fingerprint)
|
||||
|
||||
|
||||
def test_skip_resource_fingerprint_traces_or_filter_matches_fingerprint(
|
||||
signoz: types.SigNoz,
|
||||
signoz_fingerprint: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
insert_traces: Callable[[list[Traces]], None],
|
||||
) -> None:
|
||||
"""A resource condition under an OR has no fixed fingerprint set, so it filters inline; rows must match the baseline."""
|
||||
now = datetime.now(tz=UTC).replace(second=0, microsecond=0)
|
||||
|
||||
# `name = ... OR service.name = ...` can't reduce to a fingerprint set (no-op path).
|
||||
# span-1 matches on name, span-2 on service.name, span-3 matches neither.
|
||||
env = {"deployment.environment": "skip-or"}
|
||||
insert_traces(
|
||||
[
|
||||
Traces(timestamp=now - timedelta(seconds=10), name="tr-or-name", resources={"service.name": "tr-or-svc-x", **env}),
|
||||
Traces(timestamp=now - timedelta(seconds=9), name="tr-or-other", resources={"service.name": "tr-or-svc-a", **env}),
|
||||
Traces(timestamp=now - timedelta(seconds=8), name="tr-or-other", resources={"service.name": "tr-or-svc-b", **env}),
|
||||
]
|
||||
)
|
||||
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
query = BuilderQuery(
|
||||
signal="traces",
|
||||
limit=50,
|
||||
order=[OrderBy(TelemetryFieldKey("timestamp"), "asc")],
|
||||
filter_expression="name = 'tr-or-name' OR service.name = 'tr-or-svc-a'",
|
||||
select_fields=[TelemetryFieldKey("service.name", "string", "resource")],
|
||||
).to_dict()
|
||||
|
||||
start_ms = int((datetime.now(tz=UTC) - timedelta(minutes=5)).timestamp() * 1000)
|
||||
end_ms = int(datetime.now(tz=UTC).timestamp() * 1000)
|
||||
optimized = make_query_request(signoz, token, start_ms=start_ms, end_ms=end_ms, request_type="raw", queries=[query])
|
||||
fingerprint = make_query_request(signoz_fingerprint, token, start_ms=start_ms, end_ms=end_ms, request_type="raw", queries=[query])
|
||||
|
||||
assert len(get_rows(optimized)) == 2
|
||||
assert_identical_query_response(optimized, fingerprint)
|
||||
|
||||
|
||||
def test_skip_resource_fingerprint_traces_no_resource_filter_matches_fingerprint(
|
||||
signoz: types.SigNoz,
|
||||
signoz_fingerprint: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
insert_traces: Callable[[list[Traces]], None],
|
||||
) -> None:
|
||||
"""A filter with no resource field has nothing to pre-resolve, so it filters inline; rows must match the baseline."""
|
||||
now = datetime.now(tz=UTC).replace(second=0, microsecond=0)
|
||||
|
||||
# Filtering only on the span name (an intrinsic, non-resource field) is a no-op for
|
||||
# the resolver; span-1 matches, span-2 does not.
|
||||
env = {"deployment.environment": "skip-nr"}
|
||||
insert_traces(
|
||||
[
|
||||
Traces(timestamp=now - timedelta(seconds=10), name="tr-nr-name", resources={"service.name": "tr-nr-svc-a", **env}),
|
||||
Traces(timestamp=now - timedelta(seconds=9), name="tr-nr-other", resources={"service.name": "tr-nr-svc-b", **env}),
|
||||
]
|
||||
)
|
||||
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
query = BuilderQuery(
|
||||
signal="traces",
|
||||
limit=50,
|
||||
order=[OrderBy(TelemetryFieldKey("timestamp"), "asc")],
|
||||
filter_expression="name = 'tr-nr-name'",
|
||||
select_fields=[TelemetryFieldKey("service.name", "string", "resource")],
|
||||
).to_dict()
|
||||
|
||||
start_ms = int((datetime.now(tz=UTC) - timedelta(minutes=5)).timestamp() * 1000)
|
||||
end_ms = int(datetime.now(tz=UTC).timestamp() * 1000)
|
||||
optimized = make_query_request(signoz, token, start_ms=start_ms, end_ms=end_ms, request_type="raw", queries=[query])
|
||||
fingerprint = make_query_request(signoz_fingerprint, token, start_ms=start_ms, end_ms=end_ms, request_type="raw", queries=[query])
|
||||
|
||||
assert len(get_rows(optimized)) == 1
|
||||
assert_identical_query_response(optimized, fingerprint)
|
||||
|
||||
|
||||
def test_skip_resource_fingerprint_logs_fallback_matches_fingerprint(
|
||||
signoz: types.SigNoz,
|
||||
signoz_fingerprint: types.SigNoz,
|
||||
@@ -226,119 +103,3 @@ def test_skip_resource_fingerprint_logs_fallback_matches_fingerprint(
|
||||
|
||||
assert len(get_rows(optimized)) == 3
|
||||
assert_identical_query_response(optimized, fingerprint)
|
||||
|
||||
|
||||
def test_skip_resource_fingerprint_logs_cte_matches_fingerprint(
|
||||
signoz: types.SigNoz,
|
||||
signoz_fingerprint: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
insert_logs: Callable[[list[Logs]], None],
|
||||
) -> None:
|
||||
"""A < 2-fingerprint filter resolves through the fingerprint CTE; rows must match the baseline."""
|
||||
now = datetime.now(tz=UTC)
|
||||
|
||||
# One service shares the env (1 fingerprint < threshold 2 -> CTE); two logs on it
|
||||
# exercise dedup. A second service in a different env must be excluded.
|
||||
env = {"deployment.environment": "skip-logs-cte"}
|
||||
insert_logs(
|
||||
[
|
||||
Logs(timestamp=now - timedelta(seconds=10), resources={"service.name": "skip-logs-cte-svc-a", **env}, body="a"),
|
||||
Logs(timestamp=now - timedelta(seconds=9), resources={"service.name": "skip-logs-cte-svc-a", **env}, body="b"),
|
||||
Logs(timestamp=now - timedelta(seconds=8), resources={"service.name": "skip-logs-cte-other", "deployment.environment": "skip-logs-cte-other-env"}, body="noise"),
|
||||
]
|
||||
)
|
||||
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
query = BuilderQuery(
|
||||
signal="logs",
|
||||
limit=50,
|
||||
order=[OrderBy(TelemetryFieldKey("timestamp"), "asc")],
|
||||
filter_expression="deployment.environment = 'skip-logs-cte'",
|
||||
select_fields=[TelemetryFieldKey("service.name", "string", "resource"), TelemetryFieldKey("body")],
|
||||
).to_dict()
|
||||
|
||||
start_ms = int((datetime.now(tz=UTC) - timedelta(minutes=5)).timestamp() * 1000)
|
||||
end_ms = int(datetime.now(tz=UTC).timestamp() * 1000)
|
||||
optimized = make_query_request(signoz, token, start_ms=start_ms, end_ms=end_ms, request_type="raw", queries=[query])
|
||||
fingerprint = make_query_request(signoz_fingerprint, token, start_ms=start_ms, end_ms=end_ms, request_type="raw", queries=[query])
|
||||
|
||||
assert len(get_rows(optimized)) == 2
|
||||
assert_identical_query_response(optimized, fingerprint)
|
||||
|
||||
|
||||
def test_skip_resource_fingerprint_logs_or_filter_matches_fingerprint(
|
||||
signoz: types.SigNoz,
|
||||
signoz_fingerprint: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
insert_logs: Callable[[list[Logs]], None],
|
||||
) -> None:
|
||||
"""A resource condition under an OR has no fixed fingerprint set, so it filters inline; rows must match the baseline."""
|
||||
now = datetime.now(tz=UTC)
|
||||
|
||||
# `test.marker = ... OR service.name = ...` can't reduce to a fingerprint set (no-op path).
|
||||
# log-1 matches on the attribute, log-2 on service.name, log-3 matches neither.
|
||||
env = {"deployment.environment": "skip-logs-or"}
|
||||
insert_logs(
|
||||
[
|
||||
Logs(timestamp=now - timedelta(seconds=10), resources={"service.name": "logs-or-svc-x", **env}, attributes={"test.marker": "logs-or-hit"}, body="a"),
|
||||
Logs(timestamp=now - timedelta(seconds=9), resources={"service.name": "logs-or-svc-a", **env}, body="b"),
|
||||
Logs(timestamp=now - timedelta(seconds=8), resources={"service.name": "logs-or-svc-b", **env}, body="noise"),
|
||||
]
|
||||
)
|
||||
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
query = BuilderQuery(
|
||||
signal="logs",
|
||||
limit=50,
|
||||
order=[OrderBy(TelemetryFieldKey("timestamp"), "asc")],
|
||||
filter_expression="test.marker = 'logs-or-hit' OR service.name = 'logs-or-svc-a'",
|
||||
select_fields=[TelemetryFieldKey("service.name", "string", "resource"), TelemetryFieldKey("body")],
|
||||
).to_dict()
|
||||
|
||||
start_ms = int((datetime.now(tz=UTC) - timedelta(minutes=5)).timestamp() * 1000)
|
||||
end_ms = int(datetime.now(tz=UTC).timestamp() * 1000)
|
||||
optimized = make_query_request(signoz, token, start_ms=start_ms, end_ms=end_ms, request_type="raw", queries=[query])
|
||||
fingerprint = make_query_request(signoz_fingerprint, token, start_ms=start_ms, end_ms=end_ms, request_type="raw", queries=[query])
|
||||
|
||||
assert len(get_rows(optimized)) == 2
|
||||
assert_identical_query_response(optimized, fingerprint)
|
||||
|
||||
|
||||
def test_skip_resource_fingerprint_logs_no_resource_filter_matches_fingerprint(
|
||||
signoz: types.SigNoz,
|
||||
signoz_fingerprint: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
insert_logs: Callable[[list[Logs]], None],
|
||||
) -> None:
|
||||
"""A filter with no resource field has nothing to pre-resolve, so it filters inline; rows must match the baseline."""
|
||||
now = datetime.now(tz=UTC)
|
||||
|
||||
# Filtering only on an attribute (a non-resource field) is a no-op for the resolver;
|
||||
# log-1 matches, log-2 does not.
|
||||
env = {"deployment.environment": "skip-logs-nr"}
|
||||
insert_logs(
|
||||
[
|
||||
Logs(timestamp=now - timedelta(seconds=10), resources={"service.name": "logs-nr-svc-a", **env}, attributes={"test.marker": "logs-nr-hit"}, body="a"),
|
||||
Logs(timestamp=now - timedelta(seconds=9), resources={"service.name": "logs-nr-svc-b", **env}, body="b"),
|
||||
]
|
||||
)
|
||||
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
query = BuilderQuery(
|
||||
signal="logs",
|
||||
limit=50,
|
||||
order=[OrderBy(TelemetryFieldKey("timestamp"), "asc")],
|
||||
filter_expression="test.marker = 'logs-nr-hit'",
|
||||
select_fields=[TelemetryFieldKey("service.name", "string", "resource"), TelemetryFieldKey("body")],
|
||||
).to_dict()
|
||||
|
||||
start_ms = int((datetime.now(tz=UTC) - timedelta(minutes=5)).timestamp() * 1000)
|
||||
end_ms = int(datetime.now(tz=UTC).timestamp() * 1000)
|
||||
optimized = make_query_request(signoz, token, start_ms=start_ms, end_ms=end_ms, request_type="raw", queries=[query])
|
||||
fingerprint = make_query_request(signoz_fingerprint, token, start_ms=start_ms, end_ms=end_ms, request_type="raw", queries=[query])
|
||||
|
||||
assert len(get_rows(optimized)) == 1
|
||||
assert_identical_query_response(optimized, fingerprint)
|
||||
|
||||
Reference in New Issue
Block a user