mirror of
https://github.com/SigNoz/signoz.git
synced 2026-04-10 06:00:19 +01:00
Compare commits
103 Commits
feat/trace
...
nv/4172
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f868eb9ab | ||
|
|
c8d6e502e0 | ||
|
|
854e230883 | ||
|
|
8ffe675ce7 | ||
|
|
d1fcaeb924 | ||
|
|
dc2b29142e | ||
|
|
55a4471427 | ||
|
|
10f60c011f | ||
|
|
5ad56658bf | ||
|
|
bfb2f5719c | ||
|
|
bb725ea4d9 | ||
|
|
65bedcca3f | ||
|
|
1e7ddb0dbe | ||
|
|
f39e2183a3 | ||
|
|
43c9367ab5 | ||
|
|
596cb8adbb | ||
|
|
58a2737717 | ||
|
|
8263aed441 | ||
|
|
d3eb56f3da | ||
|
|
6e0c905977 | ||
|
|
8ee5b5f08e | ||
|
|
1325b0a1b3 | ||
|
|
76dc3b743b | ||
|
|
dfe0a9d147 | ||
|
|
f24c3d8e24 | ||
|
|
7dba4d7b64 | ||
|
|
2299cedeab | ||
|
|
6812d68d4d | ||
|
|
667a1e6d5d | ||
|
|
31306a57d8 | ||
|
|
839b3e29ee | ||
|
|
20ab7d4908 | ||
|
|
d5b72f9f0c | ||
|
|
9a3efa7704 | ||
|
|
fd45b4fad1 | ||
|
|
cea88bf51e | ||
|
|
6ead1cd52a | ||
|
|
7cb700428c | ||
|
|
a2dc410be6 | ||
|
|
66ce48434d | ||
|
|
7d3612c10a | ||
|
|
c82cd32f61 | ||
|
|
a3980e084c | ||
|
|
4319dd9cef | ||
|
|
92a5e9b9c9 | ||
|
|
408a914129 | ||
|
|
0e304b1d40 | ||
|
|
58a9be24d3 | ||
|
|
adf439fcf1 | ||
|
|
a1a54c4bb2 | ||
|
|
3c1961d3fc | ||
|
|
c3efa0660b | ||
|
|
183dd09082 | ||
|
|
a351373c49 | ||
|
|
8e7653b90d | ||
|
|
5c40d6b68b | ||
|
|
31115df41c | ||
|
|
869c3dccb2 | ||
|
|
c5d7a7ef8c | ||
|
|
544b87b254 | ||
|
|
e885fb98e5 | ||
|
|
be227eec43 | ||
|
|
13263c1f25 | ||
|
|
ccbf410d15 | ||
|
|
03b98ff824 | ||
|
|
2cdba0d11c | ||
|
|
84d2885530 | ||
|
|
b82dcc6138 | ||
|
|
a14d5847b9 | ||
|
|
d184746142 | ||
|
|
c335e17e1d | ||
|
|
433dd0b2d0 | ||
|
|
05e97e246a | ||
|
|
bddfe30f6c | ||
|
|
7a01a5250d | ||
|
|
09c98c830d | ||
|
|
0fbb90cc91 | ||
|
|
15f0787610 | ||
|
|
22ebc7732c | ||
|
|
cff18edf6e | ||
|
|
cb49c0bf3b | ||
|
|
1cb6f94d21 | ||
|
|
68155f374b | ||
|
|
696524509f | ||
|
|
705cdab38c | ||
|
|
ae9b881413 | ||
|
|
05f4e15d07 | ||
|
|
1653c6d725 | ||
|
|
070b4b7061 | ||
|
|
7f4c06edd6 | ||
|
|
6bed20b5b9 | ||
|
|
033bd3c9b8 | ||
|
|
d4c9a923fd | ||
|
|
387dcb529f | ||
|
|
7a4da7bcc5 | ||
|
|
b152fae3fa | ||
|
|
2ed766726c | ||
|
|
8767f6a57d | ||
|
|
22d8c7599b | ||
|
|
1019264272 | ||
|
|
c950d7e784 | ||
|
|
1e279e6193 | ||
|
|
d3a278c43e |
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
"github.com/SigNoz/signoz/cmd"
|
||||
"github.com/SigNoz/signoz/pkg/analytics"
|
||||
"github.com/SigNoz/signoz/pkg/auditor"
|
||||
"github.com/SigNoz/signoz/pkg/authn"
|
||||
"github.com/SigNoz/signoz/pkg/authz"
|
||||
"github.com/SigNoz/signoz/pkg/authz/openfgaauthz"
|
||||
@@ -94,9 +93,6 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
||||
func(_ licensing.Licensing) factory.ProviderFactory[gateway.Gateway, gateway.Config] {
|
||||
return noopgateway.NewProviderFactory()
|
||||
},
|
||||
func(_ licensing.Licensing) factory.NamedMap[factory.ProviderFactory[auditor.Auditor, auditor.Config]] {
|
||||
return signoz.NewAuditorProviderFactories()
|
||||
},
|
||||
func(ps factory.ProviderSettings, q querier.Querier, a analytics.Analytics) querier.Handler {
|
||||
return querier.NewHandler(ps, q, a)
|
||||
},
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/SigNoz/signoz/cmd"
|
||||
"github.com/SigNoz/signoz/ee/auditor/otlphttpauditor"
|
||||
"github.com/SigNoz/signoz/ee/authn/callbackauthn/oidccallbackauthn"
|
||||
"github.com/SigNoz/signoz/ee/authn/callbackauthn/samlcallbackauthn"
|
||||
"github.com/SigNoz/signoz/ee/authz/openfgaauthz"
|
||||
@@ -25,7 +24,6 @@ import (
|
||||
enterprisezeus "github.com/SigNoz/signoz/ee/zeus"
|
||||
"github.com/SigNoz/signoz/ee/zeus/httpzeus"
|
||||
"github.com/SigNoz/signoz/pkg/analytics"
|
||||
"github.com/SigNoz/signoz/pkg/auditor"
|
||||
"github.com/SigNoz/signoz/pkg/authn"
|
||||
"github.com/SigNoz/signoz/pkg/authz"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
@@ -135,13 +133,6 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
||||
func(licensing licensing.Licensing) factory.ProviderFactory[gateway.Gateway, gateway.Config] {
|
||||
return httpgateway.NewProviderFactory(licensing)
|
||||
},
|
||||
func(licensing licensing.Licensing) factory.NamedMap[factory.ProviderFactory[auditor.Auditor, auditor.Config]] {
|
||||
factories := signoz.NewAuditorProviderFactories()
|
||||
if err := factories.Add(otlphttpauditor.NewFactory(licensing, version.Info)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return factories
|
||||
},
|
||||
func(ps factory.ProviderSettings, q querier.Querier, a analytics.Analytics) querier.Handler {
|
||||
communityHandler := querier.NewHandler(ps, q, a)
|
||||
return eequerier.NewHandler(ps, q, communityHandler)
|
||||
|
||||
@@ -364,34 +364,3 @@ serviceaccount:
|
||||
analytics:
|
||||
# toggle service account analytics
|
||||
enabled: true
|
||||
|
||||
##################### Auditor #####################
|
||||
auditor:
|
||||
# Specifies the auditor provider to use.
|
||||
# noop: discards all audit events (community default).
|
||||
# otlphttp: exports audit events via OTLP HTTP (enterprise).
|
||||
provider: noop
|
||||
# The async channel capacity for audit events. Events are dropped when full (fail-open).
|
||||
buffer_size: 1000
|
||||
# The maximum number of events per export batch.
|
||||
batch_size: 100
|
||||
# The maximum time between export flushes.
|
||||
flush_interval: 1s
|
||||
otlphttp:
|
||||
# The target scheme://host:port/path of the OTLP HTTP endpoint.
|
||||
endpoint: http://localhost:4318/v1/logs
|
||||
# Whether to use HTTP instead of HTTPS.
|
||||
insecure: false
|
||||
# The maximum duration for an export attempt.
|
||||
timeout: 10s
|
||||
# Additional HTTP headers sent with every export request.
|
||||
headers: {}
|
||||
retry:
|
||||
# Whether to retry on transient failures.
|
||||
enabled: true
|
||||
# The initial wait time before the first retry.
|
||||
initial_interval: 5s
|
||||
# The upper bound on backoff interval.
|
||||
max_interval: 30s
|
||||
# The total maximum time spent retrying.
|
||||
max_elapsed_time: 60s
|
||||
|
||||
@@ -190,7 +190,7 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:v0.118.0
|
||||
image: signoz/signoz:v0.117.1
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
# - "6060:6060" # pprof port
|
||||
|
||||
@@ -117,7 +117,7 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:v0.118.0
|
||||
image: signoz/signoz:v0.117.1
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
volumes:
|
||||
|
||||
@@ -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.118.0}
|
||||
image: signoz/signoz:${VERSION:-v0.117.1}
|
||||
container_name: signoz
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
|
||||
@@ -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.118.0}
|
||||
image: signoz/signoz:${VERSION:-v0.117.1}
|
||||
container_name: signoz
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
|
||||
@@ -403,65 +403,27 @@ components:
|
||||
required:
|
||||
- regions
|
||||
type: object
|
||||
CloudintegrationtypesAWSCloudWatchLogsSubscription:
|
||||
CloudintegrationtypesAWSCollectionStrategy:
|
||||
properties:
|
||||
filterPattern:
|
||||
type: string
|
||||
logGroupNamePrefix:
|
||||
type: string
|
||||
required:
|
||||
- logGroupNamePrefix
|
||||
- filterPattern
|
||||
type: object
|
||||
CloudintegrationtypesAWSCloudWatchMetricStreamFilter:
|
||||
properties:
|
||||
metricNames:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
namespace:
|
||||
type: string
|
||||
required:
|
||||
- namespace
|
||||
aws_logs:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSLogsStrategy'
|
||||
aws_metrics:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSMetricsStrategy'
|
||||
s3_buckets:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
CloudintegrationtypesAWSConnectionArtifact:
|
||||
properties:
|
||||
connectionUrl:
|
||||
connectionURL:
|
||||
type: string
|
||||
required:
|
||||
- connectionUrl
|
||||
- connectionURL
|
||||
type: object
|
||||
CloudintegrationtypesAWSIntegrationConfig:
|
||||
properties:
|
||||
enabledRegions:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
telemetryCollectionStrategy:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSTelemetryCollectionStrategy'
|
||||
required:
|
||||
- enabledRegions
|
||||
- telemetryCollectionStrategy
|
||||
type: object
|
||||
CloudintegrationtypesAWSLogsCollectionStrategy:
|
||||
properties:
|
||||
subscriptions:
|
||||
items:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSCloudWatchLogsSubscription'
|
||||
type: array
|
||||
required:
|
||||
- subscriptions
|
||||
type: object
|
||||
CloudintegrationtypesAWSMetricsCollectionStrategy:
|
||||
properties:
|
||||
streamFilters:
|
||||
items:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSCloudWatchMetricStreamFilter'
|
||||
type: array
|
||||
required:
|
||||
- streamFilters
|
||||
type: object
|
||||
CloudintegrationtypesAWSPostableAccountConfig:
|
||||
CloudintegrationtypesAWSConnectionArtifactRequest:
|
||||
properties:
|
||||
deploymentRegion:
|
||||
type: string
|
||||
@@ -473,6 +435,46 @@ components:
|
||||
- deploymentRegion
|
||||
- regions
|
||||
type: object
|
||||
CloudintegrationtypesAWSIntegrationConfig:
|
||||
properties:
|
||||
enabledRegions:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
telemetry:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSCollectionStrategy'
|
||||
required:
|
||||
- enabledRegions
|
||||
- telemetry
|
||||
type: object
|
||||
CloudintegrationtypesAWSLogsStrategy:
|
||||
properties:
|
||||
cloudwatch_logs_subscriptions:
|
||||
items:
|
||||
properties:
|
||||
filter_pattern:
|
||||
type: string
|
||||
log_group_name_prefix:
|
||||
type: string
|
||||
type: object
|
||||
nullable: true
|
||||
type: array
|
||||
type: object
|
||||
CloudintegrationtypesAWSMetricsStrategy:
|
||||
properties:
|
||||
cloudwatch_metric_stream_filters:
|
||||
items:
|
||||
properties:
|
||||
MetricNames:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
Namespace:
|
||||
type: string
|
||||
type: object
|
||||
nullable: true
|
||||
type: array
|
||||
type: object
|
||||
CloudintegrationtypesAWSServiceConfig:
|
||||
properties:
|
||||
logs:
|
||||
@@ -484,7 +486,7 @@ components:
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
s3Buckets:
|
||||
s3_buckets:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
@@ -496,19 +498,6 @@ components:
|
||||
enabled:
|
||||
type: boolean
|
||||
type: object
|
||||
CloudintegrationtypesAWSTelemetryCollectionStrategy:
|
||||
properties:
|
||||
logs:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSLogsCollectionStrategy'
|
||||
metrics:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSMetricsCollectionStrategy'
|
||||
s3Buckets:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
CloudintegrationtypesAccount:
|
||||
properties:
|
||||
agentReport:
|
||||
@@ -572,26 +561,6 @@ components:
|
||||
nullable: true
|
||||
type: array
|
||||
type: object
|
||||
CloudintegrationtypesCloudIntegrationService:
|
||||
nullable: true
|
||||
properties:
|
||||
cloudIntegrationId:
|
||||
type: string
|
||||
config:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesServiceConfig'
|
||||
createdAt:
|
||||
format: date-time
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
type:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesServiceID'
|
||||
updatedAt:
|
||||
format: date-time
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
type: object
|
||||
CloudintegrationtypesCollectedLogAttribute:
|
||||
properties:
|
||||
name:
|
||||
@@ -612,6 +581,13 @@ components:
|
||||
unit:
|
||||
type: string
|
||||
type: object
|
||||
CloudintegrationtypesCollectionStrategy:
|
||||
properties:
|
||||
aws:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSCollectionStrategy'
|
||||
required:
|
||||
- aws
|
||||
type: object
|
||||
CloudintegrationtypesConnectionArtifact:
|
||||
properties:
|
||||
aws:
|
||||
@@ -619,21 +595,12 @@ components:
|
||||
required:
|
||||
- aws
|
||||
type: object
|
||||
CloudintegrationtypesCredentials:
|
||||
CloudintegrationtypesConnectionArtifactRequest:
|
||||
properties:
|
||||
ingestionKey:
|
||||
type: string
|
||||
ingestionUrl:
|
||||
type: string
|
||||
sigNozApiKey:
|
||||
type: string
|
||||
sigNozApiUrl:
|
||||
type: string
|
||||
aws:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSConnectionArtifactRequest'
|
||||
required:
|
||||
- sigNozApiUrl
|
||||
- sigNozApiKey
|
||||
- ingestionUrl
|
||||
- ingestionKey
|
||||
- aws
|
||||
type: object
|
||||
CloudintegrationtypesDashboard:
|
||||
properties:
|
||||
@@ -659,7 +626,7 @@ components:
|
||||
nullable: true
|
||||
type: array
|
||||
type: object
|
||||
CloudintegrationtypesGettableAccountWithConnectionArtifact:
|
||||
CloudintegrationtypesGettableAccountWithArtifact:
|
||||
properties:
|
||||
connectionArtifact:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesConnectionArtifact'
|
||||
@@ -678,7 +645,7 @@ components:
|
||||
required:
|
||||
- accounts
|
||||
type: object
|
||||
CloudintegrationtypesGettableAgentCheckIn:
|
||||
CloudintegrationtypesGettableAgentCheckInResponse:
|
||||
properties:
|
||||
account_id:
|
||||
type: string
|
||||
@@ -727,72 +694,12 @@ components:
|
||||
type: string
|
||||
type: array
|
||||
telemetry:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesOldAWSCollectionStrategy'
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSCollectionStrategy'
|
||||
required:
|
||||
- enabled_regions
|
||||
- telemetry
|
||||
type: object
|
||||
CloudintegrationtypesOldAWSCollectionStrategy:
|
||||
properties:
|
||||
aws_logs:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesOldAWSLogsStrategy'
|
||||
aws_metrics:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesOldAWSMetricsStrategy'
|
||||
provider:
|
||||
type: string
|
||||
s3_buckets:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
CloudintegrationtypesOldAWSLogsStrategy:
|
||||
properties:
|
||||
cloudwatch_logs_subscriptions:
|
||||
items:
|
||||
properties:
|
||||
filter_pattern:
|
||||
type: string
|
||||
log_group_name_prefix:
|
||||
type: string
|
||||
type: object
|
||||
nullable: true
|
||||
type: array
|
||||
type: object
|
||||
CloudintegrationtypesOldAWSMetricsStrategy:
|
||||
properties:
|
||||
cloudwatch_metric_stream_filters:
|
||||
items:
|
||||
properties:
|
||||
MetricNames:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
Namespace:
|
||||
type: string
|
||||
type: object
|
||||
nullable: true
|
||||
type: array
|
||||
type: object
|
||||
CloudintegrationtypesPostableAccount:
|
||||
properties:
|
||||
config:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesPostableAccountConfig'
|
||||
credentials:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesCredentials'
|
||||
required:
|
||||
- config
|
||||
- credentials
|
||||
type: object
|
||||
CloudintegrationtypesPostableAccountConfig:
|
||||
properties:
|
||||
aws:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSPostableAccountConfig'
|
||||
required:
|
||||
- aws
|
||||
type: object
|
||||
CloudintegrationtypesPostableAgentCheckIn:
|
||||
CloudintegrationtypesPostableAgentCheckInRequest:
|
||||
properties:
|
||||
account_id:
|
||||
type: string
|
||||
@@ -820,8 +727,6 @@ components:
|
||||
properties:
|
||||
assets:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAssets'
|
||||
cloudIntegrationService:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesCloudIntegrationService'
|
||||
dataCollected:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesDataCollected'
|
||||
icon:
|
||||
@@ -830,10 +735,12 @@ components:
|
||||
type: string
|
||||
overview:
|
||||
type: string
|
||||
supportedSignals:
|
||||
serviceConfig:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesServiceConfig'
|
||||
supported_signals:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesSupportedSignals'
|
||||
telemetryCollectionStrategy:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesTelemetryCollectionStrategy'
|
||||
$ref: '#/components/schemas/CloudintegrationtypesCollectionStrategy'
|
||||
title:
|
||||
type: string
|
||||
required:
|
||||
@@ -842,10 +749,9 @@ components:
|
||||
- icon
|
||||
- overview
|
||||
- assets
|
||||
- supportedSignals
|
||||
- supported_signals
|
||||
- dataCollected
|
||||
- telemetryCollectionStrategy
|
||||
- cloudIntegrationService
|
||||
type: object
|
||||
CloudintegrationtypesServiceConfig:
|
||||
properties:
|
||||
@@ -854,22 +760,6 @@ components:
|
||||
required:
|
||||
- aws
|
||||
type: object
|
||||
CloudintegrationtypesServiceID:
|
||||
enum:
|
||||
- alb
|
||||
- api-gateway
|
||||
- dynamodb
|
||||
- ec2
|
||||
- ecs
|
||||
- eks
|
||||
- elasticache
|
||||
- lambda
|
||||
- msk
|
||||
- rds
|
||||
- s3sync
|
||||
- sns
|
||||
- sqs
|
||||
type: string
|
||||
CloudintegrationtypesServiceMetadata:
|
||||
properties:
|
||||
enabled:
|
||||
@@ -893,13 +783,6 @@ components:
|
||||
metrics:
|
||||
type: boolean
|
||||
type: object
|
||||
CloudintegrationtypesTelemetryCollectionStrategy:
|
||||
properties:
|
||||
aws:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSTelemetryCollectionStrategy'
|
||||
required:
|
||||
- aws
|
||||
type: object
|
||||
CloudintegrationtypesUpdatableAccount:
|
||||
properties:
|
||||
config:
|
||||
@@ -3198,7 +3081,7 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesPostableAgentCheckIn'
|
||||
$ref: '#/components/schemas/CloudintegrationtypesPostableAgentCheckInRequest'
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
@@ -3206,7 +3089,7 @@ paths:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesGettableAgentCheckIn'
|
||||
$ref: '#/components/schemas/CloudintegrationtypesGettableAgentCheckInResponse'
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
@@ -3307,7 +3190,7 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesPostableAccount'
|
||||
$ref: '#/components/schemas/CloudintegrationtypesConnectionArtifactRequest'
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
@@ -3315,7 +3198,7 @@ paths:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesGettableAccountWithConnectionArtifact'
|
||||
$ref: '#/components/schemas/CloudintegrationtypesGettableAccountWithArtifact'
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
@@ -3511,61 +3394,6 @@ paths:
|
||||
summary: Update account
|
||||
tags:
|
||||
- cloudintegration
|
||||
/api/v1/cloud_integrations/{cloud_provider}/accounts/{id}/services/{service_id}:
|
||||
put:
|
||||
deprecated: false
|
||||
description: This endpoint updates a service for the specified cloud provider
|
||||
operationId: UpdateService
|
||||
parameters:
|
||||
- in: path
|
||||
name: cloud_provider
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- in: path
|
||||
name: service_id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesUpdatableService'
|
||||
responses:
|
||||
"204":
|
||||
description: No Content
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Unauthorized
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Forbidden
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
summary: Update service
|
||||
tags:
|
||||
- cloudintegration
|
||||
/api/v1/cloud_integrations/{cloud_provider}/accounts/check_in:
|
||||
post:
|
||||
deprecated: false
|
||||
@@ -3581,7 +3409,7 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesPostableAgentCheckIn'
|
||||
$ref: '#/components/schemas/CloudintegrationtypesPostableAgentCheckInRequest'
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
@@ -3589,7 +3417,7 @@ paths:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesGettableAgentCheckIn'
|
||||
$ref: '#/components/schemas/CloudintegrationtypesGettableAgentCheckInResponse'
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
@@ -3623,59 +3451,6 @@ paths:
|
||||
summary: Agent check-in
|
||||
tags:
|
||||
- cloudintegration
|
||||
/api/v1/cloud_integrations/{cloud_provider}/credentials:
|
||||
get:
|
||||
deprecated: false
|
||||
description: This endpoint retrieves the connection credentials required for
|
||||
integration
|
||||
operationId: GetConnectionCredentials
|
||||
parameters:
|
||||
- in: path
|
||||
name: cloud_provider
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesCredentials'
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
type: object
|
||||
description: OK
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Unauthorized
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Forbidden
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
summary: Get connection credentials
|
||||
tags:
|
||||
- cloudintegration
|
||||
/api/v1/cloud_integrations/{cloud_provider}/services:
|
||||
get:
|
||||
deprecated: false
|
||||
@@ -3786,6 +3561,55 @@ paths:
|
||||
summary: Get service
|
||||
tags:
|
||||
- cloudintegration
|
||||
put:
|
||||
deprecated: false
|
||||
description: This endpoint updates a service for the specified cloud provider
|
||||
operationId: UpdateService
|
||||
parameters:
|
||||
- in: path
|
||||
name: cloud_provider
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- in: path
|
||||
name: service_id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesUpdatableService'
|
||||
responses:
|
||||
"204":
|
||||
description: No Content
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Unauthorized
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Forbidden
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
summary: Update service
|
||||
tags:
|
||||
- cloudintegration
|
||||
/api/v1/complete/google:
|
||||
get:
|
||||
deprecated: false
|
||||
|
||||
@@ -227,7 +227,7 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*h
|
||||
s.config.APIServer.Timeout.Default,
|
||||
s.config.APIServer.Timeout.Max,
|
||||
).Wrap)
|
||||
r.Use(middleware.NewAudit(s.signoz.Instrumentation.Logger(), s.config.APIServer.Logging.ExcludedRoutes, s.signoz.Auditor).Wrap)
|
||||
r.Use(middleware.NewAudit(s.signoz.Instrumentation.Logger(), s.config.APIServer.Logging.ExcludedRoutes, nil).Wrap)
|
||||
r.Use(middleware.NewComment().Wrap)
|
||||
|
||||
apiHandler.RegisterRoutes(r, am)
|
||||
|
||||
@@ -139,12 +139,10 @@
|
||||
"react-helmet-async": "1.3.0",
|
||||
"react-hook-form": "7.71.2",
|
||||
"react-i18next": "^11.16.1",
|
||||
"react-json-tree": "0.20.0",
|
||||
"react-lottie": "1.2.10",
|
||||
"react-markdown": "8.0.7",
|
||||
"react-query": "3.39.3",
|
||||
"react-redux": "^7.2.2",
|
||||
"react-rnd": "10.5.3",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-router-dom-v5-compat": "6.27.0",
|
||||
"react-syntax-highlighter": "15.5.0",
|
||||
|
||||
@@ -24,8 +24,8 @@ import type {
|
||||
AgentCheckInDeprecated200,
|
||||
AgentCheckInDeprecatedPathParameters,
|
||||
AgentCheckInPathParameters,
|
||||
CloudintegrationtypesPostableAccountDTO,
|
||||
CloudintegrationtypesPostableAgentCheckInDTO,
|
||||
CloudintegrationtypesConnectionArtifactRequestDTO,
|
||||
CloudintegrationtypesPostableAgentCheckInRequestDTO,
|
||||
CloudintegrationtypesUpdatableAccountDTO,
|
||||
CloudintegrationtypesUpdatableServiceDTO,
|
||||
CreateAccount200,
|
||||
@@ -33,8 +33,6 @@ import type {
|
||||
DisconnectAccountPathParameters,
|
||||
GetAccount200,
|
||||
GetAccountPathParameters,
|
||||
GetConnectionCredentials200,
|
||||
GetConnectionCredentialsPathParameters,
|
||||
GetService200,
|
||||
GetServicePathParameters,
|
||||
ListAccounts200,
|
||||
@@ -53,14 +51,14 @@ import type {
|
||||
*/
|
||||
export const agentCheckInDeprecated = (
|
||||
{ cloudProvider }: AgentCheckInDeprecatedPathParameters,
|
||||
cloudintegrationtypesPostableAgentCheckInDTO: BodyType<CloudintegrationtypesPostableAgentCheckInDTO>,
|
||||
cloudintegrationtypesPostableAgentCheckInRequestDTO: BodyType<CloudintegrationtypesPostableAgentCheckInRequestDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<AgentCheckInDeprecated200>({
|
||||
url: `/api/v1/cloud-integrations/${cloudProvider}/agent-check-in`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: cloudintegrationtypesPostableAgentCheckInDTO,
|
||||
data: cloudintegrationtypesPostableAgentCheckInRequestDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
@@ -74,7 +72,7 @@ export const getAgentCheckInDeprecatedMutationOptions = <
|
||||
TError,
|
||||
{
|
||||
pathParams: AgentCheckInDeprecatedPathParameters;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInDTO>;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInRequestDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
@@ -83,7 +81,7 @@ export const getAgentCheckInDeprecatedMutationOptions = <
|
||||
TError,
|
||||
{
|
||||
pathParams: AgentCheckInDeprecatedPathParameters;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInDTO>;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInRequestDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
@@ -100,7 +98,7 @@ export const getAgentCheckInDeprecatedMutationOptions = <
|
||||
Awaited<ReturnType<typeof agentCheckInDeprecated>>,
|
||||
{
|
||||
pathParams: AgentCheckInDeprecatedPathParameters;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInDTO>;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInRequestDTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
@@ -114,7 +112,7 @@ export const getAgentCheckInDeprecatedMutationOptions = <
|
||||
export type AgentCheckInDeprecatedMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof agentCheckInDeprecated>>
|
||||
>;
|
||||
export type AgentCheckInDeprecatedMutationBody = BodyType<CloudintegrationtypesPostableAgentCheckInDTO>;
|
||||
export type AgentCheckInDeprecatedMutationBody = BodyType<CloudintegrationtypesPostableAgentCheckInRequestDTO>;
|
||||
export type AgentCheckInDeprecatedMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
@@ -130,7 +128,7 @@ export const useAgentCheckInDeprecated = <
|
||||
TError,
|
||||
{
|
||||
pathParams: AgentCheckInDeprecatedPathParameters;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInDTO>;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInRequestDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
@@ -139,7 +137,7 @@ export const useAgentCheckInDeprecated = <
|
||||
TError,
|
||||
{
|
||||
pathParams: AgentCheckInDeprecatedPathParameters;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInDTO>;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInRequestDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
@@ -257,14 +255,14 @@ export const invalidateListAccounts = async (
|
||||
*/
|
||||
export const createAccount = (
|
||||
{ cloudProvider }: CreateAccountPathParameters,
|
||||
cloudintegrationtypesPostableAccountDTO: BodyType<CloudintegrationtypesPostableAccountDTO>,
|
||||
cloudintegrationtypesConnectionArtifactRequestDTO: BodyType<CloudintegrationtypesConnectionArtifactRequestDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<CreateAccount200>({
|
||||
url: `/api/v1/cloud_integrations/${cloudProvider}/accounts`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: cloudintegrationtypesPostableAccountDTO,
|
||||
data: cloudintegrationtypesConnectionArtifactRequestDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
@@ -278,7 +276,7 @@ export const getCreateAccountMutationOptions = <
|
||||
TError,
|
||||
{
|
||||
pathParams: CreateAccountPathParameters;
|
||||
data: BodyType<CloudintegrationtypesPostableAccountDTO>;
|
||||
data: BodyType<CloudintegrationtypesConnectionArtifactRequestDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
@@ -287,7 +285,7 @@ export const getCreateAccountMutationOptions = <
|
||||
TError,
|
||||
{
|
||||
pathParams: CreateAccountPathParameters;
|
||||
data: BodyType<CloudintegrationtypesPostableAccountDTO>;
|
||||
data: BodyType<CloudintegrationtypesConnectionArtifactRequestDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
@@ -304,7 +302,7 @@ export const getCreateAccountMutationOptions = <
|
||||
Awaited<ReturnType<typeof createAccount>>,
|
||||
{
|
||||
pathParams: CreateAccountPathParameters;
|
||||
data: BodyType<CloudintegrationtypesPostableAccountDTO>;
|
||||
data: BodyType<CloudintegrationtypesConnectionArtifactRequestDTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
@@ -318,7 +316,7 @@ export const getCreateAccountMutationOptions = <
|
||||
export type CreateAccountMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof createAccount>>
|
||||
>;
|
||||
export type CreateAccountMutationBody = BodyType<CloudintegrationtypesPostableAccountDTO>;
|
||||
export type CreateAccountMutationBody = BodyType<CloudintegrationtypesConnectionArtifactRequestDTO>;
|
||||
export type CreateAccountMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
@@ -333,7 +331,7 @@ export const useCreateAccount = <
|
||||
TError,
|
||||
{
|
||||
pathParams: CreateAccountPathParameters;
|
||||
data: BodyType<CloudintegrationtypesPostableAccountDTO>;
|
||||
data: BodyType<CloudintegrationtypesConnectionArtifactRequestDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
@@ -342,7 +340,7 @@ export const useCreateAccount = <
|
||||
TError,
|
||||
{
|
||||
pathParams: CreateAccountPathParameters;
|
||||
data: BodyType<CloudintegrationtypesPostableAccountDTO>;
|
||||
data: BodyType<CloudintegrationtypesConnectionArtifactRequestDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
@@ -630,117 +628,20 @@ export const useUpdateAccount = <
|
||||
|
||||
return useMutation(mutationOptions);
|
||||
};
|
||||
/**
|
||||
* This endpoint updates a service for the specified cloud provider
|
||||
* @summary Update service
|
||||
*/
|
||||
export const updateService = (
|
||||
{ cloudProvider, id, serviceId }: UpdateServicePathParameters,
|
||||
cloudintegrationtypesUpdatableServiceDTO: BodyType<CloudintegrationtypesUpdatableServiceDTO>,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v1/cloud_integrations/${cloudProvider}/accounts/${id}/services/${serviceId}`,
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: cloudintegrationtypesUpdatableServiceDTO,
|
||||
});
|
||||
};
|
||||
|
||||
export const getUpdateServiceMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateService>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateServicePathParameters;
|
||||
data: BodyType<CloudintegrationtypesUpdatableServiceDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateService>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateServicePathParameters;
|
||||
data: BodyType<CloudintegrationtypesUpdatableServiceDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['updateService'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof updateService>>,
|
||||
{
|
||||
pathParams: UpdateServicePathParameters;
|
||||
data: BodyType<CloudintegrationtypesUpdatableServiceDTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
|
||||
return updateService(pathParams, data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type UpdateServiceMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof updateService>>
|
||||
>;
|
||||
export type UpdateServiceMutationBody = BodyType<CloudintegrationtypesUpdatableServiceDTO>;
|
||||
export type UpdateServiceMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Update service
|
||||
*/
|
||||
export const useUpdateService = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateService>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateServicePathParameters;
|
||||
data: BodyType<CloudintegrationtypesUpdatableServiceDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof updateService>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateServicePathParameters;
|
||||
data: BodyType<CloudintegrationtypesUpdatableServiceDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
const mutationOptions = getUpdateServiceMutationOptions(options);
|
||||
|
||||
return useMutation(mutationOptions);
|
||||
};
|
||||
/**
|
||||
* This endpoint is called by the deployed agent to check in
|
||||
* @summary Agent check-in
|
||||
*/
|
||||
export const agentCheckIn = (
|
||||
{ cloudProvider }: AgentCheckInPathParameters,
|
||||
cloudintegrationtypesPostableAgentCheckInDTO: BodyType<CloudintegrationtypesPostableAgentCheckInDTO>,
|
||||
cloudintegrationtypesPostableAgentCheckInRequestDTO: BodyType<CloudintegrationtypesPostableAgentCheckInRequestDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<AgentCheckIn200>({
|
||||
url: `/api/v1/cloud_integrations/${cloudProvider}/accounts/check_in`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: cloudintegrationtypesPostableAgentCheckInDTO,
|
||||
data: cloudintegrationtypesPostableAgentCheckInRequestDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
@@ -754,7 +655,7 @@ export const getAgentCheckInMutationOptions = <
|
||||
TError,
|
||||
{
|
||||
pathParams: AgentCheckInPathParameters;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInDTO>;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInRequestDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
@@ -763,7 +664,7 @@ export const getAgentCheckInMutationOptions = <
|
||||
TError,
|
||||
{
|
||||
pathParams: AgentCheckInPathParameters;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInDTO>;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInRequestDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
@@ -780,7 +681,7 @@ export const getAgentCheckInMutationOptions = <
|
||||
Awaited<ReturnType<typeof agentCheckIn>>,
|
||||
{
|
||||
pathParams: AgentCheckInPathParameters;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInDTO>;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInRequestDTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
@@ -794,7 +695,7 @@ export const getAgentCheckInMutationOptions = <
|
||||
export type AgentCheckInMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof agentCheckIn>>
|
||||
>;
|
||||
export type AgentCheckInMutationBody = BodyType<CloudintegrationtypesPostableAgentCheckInDTO>;
|
||||
export type AgentCheckInMutationBody = BodyType<CloudintegrationtypesPostableAgentCheckInRequestDTO>;
|
||||
export type AgentCheckInMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
@@ -809,7 +710,7 @@ export const useAgentCheckIn = <
|
||||
TError,
|
||||
{
|
||||
pathParams: AgentCheckInPathParameters;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInDTO>;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInRequestDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
@@ -818,7 +719,7 @@ export const useAgentCheckIn = <
|
||||
TError,
|
||||
{
|
||||
pathParams: AgentCheckInPathParameters;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInDTO>;
|
||||
data: BodyType<CloudintegrationtypesPostableAgentCheckInRequestDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
@@ -826,114 +727,6 @@ export const useAgentCheckIn = <
|
||||
|
||||
return useMutation(mutationOptions);
|
||||
};
|
||||
/**
|
||||
* This endpoint retrieves the connection credentials required for integration
|
||||
* @summary Get connection credentials
|
||||
*/
|
||||
export const getConnectionCredentials = (
|
||||
{ cloudProvider }: GetConnectionCredentialsPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetConnectionCredentials200>({
|
||||
url: `/api/v1/cloud_integrations/${cloudProvider}/credentials`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetConnectionCredentialsQueryKey = ({
|
||||
cloudProvider,
|
||||
}: GetConnectionCredentialsPathParameters) => {
|
||||
return [`/api/v1/cloud_integrations/${cloudProvider}/credentials`] as const;
|
||||
};
|
||||
|
||||
export const getGetConnectionCredentialsQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getConnectionCredentials>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ cloudProvider }: GetConnectionCredentialsPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getConnectionCredentials>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ??
|
||||
getGetConnectionCredentialsQueryKey({ cloudProvider });
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getConnectionCredentials>>
|
||||
> = ({ signal }) => getConnectionCredentials({ cloudProvider }, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!cloudProvider,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getConnectionCredentials>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetConnectionCredentialsQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getConnectionCredentials>>
|
||||
>;
|
||||
export type GetConnectionCredentialsQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get connection credentials
|
||||
*/
|
||||
|
||||
export function useGetConnectionCredentials<
|
||||
TData = Awaited<ReturnType<typeof getConnectionCredentials>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ cloudProvider }: GetConnectionCredentialsPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getConnectionCredentials>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetConnectionCredentialsQueryOptions(
|
||||
{ cloudProvider },
|
||||
options,
|
||||
);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
query.queryKey = queryOptions.queryKey;
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get connection credentials
|
||||
*/
|
||||
export const invalidateGetConnectionCredentials = async (
|
||||
queryClient: QueryClient,
|
||||
{ cloudProvider }: GetConnectionCredentialsPathParameters,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetConnectionCredentialsQueryKey({ cloudProvider }) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint lists the services metadata for the specified cloud provider
|
||||
* @summary List services metadata
|
||||
@@ -1148,3 +941,101 @@ export const invalidateGetService = async (
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint updates a service for the specified cloud provider
|
||||
* @summary Update service
|
||||
*/
|
||||
export const updateService = (
|
||||
{ cloudProvider, serviceId }: UpdateServicePathParameters,
|
||||
cloudintegrationtypesUpdatableServiceDTO: BodyType<CloudintegrationtypesUpdatableServiceDTO>,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v1/cloud_integrations/${cloudProvider}/services/${serviceId}`,
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: cloudintegrationtypesUpdatableServiceDTO,
|
||||
});
|
||||
};
|
||||
|
||||
export const getUpdateServiceMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateService>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateServicePathParameters;
|
||||
data: BodyType<CloudintegrationtypesUpdatableServiceDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateService>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateServicePathParameters;
|
||||
data: BodyType<CloudintegrationtypesUpdatableServiceDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['updateService'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof updateService>>,
|
||||
{
|
||||
pathParams: UpdateServicePathParameters;
|
||||
data: BodyType<CloudintegrationtypesUpdatableServiceDTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
|
||||
return updateService(pathParams, data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type UpdateServiceMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof updateService>>
|
||||
>;
|
||||
export type UpdateServiceMutationBody = BodyType<CloudintegrationtypesUpdatableServiceDTO>;
|
||||
export type UpdateServiceMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Update service
|
||||
*/
|
||||
export const useUpdateService = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateService>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateServicePathParameters;
|
||||
data: BodyType<CloudintegrationtypesUpdatableServiceDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof updateService>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateServicePathParameters;
|
||||
data: BodyType<CloudintegrationtypesUpdatableServiceDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
const mutationOptions = getUpdateServiceMutationOptions(options);
|
||||
|
||||
return useMutation(mutationOptions);
|
||||
};
|
||||
|
||||
@@ -512,58 +512,27 @@ export interface CloudintegrationtypesAWSAccountConfigDTO {
|
||||
regions: string[];
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAWSCloudWatchLogsSubscriptionDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
filterPattern: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
logGroupNamePrefix: string;
|
||||
}
|
||||
export type CloudintegrationtypesAWSCollectionStrategyDTOS3Buckets = {
|
||||
[key: string]: string[];
|
||||
};
|
||||
|
||||
export interface CloudintegrationtypesAWSCloudWatchMetricStreamFilterDTO {
|
||||
export interface CloudintegrationtypesAWSCollectionStrategyDTO {
|
||||
aws_logs?: CloudintegrationtypesAWSLogsStrategyDTO;
|
||||
aws_metrics?: CloudintegrationtypesAWSMetricsStrategyDTO;
|
||||
/**
|
||||
* @type array
|
||||
* @type object
|
||||
*/
|
||||
metricNames?: string[];
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
namespace: string;
|
||||
s3_buckets?: CloudintegrationtypesAWSCollectionStrategyDTOS3Buckets;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAWSConnectionArtifactDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
connectionUrl: string;
|
||||
connectionURL: string;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAWSIntegrationConfigDTO {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
enabledRegions: string[];
|
||||
telemetryCollectionStrategy: CloudintegrationtypesAWSTelemetryCollectionStrategyDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAWSLogsCollectionStrategyDTO {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
subscriptions: CloudintegrationtypesAWSCloudWatchLogsSubscriptionDTO[];
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAWSMetricsCollectionStrategyDTO {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
streamFilters: CloudintegrationtypesAWSCloudWatchMetricStreamFilterDTO[];
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAWSPostableAccountConfigDTO {
|
||||
export interface CloudintegrationtypesAWSConnectionArtifactRequestDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
@@ -574,6 +543,56 @@ export interface CloudintegrationtypesAWSPostableAccountConfigDTO {
|
||||
regions: string[];
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAWSIntegrationConfigDTO {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
enabledRegions: string[];
|
||||
telemetry: CloudintegrationtypesAWSCollectionStrategyDTO;
|
||||
}
|
||||
|
||||
export type CloudintegrationtypesAWSLogsStrategyDTOCloudwatchLogsSubscriptionsItem = {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
filter_pattern?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
log_group_name_prefix?: string;
|
||||
};
|
||||
|
||||
export interface CloudintegrationtypesAWSLogsStrategyDTO {
|
||||
/**
|
||||
* @type array
|
||||
* @nullable true
|
||||
*/
|
||||
cloudwatch_logs_subscriptions?:
|
||||
| CloudintegrationtypesAWSLogsStrategyDTOCloudwatchLogsSubscriptionsItem[]
|
||||
| null;
|
||||
}
|
||||
|
||||
export type CloudintegrationtypesAWSMetricsStrategyDTOCloudwatchMetricStreamFiltersItem = {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
MetricNames?: string[];
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
Namespace?: string;
|
||||
};
|
||||
|
||||
export interface CloudintegrationtypesAWSMetricsStrategyDTO {
|
||||
/**
|
||||
* @type array
|
||||
* @nullable true
|
||||
*/
|
||||
cloudwatch_metric_stream_filters?:
|
||||
| CloudintegrationtypesAWSMetricsStrategyDTOCloudwatchMetricStreamFiltersItem[]
|
||||
| null;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAWSServiceConfigDTO {
|
||||
logs?: CloudintegrationtypesAWSServiceLogsConfigDTO;
|
||||
metrics?: CloudintegrationtypesAWSServiceMetricsConfigDTO;
|
||||
@@ -591,7 +610,7 @@ export interface CloudintegrationtypesAWSServiceLogsConfigDTO {
|
||||
/**
|
||||
* @type object
|
||||
*/
|
||||
s3Buckets?: CloudintegrationtypesAWSServiceLogsConfigDTOS3Buckets;
|
||||
s3_buckets?: CloudintegrationtypesAWSServiceLogsConfigDTOS3Buckets;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAWSServiceMetricsConfigDTO {
|
||||
@@ -601,19 +620,6 @@ export interface CloudintegrationtypesAWSServiceMetricsConfigDTO {
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
export type CloudintegrationtypesAWSTelemetryCollectionStrategyDTOS3Buckets = {
|
||||
[key: string]: string[];
|
||||
};
|
||||
|
||||
export interface CloudintegrationtypesAWSTelemetryCollectionStrategyDTO {
|
||||
logs?: CloudintegrationtypesAWSLogsCollectionStrategyDTO;
|
||||
metrics?: CloudintegrationtypesAWSMetricsCollectionStrategyDTO;
|
||||
/**
|
||||
* @type object
|
||||
*/
|
||||
s3Buckets?: CloudintegrationtypesAWSTelemetryCollectionStrategyDTOS3Buckets;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAccountDTO {
|
||||
agentReport: CloudintegrationtypesAgentReportDTO;
|
||||
config: CloudintegrationtypesAccountConfigDTO;
|
||||
@@ -687,32 +693,6 @@ export interface CloudintegrationtypesAssetsDTO {
|
||||
dashboards?: CloudintegrationtypesDashboardDTO[] | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type CloudintegrationtypesCloudIntegrationServiceDTO = {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
cloudIntegrationId?: string;
|
||||
config?: CloudintegrationtypesServiceConfigDTO;
|
||||
/**
|
||||
* @type string
|
||||
* @format date-time
|
||||
*/
|
||||
createdAt?: Date;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
id: string;
|
||||
type?: CloudintegrationtypesServiceIDDTO;
|
||||
/**
|
||||
* @type string
|
||||
* @format date-time
|
||||
*/
|
||||
updatedAt?: Date;
|
||||
} | null;
|
||||
|
||||
export interface CloudintegrationtypesCollectedLogAttributeDTO {
|
||||
/**
|
||||
* @type string
|
||||
@@ -747,27 +727,16 @@ export interface CloudintegrationtypesCollectedMetricDTO {
|
||||
unit?: string;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesCollectionStrategyDTO {
|
||||
aws: CloudintegrationtypesAWSCollectionStrategyDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesConnectionArtifactDTO {
|
||||
aws: CloudintegrationtypesAWSConnectionArtifactDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesCredentialsDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
ingestionKey: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
ingestionUrl: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
sigNozApiKey: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
sigNozApiUrl: string;
|
||||
export interface CloudintegrationtypesConnectionArtifactRequestDTO {
|
||||
aws: CloudintegrationtypesAWSConnectionArtifactRequestDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesDashboardDTO {
|
||||
@@ -799,7 +768,7 @@ export interface CloudintegrationtypesDataCollectedDTO {
|
||||
metrics?: CloudintegrationtypesCollectedMetricDTO[] | null;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesGettableAccountWithConnectionArtifactDTO {
|
||||
export interface CloudintegrationtypesGettableAccountWithArtifactDTO {
|
||||
connectionArtifact: CloudintegrationtypesConnectionArtifactDTO;
|
||||
/**
|
||||
* @type string
|
||||
@@ -814,7 +783,7 @@ export interface CloudintegrationtypesGettableAccountsDTO {
|
||||
accounts: CloudintegrationtypesAccountDTO[];
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesGettableAgentCheckInDTO {
|
||||
export interface CloudintegrationtypesGettableAgentCheckInResponseDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
@@ -862,85 +831,17 @@ export type CloudintegrationtypesIntegrationConfigDTO = {
|
||||
* @type array
|
||||
*/
|
||||
enabled_regions: string[];
|
||||
telemetry: CloudintegrationtypesOldAWSCollectionStrategyDTO;
|
||||
telemetry: CloudintegrationtypesAWSCollectionStrategyDTO;
|
||||
} | null;
|
||||
|
||||
export type CloudintegrationtypesOldAWSCollectionStrategyDTOS3Buckets = {
|
||||
[key: string]: string[];
|
||||
};
|
||||
|
||||
export interface CloudintegrationtypesOldAWSCollectionStrategyDTO {
|
||||
aws_logs?: CloudintegrationtypesOldAWSLogsStrategyDTO;
|
||||
aws_metrics?: CloudintegrationtypesOldAWSMetricsStrategyDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
provider?: string;
|
||||
/**
|
||||
* @type object
|
||||
*/
|
||||
s3_buckets?: CloudintegrationtypesOldAWSCollectionStrategyDTOS3Buckets;
|
||||
}
|
||||
|
||||
export type CloudintegrationtypesOldAWSLogsStrategyDTOCloudwatchLogsSubscriptionsItem = {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
filter_pattern?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
log_group_name_prefix?: string;
|
||||
};
|
||||
|
||||
export interface CloudintegrationtypesOldAWSLogsStrategyDTO {
|
||||
/**
|
||||
* @type array
|
||||
* @nullable true
|
||||
*/
|
||||
cloudwatch_logs_subscriptions?:
|
||||
| CloudintegrationtypesOldAWSLogsStrategyDTOCloudwatchLogsSubscriptionsItem[]
|
||||
| null;
|
||||
}
|
||||
|
||||
export type CloudintegrationtypesOldAWSMetricsStrategyDTOCloudwatchMetricStreamFiltersItem = {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
MetricNames?: string[];
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
Namespace?: string;
|
||||
};
|
||||
|
||||
export interface CloudintegrationtypesOldAWSMetricsStrategyDTO {
|
||||
/**
|
||||
* @type array
|
||||
* @nullable true
|
||||
*/
|
||||
cloudwatch_metric_stream_filters?:
|
||||
| CloudintegrationtypesOldAWSMetricsStrategyDTOCloudwatchMetricStreamFiltersItem[]
|
||||
| null;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesPostableAccountDTO {
|
||||
config: CloudintegrationtypesPostableAccountConfigDTO;
|
||||
credentials: CloudintegrationtypesCredentialsDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesPostableAccountConfigDTO {
|
||||
aws: CloudintegrationtypesAWSPostableAccountConfigDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type CloudintegrationtypesPostableAgentCheckInDTOData = {
|
||||
export type CloudintegrationtypesPostableAgentCheckInRequestDTOData = {
|
||||
[key: string]: unknown;
|
||||
} | null;
|
||||
|
||||
export interface CloudintegrationtypesPostableAgentCheckInDTO {
|
||||
export interface CloudintegrationtypesPostableAgentCheckInRequestDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
@@ -957,7 +858,7 @@ export interface CloudintegrationtypesPostableAgentCheckInDTO {
|
||||
* @type object
|
||||
* @nullable true
|
||||
*/
|
||||
data: CloudintegrationtypesPostableAgentCheckInDTOData;
|
||||
data: CloudintegrationtypesPostableAgentCheckInRequestDTOData;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
@@ -970,7 +871,6 @@ export interface CloudintegrationtypesProviderIntegrationConfigDTO {
|
||||
|
||||
export interface CloudintegrationtypesServiceDTO {
|
||||
assets: CloudintegrationtypesAssetsDTO;
|
||||
cloudIntegrationService: CloudintegrationtypesCloudIntegrationServiceDTO;
|
||||
dataCollected: CloudintegrationtypesDataCollectedDTO;
|
||||
/**
|
||||
* @type string
|
||||
@@ -984,8 +884,9 @@ export interface CloudintegrationtypesServiceDTO {
|
||||
* @type string
|
||||
*/
|
||||
overview: string;
|
||||
supportedSignals: CloudintegrationtypesSupportedSignalsDTO;
|
||||
telemetryCollectionStrategy: CloudintegrationtypesTelemetryCollectionStrategyDTO;
|
||||
serviceConfig?: CloudintegrationtypesServiceConfigDTO;
|
||||
supported_signals: CloudintegrationtypesSupportedSignalsDTO;
|
||||
telemetryCollectionStrategy: CloudintegrationtypesCollectionStrategyDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
@@ -996,21 +897,6 @@ export interface CloudintegrationtypesServiceConfigDTO {
|
||||
aws: CloudintegrationtypesAWSServiceConfigDTO;
|
||||
}
|
||||
|
||||
export enum CloudintegrationtypesServiceIDDTO {
|
||||
alb = 'alb',
|
||||
'api-gateway' = 'api-gateway',
|
||||
dynamodb = 'dynamodb',
|
||||
ec2 = 'ec2',
|
||||
ecs = 'ecs',
|
||||
eks = 'eks',
|
||||
elasticache = 'elasticache',
|
||||
lambda = 'lambda',
|
||||
msk = 'msk',
|
||||
rds = 'rds',
|
||||
s3sync = 's3sync',
|
||||
sns = 'sns',
|
||||
sqs = 'sqs',
|
||||
}
|
||||
export interface CloudintegrationtypesServiceMetadataDTO {
|
||||
/**
|
||||
* @type boolean
|
||||
@@ -1041,10 +927,6 @@ export interface CloudintegrationtypesSupportedSignalsDTO {
|
||||
metrics?: boolean;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesTelemetryCollectionStrategyDTO {
|
||||
aws: CloudintegrationtypesAWSTelemetryCollectionStrategyDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesUpdatableAccountDTO {
|
||||
config: CloudintegrationtypesAccountConfigDTO;
|
||||
}
|
||||
@@ -3568,7 +3450,7 @@ export type AgentCheckInDeprecatedPathParameters = {
|
||||
cloudProvider: string;
|
||||
};
|
||||
export type AgentCheckInDeprecated200 = {
|
||||
data: CloudintegrationtypesGettableAgentCheckInDTO;
|
||||
data: CloudintegrationtypesGettableAgentCheckInResponseDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
@@ -3590,7 +3472,7 @@ export type CreateAccountPathParameters = {
|
||||
cloudProvider: string;
|
||||
};
|
||||
export type CreateAccount200 = {
|
||||
data: CloudintegrationtypesGettableAccountWithConnectionArtifactDTO;
|
||||
data: CloudintegrationtypesGettableAccountWithArtifactDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
@@ -3617,27 +3499,11 @@ export type UpdateAccountPathParameters = {
|
||||
cloudProvider: string;
|
||||
id: string;
|
||||
};
|
||||
export type UpdateServicePathParameters = {
|
||||
cloudProvider: string;
|
||||
id: string;
|
||||
serviceId: string;
|
||||
};
|
||||
export type AgentCheckInPathParameters = {
|
||||
cloudProvider: string;
|
||||
};
|
||||
export type AgentCheckIn200 = {
|
||||
data: CloudintegrationtypesGettableAgentCheckInDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetConnectionCredentialsPathParameters = {
|
||||
cloudProvider: string;
|
||||
};
|
||||
export type GetConnectionCredentials200 = {
|
||||
data: CloudintegrationtypesCredentialsDTO;
|
||||
data: CloudintegrationtypesGettableAgentCheckInResponseDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
@@ -3667,6 +3533,10 @@ export type GetService200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type UpdateServicePathParameters = {
|
||||
cloudProvider: string;
|
||||
serviceId: string;
|
||||
};
|
||||
export type CreateSessionByGoogleCallback303 = {
|
||||
data: AuthtypesGettableTokenDTO;
|
||||
/**
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
.details-header {
|
||||
// ghost + secondary missing hover bg token in @signozhq/button
|
||||
--button-ghost-hover-background: var(--l3-background);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid var(--l1-border);
|
||||
height: 36px;
|
||||
background: var(--l2-background);
|
||||
|
||||
&__icon-btn {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--l1-foreground);
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { Button } from '@signozhq/button';
|
||||
import { X } from '@signozhq/icons';
|
||||
|
||||
import './DetailsHeader.styles.scss';
|
||||
|
||||
export interface HeaderAction {
|
||||
key: string;
|
||||
component: ReactNode; // check later if we can use direct btn itself or not.
|
||||
}
|
||||
|
||||
export interface DetailsHeaderProps {
|
||||
title: string;
|
||||
onClose: () => void;
|
||||
actions?: HeaderAction[];
|
||||
closePosition?: 'left' | 'right';
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function DetailsHeader({
|
||||
title,
|
||||
onClose,
|
||||
actions,
|
||||
closePosition = 'right',
|
||||
className,
|
||||
}: DetailsHeaderProps): JSX.Element {
|
||||
const closeButton = (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
color="secondary"
|
||||
onClick={onClose}
|
||||
aria-label="Close"
|
||||
className="details-header__icon-btn"
|
||||
>
|
||||
<X size={14} />
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={`details-header ${className || ''}`}>
|
||||
{closePosition === 'left' && closeButton}
|
||||
|
||||
<span className="details-header__title">{title}</span>
|
||||
|
||||
{actions && (
|
||||
<div className="details-header__actions">
|
||||
{actions.map((action) => (
|
||||
<div key={action.key}>{action.component}</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{closePosition === 'right' && closeButton}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DetailsHeader;
|
||||
@@ -1,7 +0,0 @@
|
||||
.details-panel-drawer {
|
||||
&__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { DrawerWrapper } from '@signozhq/drawer';
|
||||
|
||||
import './DetailsPanelDrawer.styles.scss';
|
||||
|
||||
interface DetailsPanelDrawerProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function DetailsPanelDrawer({
|
||||
isOpen,
|
||||
onClose,
|
||||
children,
|
||||
className,
|
||||
}: DetailsPanelDrawerProps): JSX.Element {
|
||||
return (
|
||||
<DrawerWrapper
|
||||
open={isOpen}
|
||||
onOpenChange={(open): void => {
|
||||
if (!open) {
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
direction="right"
|
||||
type="panel"
|
||||
showOverlay={false}
|
||||
allowOutsideClick
|
||||
className={`details-panel-drawer ${className || ''}`}
|
||||
content={<div className="details-panel-drawer__body">{children}</div>}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default DetailsPanelDrawer;
|
||||
@@ -1,8 +0,0 @@
|
||||
export type {
|
||||
DetailsHeaderProps,
|
||||
HeaderAction,
|
||||
} from './DetailsHeader/DetailsHeader';
|
||||
export { default as DetailsHeader } from './DetailsHeader/DetailsHeader';
|
||||
export { default as DetailsPanelDrawer } from './DetailsPanelDrawer';
|
||||
export type { DetailsPanelState, UseDetailsPanelOptions } from './types';
|
||||
export { default as useDetailsPanel } from './useDetailsPanel';
|
||||
@@ -1,10 +0,0 @@
|
||||
export interface DetailsPanelState {
|
||||
isOpen: boolean;
|
||||
open: () => void;
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
export interface UseDetailsPanelOptions {
|
||||
entityId: string | undefined;
|
||||
onClose?: () => void;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { DetailsPanelState, UseDetailsPanelOptions } from './types';
|
||||
|
||||
function useDetailsPanel({
|
||||
entityId,
|
||||
onClose,
|
||||
}: UseDetailsPanelOptions): DetailsPanelState {
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||
const prevEntityIdRef = useRef<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
const currentId = entityId || '';
|
||||
if (currentId && currentId !== prevEntityIdRef.current) {
|
||||
setIsOpen(true);
|
||||
}
|
||||
prevEntityIdRef.current = currentId;
|
||||
}, [entityId]);
|
||||
|
||||
const open = useCallback(() => setIsOpen(true), []);
|
||||
const close = useCallback(() => {
|
||||
setIsOpen(false);
|
||||
onClose?.();
|
||||
}, [onClose]);
|
||||
|
||||
return { isOpen, open, close };
|
||||
}
|
||||
|
||||
export default useDetailsPanel;
|
||||
@@ -677,18 +677,6 @@ function NewWidget({
|
||||
queryType: currentQuery.queryType,
|
||||
isNewPanel,
|
||||
dataSource: currentQuery?.builder?.queryData?.[0]?.dataSource,
|
||||
...(currentQuery.queryType === EQueryType.CLICKHOUSE && {
|
||||
clickhouseQueryCount: currentQuery.clickhouse_sql.length,
|
||||
clickhouseQueries: currentQuery.clickhouse_sql.map((q) => ({
|
||||
name: q.name,
|
||||
query: (q.query ?? '')
|
||||
.replace(/--[^\n]*/g, '') // strip line comments
|
||||
.replace(/\/\*[\s\S]*?\*\//g, '') // strip block comments
|
||||
.replace(/'(?:[^'\\]|\\.|'')*'/g, "'?'") // replace single-quoted strings (handles \' and '' escapes)
|
||||
.replace(/\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\b/g, '?'), // replace numeric literals (int, float, scientific)
|
||||
disabled: q.disabled,
|
||||
})),
|
||||
}),
|
||||
});
|
||||
setSaveModal(true);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
.data-viewer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
|
||||
// Toolbar — view mode switcher + copy button
|
||||
&__toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
&__mode-select {
|
||||
min-width: 90px;
|
||||
height: 28px !important;
|
||||
|
||||
.ant-select-selector {
|
||||
border-radius: 4px !important;
|
||||
border-color: var(--l2-border) !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
}
|
||||
|
||||
&__copy-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4px 8px;
|
||||
border: 1px solid var(--l2-border);
|
||||
border-radius: 4px;
|
||||
background: transparent;
|
||||
color: var(--l2-foreground);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--l1-foreground);
|
||||
background: var(--l3-background);
|
||||
}
|
||||
}
|
||||
|
||||
// Shared content container — no scroll, each view handles its own
|
||||
&__content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid var(--l2-border);
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { Copy } from '@signozhq/icons';
|
||||
// TODO: Replace antd Select with @signozhq/ui component when moving to design library
|
||||
import { Select } from 'antd';
|
||||
import { JsonView } from 'periscope/components/JsonView';
|
||||
import { PrettyView } from 'periscope/components/PrettyView';
|
||||
import { PrettyViewProps } from 'periscope/components/PrettyView';
|
||||
|
||||
import { copyToClipboard } from './utils';
|
||||
|
||||
import './DataViewer.styles.scss';
|
||||
|
||||
type ViewMode = 'pretty' | 'json';
|
||||
|
||||
export interface DataViewerProps {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
data: Record<string, any>;
|
||||
drawerKey?: string;
|
||||
prettyViewProps?: Omit<PrettyViewProps, 'data' | 'drawerKey'>;
|
||||
}
|
||||
|
||||
function DataViewer({
|
||||
data,
|
||||
drawerKey = 'default',
|
||||
prettyViewProps,
|
||||
}: DataViewerProps): JSX.Element {
|
||||
const [viewMode, setViewMode] = useState<ViewMode>('pretty');
|
||||
|
||||
const jsonString = useMemo(() => JSON.stringify(data, null, 2), [data]);
|
||||
|
||||
return (
|
||||
<div className="data-viewer">
|
||||
<div className="data-viewer__toolbar">
|
||||
<Select
|
||||
className="data-viewer__mode-select"
|
||||
size="small"
|
||||
value={viewMode}
|
||||
onChange={(value: ViewMode): void => setViewMode(value)}
|
||||
options={[
|
||||
{ label: 'Pretty', value: 'pretty' },
|
||||
{ label: 'JSON', value: 'json' },
|
||||
]}
|
||||
getPopupContainer={(trigger): HTMLElement =>
|
||||
trigger.parentElement || document.body
|
||||
}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="data-viewer__copy-btn"
|
||||
onClick={(): void => copyToClipboard(data)}
|
||||
aria-label="Copy JSON"
|
||||
>
|
||||
<Copy size={14} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="data-viewer__content">
|
||||
{viewMode === 'pretty' && (
|
||||
<PrettyView data={data} drawerKey={drawerKey} {...prettyViewProps} />
|
||||
)}
|
||||
{viewMode === 'json' && <JsonView data={jsonString} />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DataViewer;
|
||||
@@ -1,2 +0,0 @@
|
||||
export type { DataViewerProps } from './DataViewer';
|
||||
export { default as DataViewer } from './DataViewer';
|
||||
@@ -1,12 +0,0 @@
|
||||
import { toast } from '@signozhq/sonner';
|
||||
|
||||
export function copyToClipboard(value: unknown): void {
|
||||
const text =
|
||||
typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value);
|
||||
// eslint-disable-next-line no-restricted-properties
|
||||
navigator.clipboard.writeText(text);
|
||||
toast.success('Copied to clipboard', {
|
||||
richColors: true,
|
||||
position: 'top-right',
|
||||
});
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
.floating-panel {
|
||||
z-index: 999;
|
||||
background: var(--l1-background);
|
||||
border: 1px solid var(--l2-border);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
|
||||
overflow: hidden;
|
||||
|
||||
// Inner div fills the Rnd-controlled size
|
||||
&__inner {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// Drag handle — any child with this class becomes the drag target
|
||||
.floating-panel__drag-handle {
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
import { ComponentProps } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { Rnd } from 'react-rnd';
|
||||
|
||||
import './FloatingPanel.styles.scss';
|
||||
|
||||
type EnableResizing = ComponentProps<typeof Rnd>['enableResizing'];
|
||||
|
||||
export interface FloatingPanelProps {
|
||||
isOpen: boolean;
|
||||
children: React.ReactNode;
|
||||
defaultPosition?: { x: number; y: number };
|
||||
width?: number;
|
||||
height?: number;
|
||||
minWidth?: number;
|
||||
minHeight?: number;
|
||||
enableResizing?: EnableResizing;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function FloatingPanel({
|
||||
isOpen,
|
||||
children,
|
||||
defaultPosition,
|
||||
width = 560,
|
||||
height = 600,
|
||||
minWidth = 400,
|
||||
minHeight = 300,
|
||||
enableResizing,
|
||||
className,
|
||||
}: FloatingPanelProps): JSX.Element | null {
|
||||
if (!isOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const initialPosition = defaultPosition || {
|
||||
x: window.innerWidth - width - 24,
|
||||
y: 80,
|
||||
};
|
||||
|
||||
return createPortal(
|
||||
<Rnd
|
||||
default={{
|
||||
x: initialPosition.x,
|
||||
y: initialPosition.y,
|
||||
width,
|
||||
height,
|
||||
}}
|
||||
dragHandleClassName="floating-panel__drag-handle"
|
||||
minWidth={minWidth}
|
||||
minHeight={minHeight}
|
||||
onDrag={(_e, d): void | false => {
|
||||
const HEADER_HEIGHT = 40;
|
||||
// Top: don't allow header to go above viewport
|
||||
if (d.y < 0) {
|
||||
return false;
|
||||
}
|
||||
// Left: don't allow panel to go off-screen left
|
||||
if (d.x < 0) {
|
||||
return false;
|
||||
}
|
||||
// Bottom: only header needs to be visible
|
||||
if (d.y > window.innerHeight - HEADER_HEIGHT) {
|
||||
return false;
|
||||
}
|
||||
// Right: at least the close button (~40px) stays visible
|
||||
if (d.x > window.innerWidth - 40) {
|
||||
return false;
|
||||
}
|
||||
}}
|
||||
className={`floating-panel ${className || ''}`}
|
||||
enableResizing={enableResizing}
|
||||
>
|
||||
<div className="floating-panel__inner">{children}</div>
|
||||
</Rnd>,
|
||||
document.body,
|
||||
);
|
||||
}
|
||||
|
||||
export default FloatingPanel;
|
||||
@@ -1,2 +0,0 @@
|
||||
export type { FloatingPanelProps } from './FloatingPanel';
|
||||
export { default as FloatingPanel } from './FloatingPanel';
|
||||
@@ -1,22 +0,0 @@
|
||||
.json-view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
|
||||
&__footer {
|
||||
flex-shrink: 0;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-top: 1px solid var(--l2-border);
|
||||
}
|
||||
|
||||
&__wrap-toggle {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-left: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
import MEditor, { EditorProps, Monaco } from '@monaco-editor/react';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Switch, Typography } from 'antd';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
|
||||
import './JsonView.styles.scss';
|
||||
|
||||
export interface JsonViewProps {
|
||||
data: string;
|
||||
height?: string;
|
||||
}
|
||||
|
||||
const editorOptions: EditorProps['options'] = {
|
||||
automaticLayout: true,
|
||||
readOnly: true,
|
||||
wordWrap: 'on',
|
||||
minimap: { enabled: false },
|
||||
fontWeight: 400,
|
||||
fontFamily: 'SF Mono, Geist Mono, Fira Code, monospace',
|
||||
fontSize: 12,
|
||||
lineHeight: '18px',
|
||||
colorDecorators: true,
|
||||
scrollBeyondLastLine: false,
|
||||
decorationsOverviewRuler: false,
|
||||
scrollbar: { vertical: 'hidden', horizontal: 'hidden' },
|
||||
folding: false,
|
||||
};
|
||||
|
||||
function setEditorTheme(monaco: Monaco): void {
|
||||
monaco.editor.defineTheme('signoz-dark', {
|
||||
base: 'vs-dark',
|
||||
inherit: true,
|
||||
rules: [
|
||||
{ token: 'string.key.json', foreground: Color.BG_VANILLA_400 },
|
||||
{ token: 'string.value.json', foreground: Color.BG_ROBIN_400 },
|
||||
],
|
||||
colors: {
|
||||
'editor.background': '#00000000', // transparent
|
||||
},
|
||||
fontFamily: 'SF Mono, Geist Mono, Fira Code, monospace',
|
||||
fontSize: 12,
|
||||
fontWeight: 'normal',
|
||||
lineHeight: 18,
|
||||
letterSpacing: -0.06,
|
||||
});
|
||||
}
|
||||
|
||||
function JsonView({ data, height = '575px' }: JsonViewProps): JSX.Element {
|
||||
const [isWrapWord, setIsWrapWord] = useState(true);
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
return (
|
||||
<div className="json-view">
|
||||
<MEditor
|
||||
value={data}
|
||||
language="json"
|
||||
options={{ ...editorOptions, wordWrap: isWrapWord ? 'on' : 'off' }}
|
||||
onChange={(): void => {}}
|
||||
height={height}
|
||||
theme={isDarkMode ? 'signoz-dark' : 'light'}
|
||||
beforeMount={setEditorTheme}
|
||||
/>
|
||||
<div className="json-view__footer">
|
||||
<div className="json-view__wrap-toggle">
|
||||
<Typography.Text>Wrap text</Typography.Text>
|
||||
<Switch
|
||||
checked={isWrapWord}
|
||||
onChange={(checked): void => setIsWrapWord(checked)}
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default JsonView;
|
||||
@@ -1,2 +0,0 @@
|
||||
export type { JsonViewProps } from './JsonView';
|
||||
export { default as JsonView } from './JsonView';
|
||||
@@ -1,67 +1,43 @@
|
||||
.key-value-label {
|
||||
display: flex;
|
||||
border: 1px solid var(--l2-border);
|
||||
align-items: center;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
border-radius: 2px;
|
||||
|
||||
&--row {
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
&--column {
|
||||
flex-direction: column;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
gap: 4px;
|
||||
padding: 8px;
|
||||
|
||||
.key-value-label__key {
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
padding: 0;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.48px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.key-value-label__value {
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
flex-wrap: wrap;
|
||||
|
||||
&__key,
|
||||
&__value {
|
||||
padding: 6px;
|
||||
padding: 1px 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
letter-spacing: -0.005em;
|
||||
&,
|
||||
&:hover {
|
||||
color: var(--l1-foreground);
|
||||
color: var(--text-vanilla-400);
|
||||
}
|
||||
}
|
||||
|
||||
&__key {
|
||||
background: var(--l2-background);
|
||||
background: var(--bg-ink-400);
|
||||
border-radius: 2px 0 0 2px;
|
||||
}
|
||||
|
||||
&__value {
|
||||
background: var(--l3-background);
|
||||
background: var(--bg-slate-400);
|
||||
}
|
||||
}
|
||||
|
||||
&--clickable {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.lightMode {
|
||||
.key-value-label {
|
||||
border-color: var(--bg-vanilla-400);
|
||||
&__key,
|
||||
&__value {
|
||||
color: var(--text-ink-400);
|
||||
}
|
||||
&__key {
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
&__value {
|
||||
background: var(--bg-vanilla-200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +1,29 @@
|
||||
import { useMemo } from 'react';
|
||||
import { Tooltip } from 'antd';
|
||||
|
||||
import TrimmedText from '../TrimmedText/TrimmedText';
|
||||
|
||||
import './KeyValueLabel.styles.scss';
|
||||
|
||||
// Rethink this component later
|
||||
type KeyValueLabelProps = {
|
||||
badgeKey: string | React.ReactNode;
|
||||
badgeValue: string | React.ReactNode;
|
||||
direction?: 'row' | 'column';
|
||||
badgeValue: string;
|
||||
maxCharacters?: number;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
export default function KeyValueLabel({
|
||||
badgeKey,
|
||||
badgeValue,
|
||||
direction = 'row',
|
||||
maxCharacters = 20,
|
||||
onClick,
|
||||
}: KeyValueLabelProps): JSX.Element | null {
|
||||
const isUrl = useMemo(() => /^https?:\/\//.test(badgeValue), [badgeValue]);
|
||||
|
||||
if (!badgeKey || !badgeValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const renderValue = (): JSX.Element => {
|
||||
if (typeof badgeValue !== 'string') {
|
||||
return <div className="key-value-label__value">{badgeValue}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip title={badgeValue}>
|
||||
<div
|
||||
className={`key-value-label__value ${
|
||||
onClick ? 'key-value-label__value--clickable' : ''
|
||||
}`}
|
||||
onClick={onClick}
|
||||
role={onClick ? 'button' : undefined}
|
||||
tabIndex={onClick ? 0 : undefined}
|
||||
onKeyDown={
|
||||
onClick
|
||||
? (e): void => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
onClick();
|
||||
}
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<TrimmedText text={badgeValue} maxCharacters={maxCharacters} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`key-value-label key-value-label--${direction}`}>
|
||||
<div className="key-value-label">
|
||||
<div className="key-value-label__key">
|
||||
{typeof badgeKey === 'string' ? (
|
||||
<TrimmedText text={badgeKey} maxCharacters={maxCharacters} />
|
||||
@@ -63,13 +31,26 @@ export default function KeyValueLabel({
|
||||
badgeKey
|
||||
)}
|
||||
</div>
|
||||
{renderValue()}
|
||||
{isUrl ? (
|
||||
<a
|
||||
href={badgeValue}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="key-value-label__value"
|
||||
>
|
||||
<TrimmedText text={badgeValue} maxCharacters={maxCharacters} />
|
||||
</a>
|
||||
) : (
|
||||
<Tooltip title={badgeValue}>
|
||||
<div className="key-value-label__value">
|
||||
<TrimmedText text={badgeValue} maxCharacters={maxCharacters} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
KeyValueLabel.defaultProps = {
|
||||
maxCharacters: 20,
|
||||
direction: 'row',
|
||||
onClick: undefined,
|
||||
};
|
||||
|
||||
@@ -1,191 +0,0 @@
|
||||
.pretty-view {
|
||||
padding: 0;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__search-input {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
margin-bottom: 8px;
|
||||
width: 100%;
|
||||
border: 1px solid var(--l2-border) !important;
|
||||
border-radius: 4px;
|
||||
font-family: 'SF Mono', 'Geist Mono', 'Fira Code', monospace !important;
|
||||
font-size: 12px !important;
|
||||
line-height: 18px !important;
|
||||
background: var(--l1-background) !important;
|
||||
outline: none !important;
|
||||
box-shadow: none !important;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--primary) !important;
|
||||
outline: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Font spec: SF Mono, 12px, 400, 18px line-height, -0.5% letter-spacing
|
||||
font-family: 'SF Mono', 'Geist Mono', 'Fira Code', monospace;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
letter-spacing: -0.06px;
|
||||
|
||||
// Override react-json-tree inline styles
|
||||
ul {
|
||||
margin: 0 !important;
|
||||
padding-left: 16px !important;
|
||||
list-style: none !important;
|
||||
background-color: transparent !important;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
> ul,
|
||||
&__pinned > ul {
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
// Force font on all tree elements
|
||||
li,
|
||||
span,
|
||||
label {
|
||||
font-family: inherit !important;
|
||||
font-size: inherit !important;
|
||||
line-height: inherit !important;
|
||||
letter-spacing: inherit !important;
|
||||
}
|
||||
|
||||
// Fix react-json-tree text-indent wrapping
|
||||
li {
|
||||
text-indent: 0 !important;
|
||||
margin-left: 0 !important;
|
||||
padding-top: 2px !important;
|
||||
padding-bottom: 2px !important;
|
||||
}
|
||||
|
||||
.pretty-view__actions {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
// Leaf node row — hover highlights only this row
|
||||
&__row {
|
||||
border-radius: 2px;
|
||||
display: flex !important;
|
||||
align-items: baseline;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--l3-background);
|
||||
|
||||
.pretty-view__actions {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Push actions to the right edge
|
||||
> span {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Nested node (object/array) — hover only on the label line, not children
|
||||
&__nested-row {
|
||||
> label,
|
||||
> span:not(ul span) {
|
||||
border-radius: 2px;
|
||||
padding: 1px 2px;
|
||||
display: inline !important; // keep item string inline with label
|
||||
}
|
||||
|
||||
// Highlight label + item string on hover, show actions
|
||||
&:hover > label,
|
||||
&:hover > label + span {
|
||||
background-color: var(--l3-background);
|
||||
}
|
||||
|
||||
&:hover > label + span .pretty-view__actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
// In nested rows, value-row should not take full width
|
||||
.pretty-view__value-row {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// Value + actions wrapper (from valueRenderer / getItemString)
|
||||
&__value-row {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
// In leaf rows, value-row takes full width to push ... to the right
|
||||
&__row &__value-row {
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// ... actions button — hidden by default, shown on row hover
|
||||
&__actions {
|
||||
opacity: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
color: var(--l2-foreground);
|
||||
transition: opacity 0.1s ease;
|
||||
padding: 0 2px;
|
||||
|
||||
&:hover {
|
||||
color: var(--l1-foreground);
|
||||
}
|
||||
}
|
||||
|
||||
// Pinned items section
|
||||
&__pinned {
|
||||
border-bottom: 1px solid var(--l2-border);
|
||||
padding-bottom: 8px;
|
||||
margin-bottom: 8px;
|
||||
padding-left: 0px;
|
||||
|
||||
li {
|
||||
margin-left: 0 !important;
|
||||
padding-left: 0 !important;
|
||||
display: flex !important;
|
||||
align-items: baseline;
|
||||
}
|
||||
}
|
||||
|
||||
&__pinned-header {
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
color: var(--l3-foreground);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
&__pinned-label {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
&__pinned-icon {
|
||||
flex-shrink: 0;
|
||||
color: var(--text-robin-400);
|
||||
cursor: pointer;
|
||||
fill: var(--text-robin-400);
|
||||
transition: fill 0.1s ease;
|
||||
|
||||
&:hover {
|
||||
color: var(--text-robin-300);
|
||||
fill: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,300 +0,0 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { JSONTree, KeyPath } from 'react-json-tree';
|
||||
import { Copy, Ellipsis, Pin, PinOff } from '@signozhq/icons';
|
||||
import { Input } from '@signozhq/input';
|
||||
import type { MenuProps } from 'antd';
|
||||
// TODO: Replace antd Dropdown with @signozhq/ui component when moving to design library
|
||||
import { Dropdown } from 'antd';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
|
||||
import { darkTheme, lightTheme, themeExtension } from './constants';
|
||||
import usePinnedFields from './hooks/usePinnedFields';
|
||||
import useSearchFilter, { filterTree } from './hooks/useSearchFilter';
|
||||
import {
|
||||
copyToClipboard,
|
||||
keyPathToDisplayString,
|
||||
keyPathToForward,
|
||||
serializeKeyPath,
|
||||
} from './utils';
|
||||
|
||||
import './PrettyView.styles.scss';
|
||||
|
||||
export interface FieldContext {
|
||||
fieldKey: string;
|
||||
fieldKeyPath: (string | number)[];
|
||||
fieldValue: unknown;
|
||||
isNested: boolean;
|
||||
}
|
||||
|
||||
export interface PrettyViewAction {
|
||||
key: string;
|
||||
label: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
onClick: (context: FieldContext) => void;
|
||||
}
|
||||
|
||||
export interface PrettyViewProps {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
data: Record<string, any>;
|
||||
actions?: PrettyViewAction[];
|
||||
searchable?: boolean;
|
||||
showPinned?: boolean;
|
||||
drawerKey?: string;
|
||||
}
|
||||
|
||||
function PrettyView({
|
||||
data,
|
||||
actions,
|
||||
searchable = true,
|
||||
showPinned = false,
|
||||
drawerKey = 'default',
|
||||
}: PrettyViewProps): JSX.Element {
|
||||
const isDarkMode = useIsDarkMode();
|
||||
const { searchQuery, setSearchQuery, filteredData } = useSearchFilter(data);
|
||||
const {
|
||||
isPinned,
|
||||
togglePin,
|
||||
pinnedEntries,
|
||||
pinnedData,
|
||||
displayKeyToForwardPath,
|
||||
} = usePinnedFields(data, drawerKey);
|
||||
|
||||
const filteredPinnedData = useMemo(() => {
|
||||
const trimmed = searchQuery.trim();
|
||||
if (!trimmed) {
|
||||
return pinnedData;
|
||||
}
|
||||
return filterTree(pinnedData, trimmed) || {};
|
||||
}, [pinnedData, searchQuery]);
|
||||
|
||||
const theme = useMemo(
|
||||
() => ({
|
||||
extend: isDarkMode ? darkTheme : lightTheme,
|
||||
...themeExtension,
|
||||
}),
|
||||
[isDarkMode],
|
||||
);
|
||||
|
||||
const shouldExpandNodeInitially = useCallback(
|
||||
(
|
||||
_keyPath: readonly (string | number)[],
|
||||
_data: unknown,
|
||||
level: number,
|
||||
): boolean => level < 5,
|
||||
[],
|
||||
);
|
||||
|
||||
const buildMenuItems = useCallback(
|
||||
(context: FieldContext): MenuProps['items'] => {
|
||||
// todo: drive dropdown through config.
|
||||
const copyItem = {
|
||||
key: 'copy',
|
||||
label: 'Copy',
|
||||
icon: <Copy size={12} />,
|
||||
onClick: (): void => {
|
||||
copyToClipboard(context.fieldValue);
|
||||
},
|
||||
};
|
||||
|
||||
const items: NonNullable<MenuProps['items']> = [copyItem];
|
||||
|
||||
// Pin action only for leaf nodes
|
||||
if (!context.isNested) {
|
||||
// Resolve the correct forward path — pinned tree uses display keys
|
||||
// which don't match the original serialized path
|
||||
const resolvedPath =
|
||||
displayKeyToForwardPath[context.fieldKey] || context.fieldKeyPath;
|
||||
const serialized = serializeKeyPath(resolvedPath);
|
||||
const pinned = isPinned(serialized);
|
||||
|
||||
items.push({
|
||||
key: 'pin',
|
||||
label: pinned ? 'Unpin field' : 'Pin field',
|
||||
icon: pinned ? <PinOff size={12} /> : <Pin size={12} />,
|
||||
onClick: (): void => {
|
||||
togglePin(resolvedPath);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (actions && actions.length > 0) {
|
||||
//todo: why this divider?
|
||||
items.push({ type: 'divider' as const, key: 'divider' });
|
||||
actions.forEach((action) => {
|
||||
items.push({
|
||||
key: action.key,
|
||||
label: action.label,
|
||||
icon: action.icon,
|
||||
onClick: (): void => {
|
||||
action.onClick(context);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
[actions, isPinned, togglePin, displayKeyToForwardPath],
|
||||
);
|
||||
|
||||
const renderWithActions = useCallback(
|
||||
({
|
||||
content,
|
||||
fieldKey,
|
||||
fieldKeyPath,
|
||||
value,
|
||||
isNested,
|
||||
}: {
|
||||
content: React.ReactNode;
|
||||
fieldKey: string;
|
||||
fieldKeyPath: (string | number)[];
|
||||
value: unknown;
|
||||
isNested: boolean;
|
||||
}): React.ReactNode => {
|
||||
const context: FieldContext = {
|
||||
fieldKey,
|
||||
fieldKeyPath,
|
||||
fieldValue: value,
|
||||
isNested,
|
||||
};
|
||||
const menuItems = buildMenuItems(context);
|
||||
return (
|
||||
<span className="pretty-view__value-row">
|
||||
<span>{content}</span>
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: menuItems,
|
||||
onClick: (e): void => {
|
||||
e.domEvent.stopPropagation();
|
||||
},
|
||||
}}
|
||||
trigger={['click']}
|
||||
placement="bottomLeft"
|
||||
getPopupContainer={(trigger): HTMLElement =>
|
||||
trigger.parentElement || document.body
|
||||
}
|
||||
>
|
||||
<span
|
||||
className="pretty-view__actions"
|
||||
onClick={(e): void => e.stopPropagation()}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<Ellipsis size={12} />
|
||||
</span>
|
||||
</Dropdown>
|
||||
</span>
|
||||
);
|
||||
},
|
||||
[buildMenuItems],
|
||||
);
|
||||
|
||||
const getItemString = useCallback(
|
||||
(
|
||||
_nodeType: string,
|
||||
nodeData: unknown,
|
||||
itemType: React.ReactNode,
|
||||
itemString: string,
|
||||
keyPath: KeyPath,
|
||||
): // eslint-disable-next-line max-params
|
||||
React.ReactNode => {
|
||||
const forwardPath = keyPathToForward(keyPath);
|
||||
return renderWithActions({
|
||||
content: (
|
||||
<>
|
||||
{itemType} {itemString}
|
||||
</>
|
||||
),
|
||||
fieldKey: keyPathToDisplayString(keyPath),
|
||||
fieldKeyPath: forwardPath,
|
||||
value: nodeData,
|
||||
isNested: true,
|
||||
});
|
||||
},
|
||||
[renderWithActions],
|
||||
);
|
||||
|
||||
const valueRenderer = useCallback(
|
||||
(
|
||||
valueAsString: unknown,
|
||||
value: unknown,
|
||||
...keyPath: KeyPath
|
||||
): React.ReactNode => {
|
||||
const forwardPath = keyPathToForward(keyPath);
|
||||
return renderWithActions({
|
||||
content: String(valueAsString),
|
||||
fieldKey: keyPathToDisplayString(keyPath),
|
||||
fieldKeyPath: forwardPath,
|
||||
value,
|
||||
isNested: typeof value === 'object' && value !== null,
|
||||
});
|
||||
},
|
||||
[renderWithActions],
|
||||
);
|
||||
|
||||
const pinnedLabelRenderer = useCallback(
|
||||
(keyPath: KeyPath): React.ReactNode => {
|
||||
const displayKey = String(keyPath[0]);
|
||||
const entry = pinnedEntries.find((e) => e.displayKey === displayKey);
|
||||
return (
|
||||
<span className="pretty-view__pinned-label">
|
||||
<Pin
|
||||
size={12}
|
||||
className="pretty-view__pinned-icon"
|
||||
onClick={(): void => {
|
||||
if (entry) {
|
||||
togglePin(entry.forwardPath);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<span>{displayKey}</span>
|
||||
</span>
|
||||
);
|
||||
},
|
||||
[togglePin, pinnedEntries],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="pretty-view">
|
||||
{searchable && (
|
||||
<Input
|
||||
className="pretty-view__search-input"
|
||||
type="text"
|
||||
placeholder="Search for a field..."
|
||||
value={searchQuery}
|
||||
onChange={(e): void => setSearchQuery(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showPinned && Object.keys(filteredPinnedData).length > 0 && (
|
||||
<div className="pretty-view__pinned">
|
||||
<div className="pretty-view__pinned-header">PINNED ITEMS</div>
|
||||
<JSONTree
|
||||
key={`pinned-${searchQuery}`}
|
||||
data={filteredPinnedData}
|
||||
theme={theme}
|
||||
invertTheme={false}
|
||||
hideRoot
|
||||
shouldExpandNodeInitially={shouldExpandNodeInitially}
|
||||
valueRenderer={valueRenderer}
|
||||
getItemString={getItemString}
|
||||
labelRenderer={pinnedLabelRenderer}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<JSONTree
|
||||
key={searchQuery}
|
||||
data={filteredData}
|
||||
theme={theme}
|
||||
invertTheme={false}
|
||||
hideRoot
|
||||
shouldExpandNodeInitially={shouldExpandNodeInitially}
|
||||
valueRenderer={valueRenderer}
|
||||
getItemString={getItemString}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PrettyView;
|
||||
@@ -1,62 +0,0 @@
|
||||
// Dark theme — SigNoz design palette
|
||||
export const darkTheme = {
|
||||
scheme: 'signoz-dark',
|
||||
author: 'signoz',
|
||||
base00: 'transparent',
|
||||
base01: '#161922',
|
||||
base02: '#1d212d',
|
||||
base03: '#62687C',
|
||||
base04: '#ADB4C2',
|
||||
base05: '#ADB4C2',
|
||||
base06: '#ADB4C2',
|
||||
base07: '#ADB4C2',
|
||||
base08: '#EA6D71',
|
||||
base09: '#7CEDBD',
|
||||
base0A: '#7CEDBD',
|
||||
base0B: '#ADB4C2',
|
||||
base0C: '#23C4F8',
|
||||
base0D: '#95ACFB',
|
||||
base0E: '#95ACFB',
|
||||
base0F: '#AD7F58',
|
||||
};
|
||||
|
||||
// Light theme
|
||||
export const lightTheme = {
|
||||
scheme: 'signoz-light',
|
||||
author: 'signoz',
|
||||
base00: 'transparent',
|
||||
base01: '#F9F9FB',
|
||||
base02: '#EFF0F3',
|
||||
base03: '#80828D',
|
||||
base04: '#62636C',
|
||||
base05: '#62636C',
|
||||
base06: '#62636C',
|
||||
base07: '#1E1F24',
|
||||
base08: '#E5484D',
|
||||
base09: '#168757',
|
||||
base0A: '#168757',
|
||||
base0B: '#62636C',
|
||||
base0C: '#157594',
|
||||
base0D: '#2F48A0',
|
||||
base0E: '#2F48A0',
|
||||
base0F: '#684C35',
|
||||
};
|
||||
|
||||
export const themeExtension = {
|
||||
value: ({
|
||||
style,
|
||||
}: {
|
||||
style: Record<string, unknown>;
|
||||
}): { style: Record<string, unknown>; className: string } => ({
|
||||
style: { ...style },
|
||||
className: 'pretty-view__row',
|
||||
}),
|
||||
nestedNode: ({
|
||||
style,
|
||||
}: {
|
||||
style: Record<string, unknown>;
|
||||
}): { style: Record<string, unknown>; className: string } => ({
|
||||
style: { ...style },
|
||||
className: 'pretty-view__nested-row',
|
||||
}),
|
||||
};
|
||||
@@ -1,127 +0,0 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import {
|
||||
deserializeKeyPath,
|
||||
keyPathToDisplayString,
|
||||
resolveValueByKeys,
|
||||
serializeKeyPath,
|
||||
} from '../utils';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type AnyRecord = Record<string, any>;
|
||||
|
||||
const STORAGE_PREFIX = 'pinnedFields';
|
||||
|
||||
function loadFromStorage(storageKey: string): string[] {
|
||||
try {
|
||||
const stored = localStorage.getItem(storageKey);
|
||||
return stored ? JSON.parse(stored) : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function saveToStorage(storageKey: string, keys: string[]): void {
|
||||
localStorage.setItem(storageKey, JSON.stringify(keys));
|
||||
}
|
||||
|
||||
export interface PinnedEntry {
|
||||
serializedKey: string;
|
||||
displayKey: string;
|
||||
forwardPath: (string | number)[];
|
||||
value: unknown;
|
||||
}
|
||||
|
||||
export interface UsePinnedFieldsReturn {
|
||||
isPinned: (serializedKey: string) => boolean;
|
||||
togglePin: (forwardPath: (string | number)[]) => void;
|
||||
pinnedEntries: PinnedEntry[];
|
||||
pinnedData: AnyRecord;
|
||||
displayKeyToForwardPath: Record<string, (string | number)[]>;
|
||||
}
|
||||
|
||||
function usePinnedFields(
|
||||
data: AnyRecord,
|
||||
drawerKey: string,
|
||||
): UsePinnedFieldsReturn {
|
||||
const storageKey = `${STORAGE_PREFIX}:${drawerKey}`;
|
||||
|
||||
// Stored as serialized keyPath arrays (JSON strings)
|
||||
const [pinnedSerializedKeys, setPinnedSerializedKeys] = useState<Set<string>>(
|
||||
() => new Set(loadFromStorage(storageKey)),
|
||||
);
|
||||
|
||||
const togglePin = useCallback(
|
||||
(forwardPath: (string | number)[]): void => {
|
||||
const serialized = serializeKeyPath(forwardPath);
|
||||
setPinnedSerializedKeys((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(serialized)) {
|
||||
next.delete(serialized);
|
||||
} else {
|
||||
next.add(serialized);
|
||||
}
|
||||
saveToStorage(storageKey, Array.from(next));
|
||||
return next;
|
||||
});
|
||||
},
|
||||
[storageKey],
|
||||
);
|
||||
|
||||
const isPinned = useCallback(
|
||||
(serializedKey: string): boolean => pinnedSerializedKeys.has(serializedKey),
|
||||
[pinnedSerializedKeys],
|
||||
);
|
||||
|
||||
const pinnedEntries = useMemo(
|
||||
(): PinnedEntry[] =>
|
||||
Array.from(pinnedSerializedKeys)
|
||||
.map((serializedKey) => {
|
||||
const forwardPath = deserializeKeyPath(serializedKey);
|
||||
if (!forwardPath) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
serializedKey,
|
||||
displayKey: keyPathToDisplayString(
|
||||
[...forwardPath].reverse() as readonly (string | number)[],
|
||||
),
|
||||
forwardPath,
|
||||
value: resolveValueByKeys(data, forwardPath),
|
||||
};
|
||||
})
|
||||
.filter(
|
||||
(entry): entry is PinnedEntry =>
|
||||
entry !== null && entry.value !== undefined,
|
||||
),
|
||||
[pinnedSerializedKeys, data],
|
||||
);
|
||||
|
||||
// Flat object for the pinned JSONTree — use display key as the object key
|
||||
const pinnedData = useMemo(
|
||||
() =>
|
||||
Object.fromEntries(
|
||||
pinnedEntries.map((entry) => [entry.displayKey, entry.value]),
|
||||
),
|
||||
[pinnedEntries],
|
||||
);
|
||||
|
||||
// Map from display key to original forward path — for unpin from pinned tree
|
||||
const displayKeyToForwardPath = useMemo(
|
||||
(): Record<string, (string | number)[]> =>
|
||||
Object.fromEntries(
|
||||
pinnedEntries.map((entry) => [entry.displayKey, entry.forwardPath]),
|
||||
),
|
||||
[pinnedEntries],
|
||||
);
|
||||
|
||||
return {
|
||||
isPinned,
|
||||
togglePin,
|
||||
pinnedEntries,
|
||||
pinnedData,
|
||||
displayKeyToForwardPath,
|
||||
};
|
||||
}
|
||||
|
||||
export default usePinnedFields;
|
||||
@@ -1,82 +0,0 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type AnyRecord = Record<string, any>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type AnyValue = any;
|
||||
|
||||
function isLeaf(value: unknown): boolean {
|
||||
return value === null || value === undefined || typeof value !== 'object';
|
||||
}
|
||||
|
||||
function matchesQuery(text: string, lowerQuery: string): boolean {
|
||||
return text.toLowerCase().includes(lowerQuery);
|
||||
}
|
||||
|
||||
// Filter a single value (leaf, array, or object) against the query.
|
||||
// Returns the filtered value or null if no match.
|
||||
function filterValue(value: AnyValue, query: string): AnyValue | null {
|
||||
if (isLeaf(value)) {
|
||||
return matchesQuery(String(value), query.toLowerCase()) ? value : null;
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return filterArray(value, query);
|
||||
}
|
||||
|
||||
return filterTree(value, query);
|
||||
}
|
||||
|
||||
// Recursively filter an array, keeping only elements that match
|
||||
function filterArray(arr: AnyValue[], query: string): AnyValue[] | null {
|
||||
const results = arr
|
||||
.map((item) => filterValue(item, query))
|
||||
.filter((item) => item !== null);
|
||||
|
||||
return results.length > 0 ? results : null;
|
||||
}
|
||||
|
||||
// Recursively filter the data tree, keeping only branches with matching keys or values
|
||||
export function filterTree(obj: AnyRecord, query: string): AnyRecord | null {
|
||||
const result: AnyRecord = {};
|
||||
const lowerQuery = query.toLowerCase();
|
||||
let hasMatch = false;
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (matchesQuery(key, lowerQuery)) {
|
||||
result[key] = value;
|
||||
hasMatch = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
const filtered = filterValue(value, query);
|
||||
if (filtered !== null) {
|
||||
result[key] = filtered;
|
||||
hasMatch = true;
|
||||
}
|
||||
}
|
||||
|
||||
return hasMatch ? result : null;
|
||||
}
|
||||
|
||||
interface UseSearchFilterReturn {
|
||||
searchQuery: string;
|
||||
setSearchQuery: (query: string) => void;
|
||||
filteredData: AnyRecord;
|
||||
}
|
||||
|
||||
function useSearchFilter(data: AnyRecord): UseSearchFilterReturn {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
const filteredData = useMemo(() => {
|
||||
const trimmed = searchQuery.trim();
|
||||
if (!trimmed) {
|
||||
return data;
|
||||
}
|
||||
return filterTree(data, trimmed) || {};
|
||||
}, [data, searchQuery]);
|
||||
|
||||
return { searchQuery, setSearchQuery, filteredData };
|
||||
}
|
||||
|
||||
export default useSearchFilter;
|
||||
@@ -1,2 +0,0 @@
|
||||
export type { PrettyViewProps } from './PrettyView';
|
||||
export { default as PrettyView } from './PrettyView';
|
||||
@@ -1,58 +0,0 @@
|
||||
import { toast } from '@signozhq/sonner';
|
||||
|
||||
export function copyToClipboard(value: unknown): void {
|
||||
const text =
|
||||
typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value);
|
||||
// eslint-disable-next-line no-restricted-properties
|
||||
navigator.clipboard.writeText(text);
|
||||
toast.success('Copied to clipboard', {
|
||||
richColors: true,
|
||||
position: 'top-right',
|
||||
});
|
||||
}
|
||||
|
||||
// Resolve a value from a nested object using an array of keys (not dot-notation)
|
||||
// e.g. resolveValueByKeys({ tagMap: { 'cloud.account.id': 'x' } }, ['tagMap', 'cloud.account.id']) → 'x'
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function resolveValueByKeys(
|
||||
data: Record<string, any>,
|
||||
keys: (string | number)[],
|
||||
): unknown {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return keys.reduce((obj: any, key) => obj?.[key], data);
|
||||
}
|
||||
|
||||
// Convert react-json-tree's reversed keyPath to forward order
|
||||
// e.g. ['cloud.account.id', 'tagMap'] → ['tagMap', 'cloud.account.id']
|
||||
export function keyPathToForward(
|
||||
keyPath: readonly (string | number)[],
|
||||
): (string | number)[] {
|
||||
return [...keyPath].reverse();
|
||||
}
|
||||
|
||||
// Display-friendly string for a keyPath
|
||||
// e.g. ['tagMap', 'cloud.account.id'] → 'tagMap.cloud.account.id'
|
||||
export function keyPathToDisplayString(
|
||||
keyPath: readonly (string | number)[],
|
||||
): string {
|
||||
return [...keyPath].reverse().join('.');
|
||||
}
|
||||
|
||||
// Serialize keyPath for storage/comparison (JSON stringified array)
|
||||
export function serializeKeyPath(keyPath: (string | number)[]): string {
|
||||
return JSON.stringify(keyPath);
|
||||
}
|
||||
|
||||
export function deserializeKeyPath(
|
||||
serialized: string,
|
||||
): (string | number)[] | null {
|
||||
try {
|
||||
const parsed = JSON.parse(serialized);
|
||||
if (Array.isArray(parsed)) {
|
||||
return parsed;
|
||||
}
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
.resizable-box {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&--disabled {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
&__content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__handle {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
background: var(--l2-border);
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
background: var(--primary);
|
||||
}
|
||||
|
||||
&--vertical {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
cursor: row-resize;
|
||||
}
|
||||
|
||||
&--horizontal {
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 4px;
|
||||
cursor: col-resize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
|
||||
import './ResizableBox.styles.scss';
|
||||
|
||||
export interface ResizableBoxProps {
|
||||
children: React.ReactNode;
|
||||
direction?: 'vertical' | 'horizontal';
|
||||
defaultHeight?: number;
|
||||
minHeight?: number;
|
||||
maxHeight?: number;
|
||||
defaultWidth?: number;
|
||||
minWidth?: number;
|
||||
maxWidth?: number;
|
||||
onResize?: (size: number) => void;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function ResizableBox({
|
||||
children,
|
||||
direction = 'vertical',
|
||||
defaultHeight = 200,
|
||||
minHeight = 50,
|
||||
maxHeight = Infinity,
|
||||
defaultWidth = 200,
|
||||
minWidth = 50,
|
||||
maxWidth = Infinity,
|
||||
onResize,
|
||||
disabled = false,
|
||||
className,
|
||||
}: ResizableBoxProps): JSX.Element {
|
||||
const isHorizontal = direction === 'horizontal';
|
||||
const [size, setSize] = useState(isHorizontal ? defaultWidth : defaultHeight);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleMouseDown = useCallback(
|
||||
(e: React.MouseEvent): void => {
|
||||
e.preventDefault();
|
||||
const startPos = isHorizontal ? e.clientX : e.clientY;
|
||||
const startSize = size;
|
||||
const min = isHorizontal ? minWidth : minHeight;
|
||||
const max = isHorizontal ? maxWidth : maxHeight;
|
||||
|
||||
const onMouseMove = (moveEvent: MouseEvent): void => {
|
||||
const currentPos = isHorizontal ? moveEvent.clientX : moveEvent.clientY;
|
||||
const delta = currentPos - startPos;
|
||||
const newSize = Math.min(max, Math.max(min, startSize + delta));
|
||||
setSize(newSize);
|
||||
onResize?.(newSize);
|
||||
};
|
||||
|
||||
const onMouseUp = (): void => {
|
||||
document.removeEventListener('mousemove', onMouseMove);
|
||||
document.removeEventListener('mouseup', onMouseUp);
|
||||
document.body.style.cursor = '';
|
||||
document.body.style.userSelect = '';
|
||||
};
|
||||
|
||||
document.body.style.cursor = isHorizontal ? 'col-resize' : 'row-resize';
|
||||
document.body.style.userSelect = 'none';
|
||||
document.addEventListener('mousemove', onMouseMove);
|
||||
document.addEventListener('mouseup', onMouseUp);
|
||||
},
|
||||
[size, isHorizontal, minWidth, maxWidth, minHeight, maxHeight, onResize],
|
||||
);
|
||||
|
||||
const containerStyle = disabled
|
||||
? undefined
|
||||
: isHorizontal
|
||||
? { width: size }
|
||||
: { height: size };
|
||||
const handleClass = `resizable-box__handle resizable-box__handle--${direction}`;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={`resizable-box ${disabled ? 'resizable-box--disabled' : ''} ${
|
||||
className || ''
|
||||
}`}
|
||||
style={containerStyle}
|
||||
>
|
||||
<div className="resizable-box__content">{children}</div>
|
||||
{!disabled && <div className={handleClass} onMouseDown={handleMouseDown} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ResizableBox;
|
||||
@@ -1,2 +0,0 @@
|
||||
export type { ResizableBoxProps } from './ResizableBox';
|
||||
export { default as ResizableBox } from './ResizableBox';
|
||||
@@ -6538,11 +6538,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.0.tgz#d774355e41f372d5350a4d0714abb48194a489c3"
|
||||
integrity sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==
|
||||
|
||||
"@types/lodash@^4.17.0", "@types/lodash@^4.17.15":
|
||||
version "4.17.24"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.24.tgz#4ae334fc62c0e915ca8ed8e35dcc6d4eeb29215f"
|
||||
integrity sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==
|
||||
|
||||
"@types/mdast@^3.0.0":
|
||||
version "3.0.12"
|
||||
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.12.tgz#beeb511b977c875a5b0cc92eab6fcac2f0895514"
|
||||
@@ -8957,7 +8952,7 @@ color-string@^1.9.0:
|
||||
color-name "^1.0.0"
|
||||
simple-swizzle "^0.2.2"
|
||||
|
||||
color@^4.2.1, color@^4.2.3:
|
||||
color@^4.2.1:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.npmjs.org/color/-/color-4.2.3.tgz"
|
||||
integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==
|
||||
@@ -9389,11 +9384,6 @@ csstype@^3.1.2:
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
|
||||
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
|
||||
|
||||
csstype@^3.1.3:
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a"
|
||||
integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==
|
||||
|
||||
"d3-array@1 - 3", "d3-array@2 - 3", "d3-array@2.10.0 - 3":
|
||||
version "3.2.3"
|
||||
resolved "https://registry.npmjs.org/d3-array/-/d3-array-3.2.3.tgz"
|
||||
@@ -16829,11 +16819,6 @@ rc-virtual-list@^3.11.1, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2:
|
||||
rc-resize-observer "^1.0.0"
|
||||
rc-util "^5.36.0"
|
||||
|
||||
re-resizable@^6.11.2:
|
||||
version "6.11.2"
|
||||
resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.11.2.tgz#2e8f7119ca3881d5b5aea0ffa014a80e5c1252b3"
|
||||
integrity sha512-2xI2P3OHs5qw7K0Ud1aLILK6MQxW50TcO+DetD9eIV58j84TqYeHoZcL9H4GXFXXIh7afhH8mv5iUCXII7OW7A==
|
||||
|
||||
react-addons-update@15.6.3:
|
||||
version "15.6.3"
|
||||
resolved "https://registry.yarnpkg.com/react-addons-update/-/react-addons-update-15.6.3.tgz#c449c309154024d04087b206d0400e020547b313"
|
||||
@@ -16841,16 +16826,6 @@ react-addons-update@15.6.3:
|
||||
dependencies:
|
||||
object-assign "^4.1.0"
|
||||
|
||||
react-base16-styling@^0.10.0:
|
||||
version "0.10.0"
|
||||
resolved "https://registry.yarnpkg.com/react-base16-styling/-/react-base16-styling-0.10.0.tgz#5d5f019bd4dc5870c3e92fd9d5410533a0bbb0c6"
|
||||
integrity sha512-H1k2eFB6M45OaiRru3PBXkuCcn2qNmx+gzLb4a9IPMR7tMH8oBRXU5jGbPDYG1Hz+82d88ED0vjR8BmqU3pQdg==
|
||||
dependencies:
|
||||
"@types/lodash" "^4.17.0"
|
||||
color "^4.2.3"
|
||||
csstype "^3.1.3"
|
||||
lodash-es "^4.17.21"
|
||||
|
||||
react-beautiful-dnd@13.1.1:
|
||||
version "13.1.1"
|
||||
resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz#b0f3087a5840920abf8bb2325f1ffa46d8c4d0a2"
|
||||
@@ -16915,14 +16890,6 @@ react-draggable@^4.0.0, react-draggable@^4.0.3:
|
||||
clsx "^1.1.1"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
react-draggable@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.5.0.tgz#0b274ccb6965fcf97ed38fcf7e3cc223bc48cdf5"
|
||||
integrity sha512-VC+HBLEZ0XJxnOxVAZsdRi8rD04Iz3SiiKOoYzamjylUcju/hP9np/aZdLHf/7WOD268WMoNJMvYfB5yAK45cw==
|
||||
dependencies:
|
||||
clsx "^2.1.1"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
react-error-boundary@4.0.11:
|
||||
version "4.0.11"
|
||||
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-4.0.11.tgz#36bf44de7746714725a814630282fee83a7c9a1c"
|
||||
@@ -17013,14 +16980,6 @@ react-is@^18.3.1:
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e"
|
||||
integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
|
||||
|
||||
react-json-tree@0.20.0:
|
||||
version "0.20.0"
|
||||
resolved "https://registry.yarnpkg.com/react-json-tree/-/react-json-tree-0.20.0.tgz#1e7fd26f093bd49d733f80dd9b6d40142e09f7c9"
|
||||
integrity sha512-h+f9fUNAxzBx1rbrgUF7+zSWKGHDtt2VPYLErIuB0JyKGnWgFMM21ksqQyb3EXwXNnoMW2rdE5kuAaubgGOx2Q==
|
||||
dependencies:
|
||||
"@types/lodash" "^4.17.15"
|
||||
react-base16-styling "^0.10.0"
|
||||
|
||||
react-kapsule@^2.5:
|
||||
version "2.5.7"
|
||||
resolved "https://registry.yarnpkg.com/react-kapsule/-/react-kapsule-2.5.7.tgz#dcd957ae8e897ff48055fc8ff48ed04ebe3c5bd2"
|
||||
@@ -17140,15 +17099,6 @@ react-resizable@^3.0.4:
|
||||
prop-types "15.x"
|
||||
react-draggable "^4.0.3"
|
||||
|
||||
react-rnd@10.5.3:
|
||||
version "10.5.3"
|
||||
resolved "https://registry.yarnpkg.com/react-rnd/-/react-rnd-10.5.3.tgz#f8c484034276a561e8aa2fbf6a94580596621013"
|
||||
integrity sha512-s/sIT3pGZnQ+57egijkTp9mizjIWrJz68Pq6yd+F/wniFY3IriML18dUXnQe/HP9uMiJ+9MAp44hljG99fZu6Q==
|
||||
dependencies:
|
||||
re-resizable "^6.11.2"
|
||||
react-draggable "^4.5.0"
|
||||
tslib "2.6.2"
|
||||
|
||||
react-router-dom-v5-compat@6.27.0:
|
||||
version "6.27.0"
|
||||
resolved "https://registry.yarnpkg.com/react-router-dom-v5-compat/-/react-router-dom-v5-compat-6.27.0.tgz#19429aefa130ee151e6f4487d073d919ec22d458"
|
||||
@@ -19234,11 +19184,6 @@ tsconfig-paths@^4.2.0:
|
||||
minimist "^1.2.6"
|
||||
strip-bom "^3.0.0"
|
||||
|
||||
tslib@2.6.2, tslib@^2.0.0:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
|
||||
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
|
||||
|
||||
tslib@2.7.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01"
|
||||
@@ -19249,6 +19194,11 @@ tslib@^1.14.1, tslib@^1.8.1:
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
||||
tslib@^2.0.0:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
|
||||
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
|
||||
|
||||
tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz"
|
||||
|
||||
37
go.mod
37
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/SigNoz/signoz
|
||||
|
||||
go 1.25.0
|
||||
go 1.25.7
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.2
|
||||
@@ -19,6 +19,7 @@ require (
|
||||
github.com/go-co-op/gocron v1.30.1
|
||||
github.com/go-openapi/runtime v0.29.2
|
||||
github.com/go-openapi/strfmt v0.25.0
|
||||
github.com/go-playground/validator/v10 v10.27.0
|
||||
github.com/go-redis/redismock/v9 v9.2.0
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0
|
||||
github.com/gojek/heimdall/v7 v7.0.3
|
||||
@@ -27,8 +28,8 @@ require (
|
||||
github.com/gorilla/handlers v1.5.1
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
|
||||
github.com/huandu/go-sqlbuilder v1.35.0
|
||||
github.com/jackc/pgx/v5 v5.7.6
|
||||
github.com/huandu/go-sqlbuilder v1.39.1
|
||||
github.com/jackc/pgx/v5 v5.8.0
|
||||
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12
|
||||
github.com/knadh/koanf v1.5.0
|
||||
github.com/knadh/koanf/v2 v2.3.2
|
||||
@@ -38,6 +39,7 @@ require (
|
||||
github.com/openfga/api/proto v0.0.0-20250909172242-b4b2a12f5c67
|
||||
github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20251027165255-0f8f255e5f6c
|
||||
github.com/opentracing/opentracing-go v1.2.0
|
||||
github.com/perses/perses v0.53.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/alertmanager v0.31.0
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
@@ -75,18 +77,18 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.40.0
|
||||
go.uber.org/multierr v1.11.0
|
||||
go.uber.org/zap v1.27.1
|
||||
golang.org/x/crypto v0.47.0
|
||||
golang.org/x/crypto v0.48.0
|
||||
golang.org/x/exp v0.0.0-20260112195511-716be5621a96
|
||||
golang.org/x/net v0.49.0
|
||||
golang.org/x/oauth2 v0.34.0
|
||||
golang.org/x/net v0.50.0
|
||||
golang.org/x/oauth2 v0.35.0
|
||||
golang.org/x/sync v0.19.0
|
||||
golang.org/x/text v0.33.0
|
||||
golang.org/x/text v0.34.0
|
||||
gonum.org/v1/gonum v0.17.0
|
||||
google.golang.org/api v0.265.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/apimachinery v0.35.0
|
||||
k8s.io/apimachinery v0.35.2
|
||||
modernc.org/sqlite v1.40.1
|
||||
)
|
||||
|
||||
@@ -125,12 +127,14 @@ require (
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.27.0 // indirect
|
||||
github.com/goccy/go-yaml v1.19.2 // indirect
|
||||
github.com/hashicorp/go-metrics v0.5.4 // indirect
|
||||
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 v0.1.9 // indirect
|
||||
github.com/perses/common v0.30.2 // indirect
|
||||
github.com/prometheus/client_golang/exp v0.0.0-20260108101519-fb0838f53562 // indirect
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.15.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
@@ -139,6 +143,8 @@ require (
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||
github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 // indirect
|
||||
github.com/zitadel/oidc/v3 v3.45.4 // indirect
|
||||
github.com/zitadel/schema v1.3.2 // indirect
|
||||
go.opentelemetry.io/collector/client v1.50.0 // indirect
|
||||
go.opentelemetry.io/collector/config/configoptional v1.50.0 // indirect
|
||||
go.opentelemetry.io/collector/config/configretry v1.50.0 // indirect
|
||||
@@ -208,7 +214,7 @@ require (
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/cel-go v0.26.1 // indirect
|
||||
github.com/google/cel-go v0.27.0 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.16.0 // indirect
|
||||
@@ -226,7 +232,7 @@ require (
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hashicorp/memberlist v0.5.4 // indirect
|
||||
github.com/huandu/xstrings v1.4.0 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
@@ -298,7 +304,6 @@ require (
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/spf13/viper v1.20.1 // indirect
|
||||
github.com/stoewer/go-strcase v1.3.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/swaggest/openapi-go v0.2.60
|
||||
@@ -376,15 +381,15 @@ require (
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/mod v0.32.0 // indirect
|
||||
golang.org/x/sys v0.40.0 // indirect
|
||||
golang.org/x/mod v0.33.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
golang.org/x/tools v0.41.0 // indirect
|
||||
golang.org/x/tools v0.42.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409
|
||||
google.golang.org/grpc v1.78.0 // indirect
|
||||
gopkg.in/telebot.v3 v3.3.8 // indirect
|
||||
k8s.io/client-go v0.35.0 // indirect
|
||||
k8s.io/client-go v0.35.2 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect
|
||||
)
|
||||
|
||||
81
go.sum
81
go.sum
@@ -489,8 +489,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ=
|
||||
github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM=
|
||||
github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo=
|
||||
github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw=
|
||||
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
|
||||
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
@@ -654,12 +654,15 @@ github.com/hetznercloud/hcloud-go/v2 v2.36.0 h1:HlLL/aaVXUulqe+rsjoJmrxKhPi1MflL
|
||||
github.com/hetznercloud/hcloud-go/v2 v2.36.0/go.mod h1:MnN/QJEa/RYNQiiVoJjNHPntM7Z1wlYPgJ2HA40/cDE=
|
||||
github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs=
|
||||
github.com/hjson/hjson-go/v4 v4.0.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E=
|
||||
github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U=
|
||||
github.com/huandu/go-assert v1.1.6 h1:oaAfYxq9KNDi9qswn/6aE0EydfxSa+tWZC1KabNitYs=
|
||||
github.com/huandu/go-assert v1.1.6/go.mod h1:JuIfbmYG9ykwvuxoJ3V8TB5QP+3+ajIA54Y44TmkMxs=
|
||||
github.com/huandu/go-sqlbuilder v1.35.0 h1:ESvxFHN8vxCTudY1Vq63zYpU5yJBESn19sf6k4v2T5Q=
|
||||
github.com/huandu/go-sqlbuilder v1.35.0/go.mod h1:mS0GAtrtW+XL6nM2/gXHRJax2RwSW1TraavWDFAc1JA=
|
||||
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
|
||||
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/huandu/go-clone v1.7.3 h1:rtQODA+ABThEn6J5LBTppJfKmZy/FwfpMUWa8d01TTQ=
|
||||
github.com/huandu/go-clone v1.7.3/go.mod h1:ReGivhG6op3GYr+UY3lS6mxjKp7MIGTknuU5TbTVaXE=
|
||||
github.com/huandu/go-sqlbuilder v1.39.1 h1:uUaj41yLNTQBe7ojNF6Im1RPbHCN4zCjMRySTEC2ooI=
|
||||
github.com/huandu/go-sqlbuilder v1.39.1/go.mod h1:zdONH67liL+/TvoUMwnZP/sUYGSSvHh9psLe/HpXn8E=
|
||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
|
||||
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
@@ -672,8 +675,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk=
|
||||
github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
|
||||
github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo=
|
||||
github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
|
||||
@@ -818,6 +821,8 @@ 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=
|
||||
@@ -827,9 +832,11 @@ 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 v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/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.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
|
||||
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/oklog/run v1.2.0 h1:O8x3yXwah4A73hJdlrwo/2X6J62gE5qTMusH0dvz60E=
|
||||
github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk=
|
||||
@@ -891,6 +898,10 @@ 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/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4/v4 v4.1.23 h1:oJE7T90aYBGtFNrI8+KbETnPymobAhzRrR8Mu8n1yfU=
|
||||
github.com/pierrec/lz4/v4 v4.1.23/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
|
||||
@@ -1049,8 +1060,6 @@ github.com/srikanthccv/ClickHouse-go-mock v0.13.0 h1:/b7DQphGkh29ocNtLh4DGmQxQYA
|
||||
github.com/srikanthccv/ClickHouse-go-mock v0.13.0/go.mod h1:LiiyBUdXNwB/1DE9rgK/8q9qjVYsTzg6WXQ/3mU3TeY=
|
||||
github.com/stackitcloud/stackit-sdk-go/core v0.21.1 h1:Y/PcAgM7DPYMNqum0MLv4n1mF9ieuevzcCIZYQfm3Ts=
|
||||
github.com/stackitcloud/stackit-sdk-go/core v0.21.1/go.mod h1:osMglDby4csGZ5sIfhNyYq1bS1TxIdPY88+skE/kkmI=
|
||||
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
|
||||
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
@@ -1150,6 +1159,10 @@ 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=
|
||||
@@ -1385,8 +1398,8 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
||||
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -1424,8 +1437,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
||||
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -1474,8 +1487,8 @@ golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su
|
||||
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1496,8 +1509,8 @@ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
|
||||
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -1598,12 +1611,12 @@ golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
||||
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
|
||||
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1614,8 +1627,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -1678,8 +1691,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||
golang.org/x/tools/godoc v0.1.0-deprecated h1:o+aZ1BOj6Hsx/GBdJO/s815sqftjSnrZZwyYTHODvtk=
|
||||
golang.org/x/tools/godoc v0.1.0-deprecated/go.mod h1:qM63CriJ961IHWmnWa9CjZnBndniPt4a3CK0PVB9bIg=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1915,12 +1928,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
k8s.io/api v0.35.0 h1:iBAU5LTyBI9vw3L5glmat1njFK34srdLmktWwLTprlY=
|
||||
k8s.io/api v0.35.0/go.mod h1:AQ0SNTzm4ZAczM03QH42c7l3bih1TbAXYo0DkF8ktnA=
|
||||
k8s.io/apimachinery v0.35.0 h1:Z2L3IHvPVv/MJ7xRxHEtk6GoJElaAqDCCU0S6ncYok8=
|
||||
k8s.io/apimachinery v0.35.0/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
|
||||
k8s.io/client-go v0.35.0 h1:IAW0ifFbfQQwQmga0UdoH0yvdqrbwMdq9vIFEhRpxBE=
|
||||
k8s.io/client-go v0.35.0/go.mod h1:q2E5AAyqcbeLGPdoRB+Nxe3KYTfPce1Dnu1myQdqz9o=
|
||||
k8s.io/api v0.35.2 h1:tW7mWc2RpxW7HS4CoRXhtYHSzme1PN1UjGHJ1bdrtdw=
|
||||
k8s.io/api v0.35.2/go.mod h1:7AJfqGoAZcwSFhOjcGM7WV05QxMMgUaChNfLTXDRE60=
|
||||
k8s.io/apimachinery v0.35.2 h1:NqsM/mmZA7sHW02JZ9RTtk3wInRgbVxL8MPfzSANAK8=
|
||||
k8s.io/apimachinery v0.35.2/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
|
||||
k8s.io/client-go v0.35.2 h1:YUfPefdGJA4aljDdayAXkc98DnPkIetMl4PrKX97W9o=
|
||||
k8s.io/client-go v0.35.2/go.mod h1:4QqEwh4oQpeK8AaefZ0jwTFJw/9kIjdQi0jpKeYvz7g=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE=
|
||||
|
||||
@@ -10,26 +10,6 @@ import (
|
||||
)
|
||||
|
||||
func (provider *provider) addCloudIntegrationRoutes(router *mux.Router) error {
|
||||
if err := router.Handle("/api/v1/cloud_integrations/{cloud_provider}/credentials", handler.New(
|
||||
provider.authZ.AdminAccess(provider.cloudIntegrationHandler.GetConnectionCredentials),
|
||||
handler.OpenAPIDef{
|
||||
ID: "GetConnectionCredentials",
|
||||
Tags: []string{"cloudintegration"},
|
||||
Summary: "Get connection credentials",
|
||||
Description: "This endpoint retrieves the connection credentials required for integration",
|
||||
Request: nil,
|
||||
RequestContentType: "application/json",
|
||||
Response: new(citypes.Credentials),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
},
|
||||
)).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/cloud_integrations/{cloud_provider}/accounts", handler.New(
|
||||
provider.authZ.AdminAccess(provider.cloudIntegrationHandler.CreateAccount),
|
||||
handler.OpenAPIDef{
|
||||
@@ -37,9 +17,9 @@ func (provider *provider) addCloudIntegrationRoutes(router *mux.Router) error {
|
||||
Tags: []string{"cloudintegration"},
|
||||
Summary: "Create account",
|
||||
Description: "This endpoint creates a new cloud integration account for the specified cloud provider",
|
||||
Request: new(citypes.PostableAccount),
|
||||
Request: new(citypes.PostableConnectionArtifact),
|
||||
RequestContentType: "application/json",
|
||||
Response: new(citypes.GettableAccountWithConnectionArtifact),
|
||||
Response: new(citypes.GettableAccountWithArtifact),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
@@ -79,7 +59,7 @@ func (provider *provider) addCloudIntegrationRoutes(router *mux.Router) error {
|
||||
Description: "This endpoint gets an account for the specified cloud provider",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: new(citypes.Account),
|
||||
Response: new(citypes.GettableAccount),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
|
||||
@@ -159,7 +139,7 @@ func (provider *provider) addCloudIntegrationRoutes(router *mux.Router) error {
|
||||
Description: "This endpoint gets a service for the specified cloud provider",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: new(citypes.Service),
|
||||
Response: new(citypes.GettableService),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
@@ -170,7 +150,7 @@ func (provider *provider) addCloudIntegrationRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/cloud_integrations/{cloud_provider}/accounts/{id}/services/{service_id}", handler.New(
|
||||
if err := router.Handle("/api/v1/cloud_integrations/{cloud_provider}/services/{service_id}", handler.New(
|
||||
provider.authZ.AdminAccess(provider.cloudIntegrationHandler.UpdateService),
|
||||
handler.OpenAPIDef{
|
||||
ID: "UpdateService",
|
||||
@@ -199,9 +179,9 @@ func (provider *provider) addCloudIntegrationRoutes(router *mux.Router) error {
|
||||
Tags: []string{"cloudintegration"},
|
||||
Summary: "Agent check-in",
|
||||
Description: "[Deprecated] This endpoint is called by the deployed agent to check in",
|
||||
Request: new(citypes.PostableAgentCheckIn),
|
||||
Request: new(citypes.PostableAgentCheckInRequest),
|
||||
RequestContentType: "application/json",
|
||||
Response: new(citypes.GettableAgentCheckIn),
|
||||
Response: new(citypes.GettableAgentCheckInResponse),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
@@ -219,9 +199,9 @@ func (provider *provider) addCloudIntegrationRoutes(router *mux.Router) error {
|
||||
Tags: []string{"cloudintegration"},
|
||||
Summary: "Agent check-in",
|
||||
Description: "This endpoint is called by the deployed agent to check in",
|
||||
Request: new(citypes.PostableAgentCheckIn),
|
||||
Request: new(citypes.PostableAgentCheckInRequest),
|
||||
RequestContentType: "application/json",
|
||||
Response: new(citypes.GettableAgentCheckIn),
|
||||
Response: new(citypes.GettableAgentCheckInResponse),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
|
||||
@@ -63,7 +63,6 @@ type RetryConfig struct {
|
||||
|
||||
func newConfig() factory.Config {
|
||||
return Config{
|
||||
Provider: "noop",
|
||||
BufferSize: 1000,
|
||||
BatchSize: 100,
|
||||
FlushInterval: time.Second,
|
||||
|
||||
@@ -10,42 +10,37 @@ import (
|
||||
)
|
||||
|
||||
type Module interface {
|
||||
GetConnectionCredentials(ctx context.Context, orgID valuer.UUID, provider citypes.CloudProviderType) (*citypes.Credentials, error)
|
||||
|
||||
CreateAccount(ctx context.Context, account *citypes.Account) error
|
||||
|
||||
// GetAccount returns cloud integration account
|
||||
GetAccount(ctx context.Context, orgID, accountID valuer.UUID, provider citypes.CloudProviderType) (*citypes.Account, error)
|
||||
GetAccount(ctx context.Context, orgID, accountID valuer.UUID) (*citypes.Account, error)
|
||||
|
||||
// ListAccounts lists accounts where agent is connected
|
||||
ListAccounts(ctx context.Context, orgID valuer.UUID, provider citypes.CloudProviderType) ([]*citypes.Account, error)
|
||||
ListAccounts(ctx context.Context, orgID valuer.UUID) ([]*citypes.Account, error)
|
||||
|
||||
// UpdateAccount updates the cloud integration account for a specific organization.
|
||||
UpdateAccount(ctx context.Context, account *citypes.Account) error
|
||||
|
||||
// DisconnectAccount soft deletes/removes a cloud integration account.
|
||||
DisconnectAccount(ctx context.Context, orgID, accountID valuer.UUID, provider citypes.CloudProviderType) error
|
||||
DisconnectAccount(ctx context.Context, orgID, accountID valuer.UUID) error
|
||||
|
||||
// GetConnectionArtifact returns cloud provider specific connection information,
|
||||
// client side handles how this information is shown
|
||||
GetConnectionArtifact(ctx context.Context, account *citypes.Account, req *citypes.GetConnectionArtifactRequest) (*citypes.ConnectionArtifact, error)
|
||||
GetConnectionArtifact(ctx context.Context, account *citypes.Account, req *citypes.ConnectionArtifactRequest) (*citypes.ConnectionArtifact, error)
|
||||
|
||||
// ListServicesMetadata returns the list of supported services' metadata for a cloud provider with optional filtering for a specific integration
|
||||
// This just returns a summary of the service and not the whole service definition.
|
||||
ListServicesMetadata(ctx context.Context, orgID valuer.UUID, provider citypes.CloudProviderType, integrationID *valuer.UUID) ([]*citypes.ServiceMetadata, error)
|
||||
// ListServicesMetadata returns the list of services metadata for a cloud provider attached with the integrationID.
|
||||
// This just returns a summary of the service and not the whole service definition
|
||||
ListServicesMetadata(ctx context.Context, orgID valuer.UUID, integrationID *valuer.UUID) ([]*citypes.ServiceMetadata, error)
|
||||
|
||||
// GetService returns service definition details for a serviceID. This optionally returns the service config
|
||||
// for integrationID if provided.
|
||||
GetService(ctx context.Context, orgID valuer.UUID, integrationID *valuer.UUID, serviceID citypes.ServiceID, provider citypes.CloudProviderType) (*citypes.Service, error)
|
||||
|
||||
// CreateService creates a new service for a cloud integration account.
|
||||
CreateService(ctx context.Context, orgID valuer.UUID, service *citypes.CloudIntegrationService, provider citypes.CloudProviderType) error
|
||||
// GetService returns service definition details for a serviceID. This returns config and
|
||||
// other details required to show in service details page on web client.
|
||||
GetService(ctx context.Context, orgID valuer.UUID, integrationID *valuer.UUID, serviceID string) (*citypes.Service, error)
|
||||
|
||||
// UpdateService updates cloud integration service
|
||||
UpdateService(ctx context.Context, orgID valuer.UUID, service *citypes.CloudIntegrationService, provider citypes.CloudProviderType) error
|
||||
UpdateService(ctx context.Context, orgID valuer.UUID, service *citypes.CloudIntegrationService) error
|
||||
|
||||
// AgentCheckIn is called by agent to send heartbeat and get latest config in response.
|
||||
AgentCheckIn(ctx context.Context, orgID valuer.UUID, provider citypes.CloudProviderType, req *citypes.AgentCheckInRequest) (*citypes.AgentCheckInResponse, error)
|
||||
// AgentCheckIn is called by agent to heartbeat and get latest config in response.
|
||||
AgentCheckIn(ctx context.Context, orgID valuer.UUID, req *citypes.AgentCheckInRequest) (*citypes.AgentCheckInResponse, error)
|
||||
|
||||
// GetDashboardByID returns dashboard JSON for a given dashboard id.
|
||||
// this only returns the dashboard when the service (embedded in dashboard id) is enabled
|
||||
@@ -57,22 +52,7 @@ type Module interface {
|
||||
ListDashboards(ctx context.Context, orgID valuer.UUID) ([]*dashboardtypes.Dashboard, error)
|
||||
}
|
||||
|
||||
type CloudProviderModule interface {
|
||||
GetConnectionArtifact(ctx context.Context, account *citypes.Account, req *citypes.GetConnectionArtifactRequest) (*citypes.ConnectionArtifact, error)
|
||||
|
||||
// ListServiceDefinitions returns all service definitions for this cloud provider.
|
||||
ListServiceDefinitions(ctx context.Context) ([]*citypes.ServiceDefinition, error)
|
||||
|
||||
// GetServiceDefinition returns the service definition for the given service ID.
|
||||
GetServiceDefinition(ctx context.Context, serviceID citypes.ServiceID) (*citypes.ServiceDefinition, error)
|
||||
|
||||
// BuildIntegrationConfig compiles the provider-specific integration config from the account
|
||||
// and list of configured services. This is the config returned to the agent on check-in.
|
||||
BuildIntegrationConfig(ctx context.Context, account *citypes.Account, services []*citypes.StorableCloudIntegrationService) (*citypes.ProviderIntegrationConfig, error)
|
||||
}
|
||||
|
||||
type Handler interface {
|
||||
GetConnectionCredentials(http.ResponseWriter, *http.Request)
|
||||
CreateAccount(http.ResponseWriter, *http.Request)
|
||||
ListAccounts(http.ResponseWriter, *http.Request)
|
||||
GetAccount(http.ResponseWriter, *http.Request)
|
||||
|
||||
@@ -447,9 +447,9 @@
|
||||
"telemetryCollectionStrategy": {
|
||||
"aws": {
|
||||
"metrics": {
|
||||
"streamFilters": [
|
||||
"cloudwatchMetricStreamFilters": [
|
||||
{
|
||||
"namespace": "AWS/ApplicationELB"
|
||||
"Namespace": "AWS/ApplicationELB"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -171,14 +171,14 @@
|
||||
"telemetryCollectionStrategy": {
|
||||
"aws": {
|
||||
"metrics": {
|
||||
"streamFilters": [
|
||||
"cloudwatchMetricStreamFilters": [
|
||||
{
|
||||
"namespace": "AWS/ApiGateway"
|
||||
"Namespace": "AWS/ApiGateway"
|
||||
}
|
||||
]
|
||||
},
|
||||
"logs": {
|
||||
"subscriptions": [
|
||||
"cloudwatchLogsSubscriptions": [
|
||||
{
|
||||
"logGroupNamePrefix": "API-Gateway",
|
||||
"filterPattern": ""
|
||||
|
||||
@@ -374,9 +374,9 @@
|
||||
"telemetryCollectionStrategy": {
|
||||
"aws": {
|
||||
"metrics": {
|
||||
"streamFilters": [
|
||||
"cloudwatchMetricStreamFilters": [
|
||||
{
|
||||
"namespace": "AWS/DynamoDB"
|
||||
"Namespace": "AWS/DynamoDB"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -495,12 +495,12 @@
|
||||
"telemetryCollectionStrategy": {
|
||||
"aws": {
|
||||
"metrics": {
|
||||
"streamFilters": [
|
||||
"cloudwatchMetricStreamFilters": [
|
||||
{
|
||||
"namespace": "AWS/EC2"
|
||||
"Namespace": "AWS/EC2"
|
||||
},
|
||||
{
|
||||
"namespace": "CWAgent"
|
||||
"Namespace": "CWAgent"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -823,17 +823,17 @@
|
||||
"telemetryCollectionStrategy": {
|
||||
"aws": {
|
||||
"metrics": {
|
||||
"streamFilters": [
|
||||
"cloudwatchMetricStreamFilters": [
|
||||
{
|
||||
"namespace": "AWS/ECS"
|
||||
"Namespace": "AWS/ECS"
|
||||
},
|
||||
{
|
||||
"namespace": "ECS/ContainerInsights"
|
||||
"Namespace": "ECS/ContainerInsights"
|
||||
}
|
||||
]
|
||||
},
|
||||
"logs": {
|
||||
"subscriptions": [
|
||||
"cloudwatchLogsSubscriptions": [
|
||||
{
|
||||
"logGroupNamePrefix": "/ecs",
|
||||
"filterPattern": ""
|
||||
|
||||
@@ -2702,17 +2702,17 @@
|
||||
"telemetryCollectionStrategy": {
|
||||
"aws": {
|
||||
"metrics": {
|
||||
"streamFilters": [
|
||||
"cloudwatchMetricStreamFilters": [
|
||||
{
|
||||
"namespace": "AWS/EKS"
|
||||
"Namespace": "AWS/EKS"
|
||||
},
|
||||
{
|
||||
"namespace": "ContainerInsights"
|
||||
"Namespace": "ContainerInsights"
|
||||
}
|
||||
]
|
||||
},
|
||||
"logs": {
|
||||
"subscriptions": [
|
||||
"cloudwatchLogsSubscriptions": [
|
||||
{
|
||||
"logGroupNamePrefix": "/aws/containerinsights",
|
||||
"filterPattern": ""
|
||||
|
||||
@@ -1934,9 +1934,9 @@
|
||||
"telemetryCollectionStrategy": {
|
||||
"aws": {
|
||||
"metrics": {
|
||||
"streamFilters": [
|
||||
"cloudwatchMetricStreamFilters": [
|
||||
{
|
||||
"namespace": "AWS/ElastiCache"
|
||||
"Namespace": "AWS/ElastiCache"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -271,14 +271,14 @@
|
||||
"telemetryCollectionStrategy": {
|
||||
"aws": {
|
||||
"metrics": {
|
||||
"streamFilters": [
|
||||
"cloudwatchMetricStreamFilters": [
|
||||
{
|
||||
"namespace": "AWS/Lambda"
|
||||
"Namespace": "AWS/Lambda"
|
||||
}
|
||||
]
|
||||
},
|
||||
"logs": {
|
||||
"subscriptions": [
|
||||
"cloudwatchLogsSubscriptions": [
|
||||
{
|
||||
"logGroupNamePrefix": "/aws/lambda",
|
||||
"filterPattern": ""
|
||||
|
||||
@@ -1070,9 +1070,9 @@
|
||||
"telemetryCollectionStrategy": {
|
||||
"aws": {
|
||||
"metrics": {
|
||||
"streamFilters": [
|
||||
"cloudwatchMetricStreamFilters": [
|
||||
{
|
||||
"namespace": "AWS/Kafka"
|
||||
"Namespace": "AWS/Kafka"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -775,14 +775,14 @@
|
||||
"telemetryCollectionStrategy": {
|
||||
"aws": {
|
||||
"metrics": {
|
||||
"streamFilters": [
|
||||
"cloudwatchMetricStreamFilters": [
|
||||
{
|
||||
"namespace": "AWS/RDS"
|
||||
"Namespace": "AWS/RDS"
|
||||
}
|
||||
]
|
||||
},
|
||||
"logs": {
|
||||
"subscriptions": [
|
||||
"cloudwatchLogsSubscriptions": [
|
||||
{
|
||||
"logGroupNamePrefix": "/aws/rds",
|
||||
"filterPattern": ""
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"telemetryCollectionStrategy": {
|
||||
"aws": {
|
||||
"logs": {
|
||||
"subscriptions": [
|
||||
"cloudwatchLogsSubscriptions": [
|
||||
{
|
||||
"logGroupNamePrefix": "x/signoz/forwarder",
|
||||
"filterPattern": ""
|
||||
|
||||
@@ -110,9 +110,9 @@
|
||||
"telemetryCollectionStrategy": {
|
||||
"aws": {
|
||||
"metrics": {
|
||||
"streamFilters": [
|
||||
"cloudwatchMetricStreamFilters": [
|
||||
{
|
||||
"namespace": "AWS/SNS"
|
||||
"Namespace": "AWS/SNS"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -230,9 +230,9 @@
|
||||
"telemetryCollectionStrategy": {
|
||||
"aws": {
|
||||
"metrics": {
|
||||
"streamFilters": [
|
||||
"cloudwatchMetricStreamFilters": [
|
||||
{
|
||||
"namespace": "AWS/SQS"
|
||||
"Namespace": "AWS/SQS"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -12,10 +12,6 @@ func NewHandler() cloudintegration.Handler {
|
||||
return &handler{}
|
||||
}
|
||||
|
||||
func (handler *handler) GetConnectionCredentials(http.ResponseWriter, *http.Request) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (handler *handler) CreateAccount(writer http.ResponseWriter, request *http.Request) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
|
||||
@@ -34,25 +34,6 @@ func (store *store) GetAccountByID(ctx context.Context, orgID, id valuer.UUID, p
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func (store *store) GetConnectedAccount(ctx context.Context, orgID valuer.UUID, provider cloudintegrationtypes.CloudProviderType, providerAccountID string) (*cloudintegrationtypes.StorableCloudIntegration, error) {
|
||||
account := new(cloudintegrationtypes.StorableCloudIntegration)
|
||||
err := store.
|
||||
store.
|
||||
BunDBCtx(ctx).
|
||||
NewSelect().
|
||||
Model(account).
|
||||
Where("org_id = ?", orgID).
|
||||
Where("provider = ?", provider).
|
||||
Where("account_id = ?", providerAccountID).
|
||||
Where("last_agent_report IS NOT NULL").
|
||||
Where("removed_at IS NULL").
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, store.store.WrapNotFoundErrf(err, cloudintegrationtypes.ErrCodeCloudIntegrationNotFound, "connected account with provider account id %s not found", providerAccountID)
|
||||
}
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func (store *store) ListConnectedAccounts(ctx context.Context, orgID valuer.UUID, provider cloudintegrationtypes.CloudProviderType) ([]*cloudintegrationtypes.StorableCloudIntegration, error) {
|
||||
var accounts []*cloudintegrationtypes.StorableCloudIntegration
|
||||
err := store.
|
||||
@@ -115,6 +96,25 @@ func (store *store) RemoveAccount(ctx context.Context, orgID, id valuer.UUID, pr
|
||||
return err
|
||||
}
|
||||
|
||||
func (store *store) GetConnectedAccount(ctx context.Context, orgID valuer.UUID, provider cloudintegrationtypes.CloudProviderType, providerAccountID string) (*cloudintegrationtypes.StorableCloudIntegration, error) {
|
||||
account := new(cloudintegrationtypes.StorableCloudIntegration)
|
||||
err := store.
|
||||
store.
|
||||
BunDBCtx(ctx).
|
||||
NewSelect().
|
||||
Model(account).
|
||||
Where("org_id = ?", orgID).
|
||||
Where("provider = ?", provider).
|
||||
Where("account_id = ?", providerAccountID).
|
||||
Where("last_agent_report IS NOT NULL").
|
||||
Where("removed_at IS NULL").
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, store.store.WrapNotFoundErrf(err, cloudintegrationtypes.ErrCodeCloudIntegrationNotFound, "connected account with provider account id %s not found", providerAccountID)
|
||||
}
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func (store *store) GetServiceByServiceID(ctx context.Context, cloudIntegrationID valuer.UUID, serviceID cloudintegrationtypes.ServiceID) (*cloudintegrationtypes.StorableCloudIntegrationService, error) {
|
||||
service := new(cloudintegrationtypes.StorableCloudIntegrationService)
|
||||
err := store.
|
||||
@@ -172,9 +172,3 @@ func (store *store) UpdateService(ctx context.Context, service *cloudintegration
|
||||
Exec(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
func (store *store) RunInTx(ctx context.Context, cb func(ctx context.Context) error) error {
|
||||
return store.store.RunInTxCtx(ctx, nil, func(ctx context.Context) error {
|
||||
return cb(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package tracedetail
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/types/tracedetailtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
// Handler exposes HTTP handlers for trace detail APIs.
|
||||
type Handler interface {
|
||||
GetWaterfall(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
|
||||
// Module defines the business logic for trace detail operations.
|
||||
type Module interface {
|
||||
GetWaterfall(ctx context.Context, orgID valuer.UUID, traceID string, req *tracedetailtypes.WaterfallRequest) (*tracedetailtypes.WaterfallResponse, error)
|
||||
}
|
||||
@@ -40,7 +40,6 @@ type querier struct {
|
||||
promEngine prometheus.Prometheus
|
||||
traceStmtBuilder qbtypes.StatementBuilder[qbtypes.TraceAggregation]
|
||||
logStmtBuilder qbtypes.StatementBuilder[qbtypes.LogAggregation]
|
||||
auditStmtBuilder qbtypes.StatementBuilder[qbtypes.LogAggregation]
|
||||
metricStmtBuilder qbtypes.StatementBuilder[qbtypes.MetricAggregation]
|
||||
meterStmtBuilder qbtypes.StatementBuilder[qbtypes.MetricAggregation]
|
||||
traceOperatorStmtBuilder qbtypes.TraceOperatorStatementBuilder
|
||||
@@ -57,7 +56,6 @@ func New(
|
||||
promEngine prometheus.Prometheus,
|
||||
traceStmtBuilder qbtypes.StatementBuilder[qbtypes.TraceAggregation],
|
||||
logStmtBuilder qbtypes.StatementBuilder[qbtypes.LogAggregation],
|
||||
auditStmtBuilder qbtypes.StatementBuilder[qbtypes.LogAggregation],
|
||||
metricStmtBuilder qbtypes.StatementBuilder[qbtypes.MetricAggregation],
|
||||
meterStmtBuilder qbtypes.StatementBuilder[qbtypes.MetricAggregation],
|
||||
traceOperatorStmtBuilder qbtypes.TraceOperatorStatementBuilder,
|
||||
@@ -71,7 +69,6 @@ func New(
|
||||
promEngine: promEngine,
|
||||
traceStmtBuilder: traceStmtBuilder,
|
||||
logStmtBuilder: logStmtBuilder,
|
||||
auditStmtBuilder: auditStmtBuilder,
|
||||
metricStmtBuilder: metricStmtBuilder,
|
||||
meterStmtBuilder: meterStmtBuilder,
|
||||
traceOperatorStmtBuilder: traceOperatorStmtBuilder,
|
||||
@@ -364,11 +361,7 @@ func (q *querier) QueryRange(ctx context.Context, orgID valuer.UUID, req *qbtype
|
||||
case qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]:
|
||||
spec.ShiftBy = extractShiftFromBuilderQuery(spec)
|
||||
timeRange := adjustTimeRangeForShift(spec, qbtypes.TimeRange{From: req.Start, To: req.End}, req.RequestType)
|
||||
stmtBuilder := q.logStmtBuilder
|
||||
if spec.Source == telemetrytypes.SourceAudit {
|
||||
stmtBuilder = q.auditStmtBuilder
|
||||
}
|
||||
bq := newBuilderQuery(q.logger, q.telemetryStore, stmtBuilder, spec, timeRange, req.RequestType, tmplVars)
|
||||
bq := newBuilderQuery(q.logger, q.telemetryStore, q.logStmtBuilder, spec, timeRange, req.RequestType, tmplVars)
|
||||
queries[spec.Name] = bq
|
||||
steps[spec.Name] = spec.StepInterval
|
||||
case qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]:
|
||||
@@ -557,11 +550,7 @@ func (q *querier) QueryRawStream(ctx context.Context, orgID valuer.UUID, req *qb
|
||||
case <-tick:
|
||||
// timestamp end is not specified here
|
||||
timeRange := adjustTimeRangeForShift(spec, qbtypes.TimeRange{From: tsStart}, req.RequestType)
|
||||
liveTailStmtBuilder := q.logStmtBuilder
|
||||
if spec.Source == telemetrytypes.SourceAudit {
|
||||
liveTailStmtBuilder = q.auditStmtBuilder
|
||||
}
|
||||
bq := newBuilderQuery(q.logger, q.telemetryStore, liveTailStmtBuilder, spec, timeRange, req.RequestType, map[string]qbtypes.VariableItem{
|
||||
bq := newBuilderQuery(q.logger, q.telemetryStore, q.logStmtBuilder, spec, timeRange, req.RequestType, map[string]qbtypes.VariableItem{
|
||||
"id": {
|
||||
Value: updatedLogID,
|
||||
},
|
||||
@@ -861,11 +850,7 @@ func (q *querier) createRangedQuery(originalQuery qbtypes.Query, timeRange qbtyp
|
||||
specCopy := qt.spec.Copy()
|
||||
specCopy.ShiftBy = extractShiftFromBuilderQuery(specCopy)
|
||||
adjustedTimeRange := adjustTimeRangeForShift(specCopy, timeRange, qt.kind)
|
||||
shiftStmtBuilder := q.logStmtBuilder
|
||||
if qt.spec.Source == telemetrytypes.SourceAudit {
|
||||
shiftStmtBuilder = q.auditStmtBuilder
|
||||
}
|
||||
return newBuilderQuery(q.logger, q.telemetryStore, shiftStmtBuilder, specCopy, adjustedTimeRange, qt.kind, qt.variables)
|
||||
return newBuilderQuery(q.logger, q.telemetryStore, q.logStmtBuilder, specCopy, adjustedTimeRange, qt.kind, qt.variables)
|
||||
|
||||
case *builderQuery[qbtypes.MetricAggregation]:
|
||||
specCopy := qt.spec.Copy()
|
||||
|
||||
@@ -47,7 +47,6 @@ func TestQueryRange_MetricTypeMissing(t *testing.T) {
|
||||
nil, // prometheus
|
||||
nil, // traceStmtBuilder
|
||||
nil, // logStmtBuilder
|
||||
nil, // auditStmtBuilder
|
||||
nil, // metricStmtBuilder
|
||||
nil, // meterStmtBuilder
|
||||
nil, // traceOperatorStmtBuilder
|
||||
@@ -111,7 +110,6 @@ func TestQueryRange_MetricTypeFromStore(t *testing.T) {
|
||||
nil, // prometheus
|
||||
nil, // traceStmtBuilder
|
||||
nil, // logStmtBuilder
|
||||
nil, // auditStmtBuilder
|
||||
&mockMetricStmtBuilder{}, // metricStmtBuilder
|
||||
nil, // meterStmtBuilder
|
||||
nil, // traceOperatorStmtBuilder
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/prometheus"
|
||||
"github.com/SigNoz/signoz/pkg/querier"
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
"github.com/SigNoz/signoz/pkg/telemetryaudit"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrylogs"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrymetadata"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrymeter"
|
||||
@@ -64,11 +63,6 @@ func newProvider(
|
||||
telemetrylogs.TagAttributesV2TableName,
|
||||
telemetrylogs.LogAttributeKeysTblName,
|
||||
telemetrylogs.LogResourceKeysTblName,
|
||||
telemetryaudit.DBName,
|
||||
telemetryaudit.AuditLogsTableName,
|
||||
telemetryaudit.TagAttributesTableName,
|
||||
telemetryaudit.LogAttributeKeysTblName,
|
||||
telemetryaudit.LogResourceKeysTblName,
|
||||
telemetrymetadata.DBName,
|
||||
telemetrymetadata.AttributesMetadataLocalTableName,
|
||||
telemetrymetadata.ColumnEvolutionMetadataTableName,
|
||||
@@ -88,13 +82,13 @@ func newProvider(
|
||||
telemetryStore,
|
||||
)
|
||||
|
||||
// Create trace operator statement builder
|
||||
// ADD: Create trace operator statement builder
|
||||
traceOperatorStmtBuilder := telemetrytraces.NewTraceOperatorStatementBuilder(
|
||||
settings,
|
||||
telemetryMetadataStore,
|
||||
traceFieldMapper,
|
||||
traceConditionBuilder,
|
||||
traceStmtBuilder,
|
||||
traceStmtBuilder, // Pass the regular trace statement builder
|
||||
traceAggExprRewriter,
|
||||
)
|
||||
|
||||
@@ -118,26 +112,6 @@ func newProvider(
|
||||
telemetrylogs.GetBodyJSONKey,
|
||||
)
|
||||
|
||||
// Create audit statement builder
|
||||
auditFieldMapper := telemetryaudit.NewFieldMapper()
|
||||
auditConditionBuilder := telemetryaudit.NewConditionBuilder(auditFieldMapper)
|
||||
auditAggExprRewriter := querybuilder.NewAggExprRewriter(
|
||||
settings,
|
||||
telemetryaudit.DefaultFullTextColumn,
|
||||
auditFieldMapper,
|
||||
auditConditionBuilder,
|
||||
nil,
|
||||
)
|
||||
auditStmtBuilder := telemetryaudit.NewAuditQueryStatementBuilder(
|
||||
settings,
|
||||
telemetryMetadataStore,
|
||||
auditFieldMapper,
|
||||
auditConditionBuilder,
|
||||
auditAggExprRewriter,
|
||||
telemetryaudit.DefaultFullTextColumn,
|
||||
nil,
|
||||
)
|
||||
|
||||
// Create metric statement builder
|
||||
metricFieldMapper := telemetrymetrics.NewFieldMapper()
|
||||
metricConditionBuilder := telemetrymetrics.NewConditionBuilder(metricFieldMapper)
|
||||
@@ -174,7 +148,6 @@ func newProvider(
|
||||
prometheus,
|
||||
traceStmtBuilder,
|
||||
logStmtBuilder,
|
||||
auditStmtBuilder,
|
||||
metricStmtBuilder,
|
||||
meterStmtBuilder,
|
||||
traceOperatorStmtBuilder,
|
||||
|
||||
@@ -208,7 +208,7 @@ func (s *Server) createPublicServer(api *APIHandler, web web.Web) (*http.Server,
|
||||
s.config.APIServer.Timeout.Default,
|
||||
s.config.APIServer.Timeout.Max,
|
||||
).Wrap)
|
||||
r.Use(middleware.NewAudit(s.signoz.Instrumentation.Logger(), s.config.APIServer.Logging.ExcludedRoutes, s.signoz.Auditor).Wrap)
|
||||
r.Use(middleware.NewAudit(s.signoz.Instrumentation.Logger(), s.config.APIServer.Logging.ExcludedRoutes, nil).Wrap)
|
||||
r.Use(middleware.NewComment().Wrap)
|
||||
|
||||
am := middleware.NewAuthZ(s.signoz.Instrumentation.Logger(), s.signoz.Modules.OrgGetter, s.signoz.Authz)
|
||||
|
||||
@@ -46,7 +46,6 @@ func prepareQuerierForMetrics(t *testing.T, telemetryStore telemetrystore.Teleme
|
||||
nil, // prometheus
|
||||
nil, // traceStmtBuilder
|
||||
nil, // logStmtBuilder
|
||||
nil, // auditStmtBuilder
|
||||
metricStmtBuilder,
|
||||
nil, // meterStmtBuilder
|
||||
nil, // traceOperatorStmtBuilder
|
||||
@@ -92,7 +91,6 @@ func prepareQuerierForLogs(telemetryStore telemetrystore.TelemetryStore, keysMap
|
||||
nil, // prometheus
|
||||
nil, // traceStmtBuilder
|
||||
logStmtBuilder, // logStmtBuilder
|
||||
nil, // auditStmtBuilder
|
||||
nil, // metricStmtBuilder
|
||||
nil, // meterStmtBuilder
|
||||
nil, // traceOperatorStmtBuilder
|
||||
@@ -133,7 +131,6 @@ func prepareQuerierForTraces(telemetryStore telemetrystore.TelemetryStore, keysM
|
||||
nil, // prometheus
|
||||
traceStmtBuilder, // traceStmtBuilder
|
||||
nil, // logStmtBuilder
|
||||
nil, // auditStmtBuilder
|
||||
nil, // metricStmtBuilder
|
||||
nil, // meterStmtBuilder
|
||||
nil, // traceOperatorStmtBuilder
|
||||
|
||||
@@ -818,9 +818,9 @@ func (v *filterExpressionVisitor) VisitFunctionCall(ctx *grammar.FunctionCallCon
|
||||
case "has":
|
||||
cond = fmt.Sprintf("has(%s, %s)", fieldName, v.builder.Var(value[0]))
|
||||
case "hasAny":
|
||||
cond = fmt.Sprintf("hasAny(%s, %s)", fieldName, v.builder.Var(value[0]))
|
||||
cond = fmt.Sprintf("hasAny(%s, %s)", fieldName, v.builder.Var(value))
|
||||
case "hasAll":
|
||||
cond = fmt.Sprintf("hasAll(%s, %s)", fieldName, v.builder.Var(value[0]))
|
||||
cond = fmt.Sprintf("hasAll(%s, %s)", fieldName, v.builder.Var(value))
|
||||
}
|
||||
conds = append(conds, cond)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager"
|
||||
"github.com/SigNoz/signoz/pkg/analytics"
|
||||
"github.com/SigNoz/signoz/pkg/apiserver"
|
||||
"github.com/SigNoz/signoz/pkg/auditor"
|
||||
"github.com/SigNoz/signoz/pkg/cache"
|
||||
"github.com/SigNoz/signoz/pkg/config"
|
||||
"github.com/SigNoz/signoz/pkg/emailing"
|
||||
@@ -124,9 +123,6 @@ type Config struct {
|
||||
|
||||
// ServiceAccount config
|
||||
ServiceAccount serviceaccount.Config `mapstructure:"serviceaccount"`
|
||||
|
||||
// Auditor config
|
||||
Auditor auditor.Config `mapstructure:"auditor"`
|
||||
}
|
||||
|
||||
func NewConfig(ctx context.Context, logger *slog.Logger, resolverConfig config.ResolverConfig) (Config, error) {
|
||||
@@ -157,7 +153,6 @@ func NewConfig(ctx context.Context, logger *slog.Logger, resolverConfig config.R
|
||||
user.NewConfigFactory(),
|
||||
identn.NewConfigFactory(),
|
||||
serviceaccount.NewConfigFactory(),
|
||||
auditor.NewConfigFactory(),
|
||||
}
|
||||
|
||||
conf, err := config.New(ctx, resolverConfig, configFactories)
|
||||
|
||||
@@ -3,8 +3,6 @@ package signoz
|
||||
import (
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager"
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager/nfmanager"
|
||||
"github.com/SigNoz/signoz/pkg/auditor"
|
||||
"github.com/SigNoz/signoz/pkg/auditor/noopauditor"
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager/nfmanager/rulebasednotification"
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager/signozalertmanager"
|
||||
"github.com/SigNoz/signoz/pkg/analytics"
|
||||
@@ -314,12 +312,6 @@ func NewGlobalProviderFactories(identNConfig identn.Config) factory.NamedMap[fac
|
||||
)
|
||||
}
|
||||
|
||||
func NewAuditorProviderFactories() factory.NamedMap[factory.ProviderFactory[auditor.Auditor, auditor.Config]] {
|
||||
return factory.MustNewNamedMap(
|
||||
noopauditor.NewFactory(),
|
||||
)
|
||||
}
|
||||
|
||||
func NewFlaggerProviderFactories(registry featuretypes.Registry) factory.NamedMap[factory.ProviderFactory[flagger.FlaggerProvider, flagger.Config]] {
|
||||
return factory.MustNewNamedMap(
|
||||
configflagger.NewFactory(registry),
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager"
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager/nfmanager"
|
||||
"github.com/SigNoz/signoz/pkg/auditor"
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager/nfmanager/nfroutingstore/sqlroutingstore"
|
||||
"github.com/SigNoz/signoz/pkg/analytics"
|
||||
"github.com/SigNoz/signoz/pkg/apiserver"
|
||||
@@ -34,7 +33,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/sqlschema"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
"github.com/SigNoz/signoz/pkg/statsreporter"
|
||||
"github.com/SigNoz/signoz/pkg/telemetryaudit"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrylogs"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrymetadata"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrymeter"
|
||||
@@ -76,7 +74,6 @@ type SigNoz struct {
|
||||
QueryParser queryparser.QueryParser
|
||||
Flagger flagger.Flagger
|
||||
Gateway gateway.Gateway
|
||||
Auditor auditor.Auditor
|
||||
}
|
||||
|
||||
func New(
|
||||
@@ -96,7 +93,6 @@ func New(
|
||||
authzCallback func(context.Context, sqlstore.SQLStore, licensing.Licensing, dashboard.Module) (factory.ProviderFactory[authz.AuthZ, authz.Config], error),
|
||||
dashboardModuleCallback func(sqlstore.SQLStore, factory.ProviderSettings, analytics.Analytics, organization.Getter, queryparser.QueryParser, querier.Querier, licensing.Licensing) dashboard.Module,
|
||||
gatewayProviderFactory func(licensing.Licensing) factory.ProviderFactory[gateway.Gateway, gateway.Config],
|
||||
auditorProviderFactories func(licensing.Licensing) factory.NamedMap[factory.ProviderFactory[auditor.Auditor, auditor.Config]],
|
||||
querierHandlerCallback func(factory.ProviderSettings, querier.Querier, analytics.Analytics) querier.Handler,
|
||||
) (*SigNoz, error) {
|
||||
// Initialize instrumentation
|
||||
@@ -374,12 +370,6 @@ func New(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Initialize auditor from the variant-specific provider factories
|
||||
auditor, err := factory.NewProviderFromNamedMap(ctx, providerSettings, config.Auditor, auditorProviderFactories(licensing), config.Auditor.Provider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Initialize authns
|
||||
store := sqlauthnstore.NewStore(sqlstore)
|
||||
authNs, err := authNsCallback(ctx, providerSettings, store, licensing)
|
||||
@@ -405,11 +395,6 @@ func New(
|
||||
telemetrylogs.TagAttributesV2TableName,
|
||||
telemetrylogs.LogAttributeKeysTblName,
|
||||
telemetrylogs.LogResourceKeysTblName,
|
||||
telemetryaudit.DBName,
|
||||
telemetryaudit.AuditLogsTableName,
|
||||
telemetryaudit.TagAttributesTableName,
|
||||
telemetryaudit.LogAttributeKeysTblName,
|
||||
telemetryaudit.LogResourceKeysTblName,
|
||||
telemetrymetadata.DBName,
|
||||
telemetrymetadata.AttributesMetadataLocalTableName,
|
||||
telemetrymetadata.ColumnEvolutionMetadataTableName,
|
||||
@@ -479,7 +464,6 @@ func New(
|
||||
factory.NewNamedService(factory.MustNewName("tokenizer"), tokenizer),
|
||||
factory.NewNamedService(factory.MustNewName("authz"), authz),
|
||||
factory.NewNamedService(factory.MustNewName("user"), userService, factory.MustNewName("authz")),
|
||||
factory.NewNamedService(factory.MustNewName("auditor"), auditor),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -526,6 +510,5 @@ func New(
|
||||
QueryParser: queryParser,
|
||||
Flagger: flagger,
|
||||
Gateway: gateway,
|
||||
Auditor: auditor,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
package telemetryaudit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
schema "github.com/SigNoz/signoz-otel-collector/cmd/signozschemamigrator/schema_migrator"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/huandu/go-sqlbuilder"
|
||||
)
|
||||
|
||||
type conditionBuilder struct {
|
||||
fm qbtypes.FieldMapper
|
||||
}
|
||||
|
||||
func NewConditionBuilder(fm qbtypes.FieldMapper) *conditionBuilder {
|
||||
return &conditionBuilder{fm: fm}
|
||||
}
|
||||
|
||||
func (c *conditionBuilder) conditionFor(
|
||||
ctx context.Context,
|
||||
startNs, endNs uint64,
|
||||
key *telemetrytypes.TelemetryFieldKey,
|
||||
operator qbtypes.FilterOperator,
|
||||
value any,
|
||||
sb *sqlbuilder.SelectBuilder,
|
||||
) (string, error) {
|
||||
columns, err := c.fm.ColumnFor(ctx, startNs, endNs, key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if operator.IsStringSearchOperator() {
|
||||
value = querybuilder.FormatValueForContains(value)
|
||||
}
|
||||
|
||||
fieldExpression, err := c.fm.FieldFor(ctx, startNs, endNs, key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fieldExpression, value = querybuilder.DataTypeCollisionHandledFieldName(key, value, fieldExpression, operator)
|
||||
|
||||
switch operator {
|
||||
case qbtypes.FilterOperatorEqual:
|
||||
return sb.E(fieldExpression, value), nil
|
||||
case qbtypes.FilterOperatorNotEqual:
|
||||
return sb.NE(fieldExpression, value), nil
|
||||
case qbtypes.FilterOperatorGreaterThan:
|
||||
return sb.G(fieldExpression, value), nil
|
||||
case qbtypes.FilterOperatorGreaterThanOrEq:
|
||||
return sb.GE(fieldExpression, value), nil
|
||||
case qbtypes.FilterOperatorLessThan:
|
||||
return sb.LT(fieldExpression, value), nil
|
||||
case qbtypes.FilterOperatorLessThanOrEq:
|
||||
return sb.LE(fieldExpression, value), nil
|
||||
case qbtypes.FilterOperatorLike:
|
||||
return sb.Like(fieldExpression, value), nil
|
||||
case qbtypes.FilterOperatorNotLike:
|
||||
return sb.NotLike(fieldExpression, value), nil
|
||||
case qbtypes.FilterOperatorILike:
|
||||
return sb.ILike(fieldExpression, value), nil
|
||||
case qbtypes.FilterOperatorNotILike:
|
||||
return sb.NotILike(fieldExpression, value), nil
|
||||
case qbtypes.FilterOperatorContains:
|
||||
return sb.ILike(fieldExpression, fmt.Sprintf("%%%s%%", value)), nil
|
||||
case qbtypes.FilterOperatorNotContains:
|
||||
return sb.NotILike(fieldExpression, fmt.Sprintf("%%%s%%", value)), nil
|
||||
case qbtypes.FilterOperatorRegexp:
|
||||
return fmt.Sprintf(`match(%s, %s)`, sqlbuilder.Escape(fieldExpression), sb.Var(value)), nil
|
||||
case qbtypes.FilterOperatorNotRegexp:
|
||||
return fmt.Sprintf(`NOT match(%s, %s)`, sqlbuilder.Escape(fieldExpression), sb.Var(value)), nil
|
||||
case qbtypes.FilterOperatorBetween:
|
||||
values, ok := value.([]any)
|
||||
if !ok {
|
||||
return "", qbtypes.ErrBetweenValues
|
||||
}
|
||||
if len(values) != 2 {
|
||||
return "", qbtypes.ErrBetweenValues
|
||||
}
|
||||
return sb.Between(fieldExpression, values[0], values[1]), nil
|
||||
case qbtypes.FilterOperatorNotBetween:
|
||||
values, ok := value.([]any)
|
||||
if !ok {
|
||||
return "", qbtypes.ErrBetweenValues
|
||||
}
|
||||
if len(values) != 2 {
|
||||
return "", qbtypes.ErrBetweenValues
|
||||
}
|
||||
return sb.NotBetween(fieldExpression, values[0], values[1]), nil
|
||||
case qbtypes.FilterOperatorIn:
|
||||
values, ok := value.([]any)
|
||||
if !ok {
|
||||
return "", qbtypes.ErrInValues
|
||||
}
|
||||
conditions := []string{}
|
||||
for _, value := range values {
|
||||
conditions = append(conditions, sb.E(fieldExpression, value))
|
||||
}
|
||||
return sb.Or(conditions...), nil
|
||||
case qbtypes.FilterOperatorNotIn:
|
||||
values, ok := value.([]any)
|
||||
if !ok {
|
||||
return "", qbtypes.ErrInValues
|
||||
}
|
||||
conditions := []string{}
|
||||
for _, value := range values {
|
||||
conditions = append(conditions, sb.NE(fieldExpression, value))
|
||||
}
|
||||
return sb.And(conditions...), nil
|
||||
case qbtypes.FilterOperatorExists, qbtypes.FilterOperatorNotExists:
|
||||
var value any
|
||||
column := columns[0]
|
||||
|
||||
switch column.Type.GetType() {
|
||||
case schema.ColumnTypeEnumJSON:
|
||||
if operator == qbtypes.FilterOperatorExists {
|
||||
return sb.IsNotNull(fieldExpression), nil
|
||||
}
|
||||
return sb.IsNull(fieldExpression), nil
|
||||
case schema.ColumnTypeEnumLowCardinality:
|
||||
switch elementType := column.Type.(schema.LowCardinalityColumnType).ElementType; elementType.GetType() {
|
||||
case schema.ColumnTypeEnumString:
|
||||
value = ""
|
||||
if operator == qbtypes.FilterOperatorExists {
|
||||
return sb.NE(fieldExpression, value), nil
|
||||
}
|
||||
return sb.E(fieldExpression, value), nil
|
||||
default:
|
||||
return "", errors.NewInvalidInputf(errors.CodeInvalidInput, "exists operator is not supported for low cardinality column type %s", elementType)
|
||||
}
|
||||
case schema.ColumnTypeEnumString:
|
||||
value = ""
|
||||
if operator == qbtypes.FilterOperatorExists {
|
||||
return sb.NE(fieldExpression, value), nil
|
||||
}
|
||||
return sb.E(fieldExpression, value), nil
|
||||
case schema.ColumnTypeEnumUInt64, schema.ColumnTypeEnumUInt32, schema.ColumnTypeEnumUInt8:
|
||||
value = 0
|
||||
if operator == qbtypes.FilterOperatorExists {
|
||||
return sb.NE(fieldExpression, value), nil
|
||||
}
|
||||
return sb.E(fieldExpression, value), nil
|
||||
case schema.ColumnTypeEnumMap:
|
||||
keyType := column.Type.(schema.MapColumnType).KeyType
|
||||
if _, ok := keyType.(schema.LowCardinalityColumnType); !ok {
|
||||
return "", errors.NewInvalidInputf(errors.CodeInvalidInput, "key type %s is not supported for map column type %s", keyType, column.Type)
|
||||
}
|
||||
|
||||
switch valueType := column.Type.(schema.MapColumnType).ValueType; valueType.GetType() {
|
||||
case schema.ColumnTypeEnumString, schema.ColumnTypeEnumBool, schema.ColumnTypeEnumFloat64:
|
||||
leftOperand := fmt.Sprintf("mapContains(%s, '%s')", column.Name, key.Name)
|
||||
if key.Materialized {
|
||||
leftOperand = telemetrytypes.FieldKeyToMaterializedColumnNameForExists(key)
|
||||
}
|
||||
if operator == qbtypes.FilterOperatorExists {
|
||||
return sb.E(leftOperand, true), nil
|
||||
}
|
||||
return sb.NE(leftOperand, true), nil
|
||||
default:
|
||||
return "", errors.NewInvalidInputf(errors.CodeInvalidInput, "exists operator is not supported for map column type %s", valueType)
|
||||
}
|
||||
default:
|
||||
return "", errors.NewInvalidInputf(errors.CodeInvalidInput, "exists operator is not supported for column type %s", column.Type)
|
||||
}
|
||||
}
|
||||
return "", errors.NewInvalidInputf(errors.CodeInvalidInput, "unsupported operator: %v", operator)
|
||||
}
|
||||
|
||||
func (c *conditionBuilder) ConditionFor(
|
||||
ctx context.Context,
|
||||
startNs uint64,
|
||||
endNs uint64,
|
||||
key *telemetrytypes.TelemetryFieldKey,
|
||||
operator qbtypes.FilterOperator,
|
||||
value any,
|
||||
sb *sqlbuilder.SelectBuilder,
|
||||
) (string, error) {
|
||||
condition, err := c.conditionFor(ctx, startNs, endNs, key, operator, value, sb)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if key.FieldContext == telemetrytypes.FieldContextLog || key.FieldContext == telemetrytypes.FieldContextScope {
|
||||
return condition, nil
|
||||
}
|
||||
|
||||
if operator.AddDefaultExistsFilter() {
|
||||
existsCondition, err := c.conditionFor(ctx, startNs, endNs, key, qbtypes.FilterOperatorExists, nil, sb)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return sb.And(condition, existsCondition), nil
|
||||
}
|
||||
|
||||
return condition, nil
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
package telemetryaudit
|
||||
|
||||
import (
|
||||
schema "github.com/SigNoz/signoz-otel-collector/cmd/signozschemamigrator/schema_migrator"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
)
|
||||
|
||||
const (
|
||||
// Internal Columns.
|
||||
IDColumn = "id"
|
||||
TimestampBucketStartColumn = "ts_bucket_start"
|
||||
ResourceFingerPrintColumn = "resource_fingerprint"
|
||||
|
||||
// Intrinsic Columns.
|
||||
TimestampColumn = "timestamp"
|
||||
ObservedTimestampColumn = "observed_timestamp"
|
||||
BodyColumn = "body"
|
||||
EventNameColumn = "event_name"
|
||||
TraceIDColumn = "trace_id"
|
||||
SpanIDColumn = "span_id"
|
||||
TraceFlagsColumn = "trace_flags"
|
||||
SeverityTextColumn = "severity_text"
|
||||
SeverityNumberColumn = "severity_number"
|
||||
ScopeNameColumn = "scope_name"
|
||||
ScopeVersionColumn = "scope_version"
|
||||
|
||||
// Contextual Columns.
|
||||
AttributesStringColumn = "attributes_string"
|
||||
AttributesNumberColumn = "attributes_number"
|
||||
AttributesBoolColumn = "attributes_bool"
|
||||
ResourceColumn = "resource"
|
||||
ScopeStringColumn = "scope_string"
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultFullTextColumn = &telemetrytypes.TelemetryFieldKey{
|
||||
Name: "body",
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
FieldContext: telemetrytypes.FieldContextLog,
|
||||
FieldDataType: telemetrytypes.FieldDataTypeString,
|
||||
}
|
||||
|
||||
IntrinsicFields = map[string]telemetrytypes.TelemetryFieldKey{
|
||||
"body": {
|
||||
Name: "body",
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
FieldContext: telemetrytypes.FieldContextLog,
|
||||
FieldDataType: telemetrytypes.FieldDataTypeString,
|
||||
},
|
||||
"trace_id": {
|
||||
Name: "trace_id",
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
FieldContext: telemetrytypes.FieldContextLog,
|
||||
FieldDataType: telemetrytypes.FieldDataTypeString,
|
||||
},
|
||||
"span_id": {
|
||||
Name: "span_id",
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
FieldContext: telemetrytypes.FieldContextLog,
|
||||
FieldDataType: telemetrytypes.FieldDataTypeString,
|
||||
},
|
||||
"trace_flags": {
|
||||
Name: "trace_flags",
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
FieldContext: telemetrytypes.FieldContextLog,
|
||||
FieldDataType: telemetrytypes.FieldDataTypeNumber,
|
||||
},
|
||||
"severity_text": {
|
||||
Name: "severity_text",
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
FieldContext: telemetrytypes.FieldContextLog,
|
||||
FieldDataType: telemetrytypes.FieldDataTypeString,
|
||||
},
|
||||
"severity_number": {
|
||||
Name: "severity_number",
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
FieldContext: telemetrytypes.FieldContextLog,
|
||||
FieldDataType: telemetrytypes.FieldDataTypeNumber,
|
||||
},
|
||||
"event_name": {
|
||||
Name: "event_name",
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
FieldContext: telemetrytypes.FieldContextLog,
|
||||
FieldDataType: telemetrytypes.FieldDataTypeString,
|
||||
},
|
||||
}
|
||||
|
||||
DefaultSortingOrder = []qbtypes.OrderBy{
|
||||
{
|
||||
Key: qbtypes.OrderByKey{
|
||||
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{
|
||||
Name: TimestampColumn,
|
||||
},
|
||||
},
|
||||
Direction: qbtypes.OrderDirectionDesc,
|
||||
},
|
||||
{
|
||||
Key: qbtypes.OrderByKey{
|
||||
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{
|
||||
Name: IDColumn,
|
||||
},
|
||||
},
|
||||
Direction: qbtypes.OrderDirectionDesc,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
var auditLogColumns = map[string]*schema.Column{
|
||||
"ts_bucket_start": {Name: "ts_bucket_start", Type: schema.ColumnTypeUInt64},
|
||||
"resource_fingerprint": {Name: "resource_fingerprint", Type: schema.ColumnTypeString},
|
||||
"timestamp": {Name: "timestamp", Type: schema.ColumnTypeUInt64},
|
||||
"observed_timestamp": {Name: "observed_timestamp", Type: schema.ColumnTypeUInt64},
|
||||
"id": {Name: "id", Type: schema.ColumnTypeString},
|
||||
"trace_id": {Name: "trace_id", Type: schema.ColumnTypeString},
|
||||
"span_id": {Name: "span_id", Type: schema.ColumnTypeString},
|
||||
"trace_flags": {Name: "trace_flags", Type: schema.ColumnTypeUInt32},
|
||||
"severity_text": {Name: "severity_text", Type: schema.LowCardinalityColumnType{ElementType: schema.ColumnTypeString}},
|
||||
"severity_number": {Name: "severity_number", Type: schema.ColumnTypeUInt8},
|
||||
"body": {Name: "body", Type: schema.ColumnTypeString},
|
||||
"attributes_string": {Name: "attributes_string", Type: schema.MapColumnType{KeyType: schema.LowCardinalityColumnType{ElementType: schema.ColumnTypeString}, ValueType: schema.ColumnTypeString}},
|
||||
"attributes_number": {Name: "attributes_number", Type: schema.MapColumnType{KeyType: schema.LowCardinalityColumnType{ElementType: schema.ColumnTypeString}, ValueType: schema.ColumnTypeFloat64}},
|
||||
"attributes_bool": {Name: "attributes_bool", Type: schema.MapColumnType{KeyType: schema.LowCardinalityColumnType{ElementType: schema.ColumnTypeString}, ValueType: schema.ColumnTypeBool}},
|
||||
"resource": {Name: "resource", Type: schema.JSONColumnType{}},
|
||||
"event_name": {Name: "event_name", Type: schema.ColumnTypeString},
|
||||
"scope_name": {Name: "scope_name", Type: schema.ColumnTypeString},
|
||||
"scope_version": {Name: "scope_version", Type: schema.ColumnTypeString},
|
||||
"scope_string": {Name: "scope_string", Type: schema.MapColumnType{KeyType: schema.LowCardinalityColumnType{ElementType: schema.ColumnTypeString}, ValueType: schema.ColumnTypeString}},
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package telemetryaudit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
schema "github.com/SigNoz/signoz-otel-collector/cmd/signozschemamigrator/schema_migrator"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/huandu/go-sqlbuilder"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
type fieldMapper struct{}
|
||||
|
||||
func NewFieldMapper() qbtypes.FieldMapper {
|
||||
return &fieldMapper{}
|
||||
}
|
||||
|
||||
func (m *fieldMapper) getColumn(_ context.Context, key *telemetrytypes.TelemetryFieldKey) ([]*schema.Column, error) {
|
||||
switch key.FieldContext {
|
||||
case telemetrytypes.FieldContextResource:
|
||||
return []*schema.Column{auditLogColumns["resource"]}, nil
|
||||
case telemetrytypes.FieldContextScope:
|
||||
switch key.Name {
|
||||
case "name", "scope.name", "scope_name":
|
||||
return []*schema.Column{auditLogColumns["scope_name"]}, nil
|
||||
case "version", "scope.version", "scope_version":
|
||||
return []*schema.Column{auditLogColumns["scope_version"]}, nil
|
||||
}
|
||||
return []*schema.Column{auditLogColumns["scope_string"]}, nil
|
||||
case telemetrytypes.FieldContextAttribute:
|
||||
switch key.FieldDataType {
|
||||
case telemetrytypes.FieldDataTypeString:
|
||||
return []*schema.Column{auditLogColumns["attributes_string"]}, nil
|
||||
case telemetrytypes.FieldDataTypeInt64, telemetrytypes.FieldDataTypeFloat64, telemetrytypes.FieldDataTypeNumber:
|
||||
return []*schema.Column{auditLogColumns["attributes_number"]}, nil
|
||||
case telemetrytypes.FieldDataTypeBool:
|
||||
return []*schema.Column{auditLogColumns["attributes_bool"]}, nil
|
||||
}
|
||||
case telemetrytypes.FieldContextLog, telemetrytypes.FieldContextUnspecified:
|
||||
col, ok := auditLogColumns[key.Name]
|
||||
if !ok {
|
||||
return nil, qbtypes.ErrColumnNotFound
|
||||
}
|
||||
return []*schema.Column{col}, nil
|
||||
}
|
||||
|
||||
return nil, qbtypes.ErrColumnNotFound
|
||||
}
|
||||
|
||||
func (m *fieldMapper) FieldFor(ctx context.Context, _, _ uint64, key *telemetrytypes.TelemetryFieldKey) (string, error) {
|
||||
columns, err := m.getColumn(ctx, key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(columns) != 1 {
|
||||
return "", errors.Newf(errors.TypeInternal, errors.CodeInternal, "expected exactly 1 column, got %d", len(columns))
|
||||
}
|
||||
column := columns[0]
|
||||
|
||||
switch column.Type.GetType() {
|
||||
case schema.ColumnTypeEnumJSON:
|
||||
if key.FieldContext != telemetrytypes.FieldContextResource {
|
||||
return "", errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "only resource context fields are supported for json columns in audit, got %s", key.FieldContext.String)
|
||||
}
|
||||
return fmt.Sprintf("%s.`%s`::String", column.Name, key.Name), nil
|
||||
case schema.ColumnTypeEnumLowCardinality:
|
||||
return column.Name, nil
|
||||
case schema.ColumnTypeEnumString, schema.ColumnTypeEnumUInt64, schema.ColumnTypeEnumUInt32, schema.ColumnTypeEnumUInt8:
|
||||
return column.Name, nil
|
||||
case schema.ColumnTypeEnumMap:
|
||||
keyType := column.Type.(schema.MapColumnType).KeyType
|
||||
if _, ok := keyType.(schema.LowCardinalityColumnType); !ok {
|
||||
return "", errors.NewInvalidInputf(errors.CodeInvalidInput, "key type %s is not supported for map column type %s", keyType, column.Type)
|
||||
}
|
||||
|
||||
switch valueType := column.Type.(schema.MapColumnType).ValueType; valueType.GetType() {
|
||||
case schema.ColumnTypeEnumString, schema.ColumnTypeEnumBool, schema.ColumnTypeEnumFloat64:
|
||||
if key.Materialized {
|
||||
return telemetrytypes.FieldKeyToMaterializedColumnName(key), nil
|
||||
}
|
||||
return fmt.Sprintf("%s['%s']", column.Name, key.Name), nil
|
||||
default:
|
||||
return "", errors.NewInvalidInputf(errors.CodeInvalidInput, "unsupported map value type %s", valueType)
|
||||
}
|
||||
}
|
||||
|
||||
return column.Name, nil
|
||||
}
|
||||
|
||||
func (m *fieldMapper) ColumnFor(ctx context.Context, _, _ uint64, key *telemetrytypes.TelemetryFieldKey) ([]*schema.Column, error) {
|
||||
return m.getColumn(ctx, key)
|
||||
}
|
||||
|
||||
func (m *fieldMapper) ColumnExpressionFor(
|
||||
ctx context.Context,
|
||||
tsStart, tsEnd uint64,
|
||||
field *telemetrytypes.TelemetryFieldKey,
|
||||
keys map[string][]*telemetrytypes.TelemetryFieldKey,
|
||||
) (string, error) {
|
||||
fieldExpression, err := m.FieldFor(ctx, tsStart, tsEnd, field)
|
||||
if errors.Is(err, qbtypes.ErrColumnNotFound) {
|
||||
keysForField := keys[field.Name]
|
||||
if len(keysForField) == 0 {
|
||||
if _, ok := auditLogColumns[field.Name]; ok {
|
||||
field.FieldContext = telemetrytypes.FieldContextLog
|
||||
fieldExpression, _ = m.FieldFor(ctx, tsStart, tsEnd, field)
|
||||
} else {
|
||||
correction, found := telemetrytypes.SuggestCorrection(field.Name, maps.Keys(keys))
|
||||
if found {
|
||||
return "", errors.Wrap(err, errors.TypeInvalidInput, errors.CodeInvalidInput, correction)
|
||||
}
|
||||
return "", errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "field `%s` not found", field.Name)
|
||||
}
|
||||
} else {
|
||||
fieldExpression, _ = m.FieldFor(ctx, tsStart, tsEnd, keysForField[0])
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s AS `%s`", sqlbuilder.Escape(fieldExpression), field.Name), nil
|
||||
}
|
||||
@@ -1,612 +0,0 @@
|
||||
package telemetryaudit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
"github.com/SigNoz/signoz/pkg/telemetryresourcefilter"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/huandu/go-sqlbuilder"
|
||||
)
|
||||
|
||||
type auditQueryStatementBuilder struct {
|
||||
logger *slog.Logger
|
||||
metadataStore telemetrytypes.MetadataStore
|
||||
fm qbtypes.FieldMapper
|
||||
cb qbtypes.ConditionBuilder
|
||||
resourceFilterStmtBuilder qbtypes.StatementBuilder[qbtypes.LogAggregation]
|
||||
aggExprRewriter qbtypes.AggExprRewriter
|
||||
fullTextColumn *telemetrytypes.TelemetryFieldKey
|
||||
jsonKeyToKey qbtypes.JsonKeyToFieldFunc
|
||||
}
|
||||
|
||||
var _ qbtypes.StatementBuilder[qbtypes.LogAggregation] = (*auditQueryStatementBuilder)(nil)
|
||||
|
||||
func NewAuditQueryStatementBuilder(
|
||||
settings factory.ProviderSettings,
|
||||
metadataStore telemetrytypes.MetadataStore,
|
||||
fieldMapper qbtypes.FieldMapper,
|
||||
conditionBuilder qbtypes.ConditionBuilder,
|
||||
aggExprRewriter qbtypes.AggExprRewriter,
|
||||
fullTextColumn *telemetrytypes.TelemetryFieldKey,
|
||||
jsonKeyToKey qbtypes.JsonKeyToFieldFunc,
|
||||
) *auditQueryStatementBuilder {
|
||||
auditSettings := factory.NewScopedProviderSettings(settings, "github.com/SigNoz/signoz/pkg/telemetryaudit")
|
||||
|
||||
resourceFilterStmtBuilder := telemetryresourcefilter.New[qbtypes.LogAggregation](
|
||||
settings,
|
||||
DBName,
|
||||
LogsResourceTableName,
|
||||
telemetrytypes.SignalLogs,
|
||||
telemetrytypes.SourceAudit,
|
||||
metadataStore,
|
||||
fullTextColumn,
|
||||
jsonKeyToKey,
|
||||
)
|
||||
|
||||
return &auditQueryStatementBuilder{
|
||||
logger: auditSettings.Logger(),
|
||||
metadataStore: metadataStore,
|
||||
fm: fieldMapper,
|
||||
cb: conditionBuilder,
|
||||
resourceFilterStmtBuilder: resourceFilterStmtBuilder,
|
||||
aggExprRewriter: aggExprRewriter,
|
||||
fullTextColumn: fullTextColumn,
|
||||
jsonKeyToKey: jsonKeyToKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *auditQueryStatementBuilder) Build(
|
||||
ctx context.Context,
|
||||
start uint64,
|
||||
end uint64,
|
||||
requestType qbtypes.RequestType,
|
||||
query qbtypes.QueryBuilderQuery[qbtypes.LogAggregation],
|
||||
variables map[string]qbtypes.VariableItem,
|
||||
) (*qbtypes.Statement, error) {
|
||||
start = querybuilder.ToNanoSecs(start)
|
||||
end = querybuilder.ToNanoSecs(end)
|
||||
|
||||
keySelectors := getKeySelectors(query)
|
||||
keys, _, err := b.metadataStore.GetKeysMulti(ctx, keySelectors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query = b.adjustKeys(ctx, keys, query, requestType)
|
||||
|
||||
q := sqlbuilder.NewSelectBuilder()
|
||||
|
||||
var stmt *qbtypes.Statement
|
||||
switch requestType {
|
||||
case qbtypes.RequestTypeRaw, qbtypes.RequestTypeRawStream:
|
||||
stmt, err = b.buildListQuery(ctx, q, query, start, end, keys, variables)
|
||||
case qbtypes.RequestTypeTimeSeries:
|
||||
stmt, err = b.buildTimeSeriesQuery(ctx, q, query, start, end, keys, variables)
|
||||
case qbtypes.RequestTypeScalar:
|
||||
stmt, err = b.buildScalarQuery(ctx, q, query, start, end, keys, false, variables)
|
||||
default:
|
||||
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "unsupported request type: %s", requestType)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
func getKeySelectors(query qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]) []*telemetrytypes.FieldKeySelector {
|
||||
var keySelectors []*telemetrytypes.FieldKeySelector
|
||||
|
||||
for idx := range query.Aggregations {
|
||||
aggExpr := query.Aggregations[idx]
|
||||
selectors := querybuilder.QueryStringToKeysSelectors(aggExpr.Expression)
|
||||
keySelectors = append(keySelectors, selectors...)
|
||||
}
|
||||
|
||||
if query.Filter != nil && query.Filter.Expression != "" {
|
||||
whereClauseSelectors := querybuilder.QueryStringToKeysSelectors(query.Filter.Expression)
|
||||
keySelectors = append(keySelectors, whereClauseSelectors...)
|
||||
}
|
||||
|
||||
for idx := range query.GroupBy {
|
||||
groupBy := query.GroupBy[idx]
|
||||
keySelectors = append(keySelectors, &telemetrytypes.FieldKeySelector{
|
||||
Name: groupBy.Name,
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
FieldContext: groupBy.FieldContext,
|
||||
FieldDataType: groupBy.FieldDataType,
|
||||
})
|
||||
}
|
||||
|
||||
for idx := range query.SelectFields {
|
||||
selectField := query.SelectFields[idx]
|
||||
keySelectors = append(keySelectors, &telemetrytypes.FieldKeySelector{
|
||||
Name: selectField.Name,
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
FieldContext: selectField.FieldContext,
|
||||
FieldDataType: selectField.FieldDataType,
|
||||
})
|
||||
}
|
||||
|
||||
for idx := range query.Order {
|
||||
keySelectors = append(keySelectors, &telemetrytypes.FieldKeySelector{
|
||||
Name: query.Order[idx].Key.Name,
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
FieldContext: query.Order[idx].Key.FieldContext,
|
||||
FieldDataType: query.Order[idx].Key.FieldDataType,
|
||||
})
|
||||
}
|
||||
|
||||
for idx := range keySelectors {
|
||||
keySelectors[idx].Signal = telemetrytypes.SignalLogs
|
||||
keySelectors[idx].Source = telemetrytypes.SourceAudit
|
||||
keySelectors[idx].SelectorMatchType = telemetrytypes.FieldSelectorMatchTypeExact
|
||||
}
|
||||
|
||||
return keySelectors
|
||||
}
|
||||
|
||||
func (b *auditQueryStatementBuilder) adjustKeys(ctx context.Context, keys map[string][]*telemetrytypes.TelemetryFieldKey, query qbtypes.QueryBuilderQuery[qbtypes.LogAggregation], requestType qbtypes.RequestType) qbtypes.QueryBuilderQuery[qbtypes.LogAggregation] {
|
||||
keys["id"] = append([]*telemetrytypes.TelemetryFieldKey{{
|
||||
Name: "id",
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
FieldContext: telemetrytypes.FieldContextLog,
|
||||
FieldDataType: telemetrytypes.FieldDataTypeString,
|
||||
}}, keys["id"]...)
|
||||
|
||||
keys["timestamp"] = append([]*telemetrytypes.TelemetryFieldKey{{
|
||||
Name: "timestamp",
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
FieldContext: telemetrytypes.FieldContextLog,
|
||||
FieldDataType: telemetrytypes.FieldDataTypeNumber,
|
||||
}}, keys["timestamp"]...)
|
||||
|
||||
actions := querybuilder.AdjustKeysForAliasExpressions(&query, requestType)
|
||||
actions = append(actions, querybuilder.AdjustDuplicateKeys(&query)...)
|
||||
|
||||
for idx := range query.SelectFields {
|
||||
actions = append(actions, b.adjustKey(&query.SelectFields[idx], keys)...)
|
||||
}
|
||||
for idx := range query.GroupBy {
|
||||
actions = append(actions, b.adjustKey(&query.GroupBy[idx].TelemetryFieldKey, keys)...)
|
||||
}
|
||||
for idx := range query.Order {
|
||||
actions = append(actions, b.adjustKey(&query.Order[idx].Key.TelemetryFieldKey, keys)...)
|
||||
}
|
||||
|
||||
for _, action := range actions {
|
||||
b.logger.InfoContext(ctx, "key adjustment action", slog.String("action", action))
|
||||
}
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
func (b *auditQueryStatementBuilder) adjustKey(key *telemetrytypes.TelemetryFieldKey, keys map[string][]*telemetrytypes.TelemetryFieldKey) []string {
|
||||
if _, ok := IntrinsicFields[key.Name]; ok {
|
||||
intrinsicField := IntrinsicFields[key.Name]
|
||||
return querybuilder.AdjustKey(key, keys, &intrinsicField)
|
||||
}
|
||||
return querybuilder.AdjustKey(key, keys, nil)
|
||||
}
|
||||
|
||||
func (b *auditQueryStatementBuilder) buildListQuery(
|
||||
ctx context.Context,
|
||||
sb *sqlbuilder.SelectBuilder,
|
||||
query qbtypes.QueryBuilderQuery[qbtypes.LogAggregation],
|
||||
start, end uint64,
|
||||
keys map[string][]*telemetrytypes.TelemetryFieldKey,
|
||||
variables map[string]qbtypes.VariableItem,
|
||||
) (*qbtypes.Statement, error) {
|
||||
var (
|
||||
cteFragments []string
|
||||
cteArgs [][]any
|
||||
)
|
||||
|
||||
if frag, args, err := b.maybeAttachResourceFilter(ctx, sb, query, start, end, variables); err != nil {
|
||||
return nil, err
|
||||
} else if frag != "" {
|
||||
cteFragments = append(cteFragments, frag)
|
||||
cteArgs = append(cteArgs, args)
|
||||
}
|
||||
|
||||
sb.Select(TimestampColumn)
|
||||
sb.SelectMore(IDColumn)
|
||||
if len(query.SelectFields) == 0 {
|
||||
sb.SelectMore(TraceIDColumn)
|
||||
sb.SelectMore(SpanIDColumn)
|
||||
sb.SelectMore(TraceFlagsColumn)
|
||||
sb.SelectMore(SeverityTextColumn)
|
||||
sb.SelectMore(SeverityNumberColumn)
|
||||
sb.SelectMore(ScopeNameColumn)
|
||||
sb.SelectMore(ScopeVersionColumn)
|
||||
sb.SelectMore(BodyColumn)
|
||||
sb.SelectMore(EventNameColumn)
|
||||
sb.SelectMore(AttributesStringColumn)
|
||||
sb.SelectMore(AttributesNumberColumn)
|
||||
sb.SelectMore(AttributesBoolColumn)
|
||||
sb.SelectMore(ResourceColumn)
|
||||
sb.SelectMore(ScopeStringColumn)
|
||||
} else {
|
||||
for index := range query.SelectFields {
|
||||
if query.SelectFields[index].Name == TimestampColumn || query.SelectFields[index].Name == IDColumn {
|
||||
continue
|
||||
}
|
||||
|
||||
colExpr, err := b.fm.ColumnExpressionFor(ctx, start, end, &query.SelectFields[index], keys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sb.SelectMore(colExpr)
|
||||
}
|
||||
}
|
||||
|
||||
sb.From(fmt.Sprintf("%s.%s", DBName, AuditLogsTableName))
|
||||
|
||||
preparedWhereClause, err := b.addFilterCondition(ctx, sb, start, end, query, keys, variables)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, orderBy := range query.Order {
|
||||
colExpr, err := b.fm.ColumnExpressionFor(ctx, start, end, &orderBy.Key.TelemetryFieldKey, keys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sb.OrderBy(fmt.Sprintf("%s %s", colExpr, orderBy.Direction.StringValue()))
|
||||
}
|
||||
|
||||
if query.Limit > 0 {
|
||||
sb.Limit(query.Limit)
|
||||
} else {
|
||||
sb.Limit(100)
|
||||
}
|
||||
|
||||
if query.Offset > 0 {
|
||||
sb.Offset(query.Offset)
|
||||
}
|
||||
|
||||
mainSQL, mainArgs := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
|
||||
finalSQL := querybuilder.CombineCTEs(cteFragments) + mainSQL
|
||||
finalArgs := querybuilder.PrependArgs(cteArgs, mainArgs)
|
||||
|
||||
stmt := &qbtypes.Statement{
|
||||
Query: finalSQL,
|
||||
Args: finalArgs,
|
||||
}
|
||||
if preparedWhereClause != nil {
|
||||
stmt.Warnings = preparedWhereClause.Warnings
|
||||
stmt.WarningsDocURL = preparedWhereClause.WarningsDocURL
|
||||
}
|
||||
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
func (b *auditQueryStatementBuilder) buildTimeSeriesQuery(
|
||||
ctx context.Context,
|
||||
sb *sqlbuilder.SelectBuilder,
|
||||
query qbtypes.QueryBuilderQuery[qbtypes.LogAggregation],
|
||||
start, end uint64,
|
||||
keys map[string][]*telemetrytypes.TelemetryFieldKey,
|
||||
variables map[string]qbtypes.VariableItem,
|
||||
) (*qbtypes.Statement, error) {
|
||||
var (
|
||||
cteFragments []string
|
||||
cteArgs [][]any
|
||||
)
|
||||
|
||||
if frag, args, err := b.maybeAttachResourceFilter(ctx, sb, query, start, end, variables); err != nil {
|
||||
return nil, err
|
||||
} else if frag != "" {
|
||||
cteFragments = append(cteFragments, frag)
|
||||
cteArgs = append(cteArgs, args)
|
||||
}
|
||||
|
||||
sb.SelectMore(fmt.Sprintf(
|
||||
"toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL %d SECOND) AS ts",
|
||||
int64(query.StepInterval.Seconds()),
|
||||
))
|
||||
|
||||
var allGroupByArgs []any
|
||||
|
||||
fieldNames := make([]string, 0, len(query.GroupBy))
|
||||
for _, gb := range query.GroupBy {
|
||||
expr, args, err := querybuilder.CollisionHandledFinalExpr(ctx, start, end, &gb.TelemetryFieldKey, b.fm, b.cb, keys, telemetrytypes.FieldDataTypeString, b.jsonKeyToKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
colExpr := fmt.Sprintf("toString(%s) AS `%s`", expr, gb.Name)
|
||||
allGroupByArgs = append(allGroupByArgs, args...)
|
||||
sb.SelectMore(colExpr)
|
||||
fieldNames = append(fieldNames, fmt.Sprintf("`%s`", gb.Name))
|
||||
}
|
||||
|
||||
allAggChArgs := make([]any, 0)
|
||||
for i, agg := range query.Aggregations {
|
||||
rewritten, chArgs, err := b.aggExprRewriter.Rewrite(ctx, start, end, agg.Expression, uint64(query.StepInterval.Seconds()), keys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allAggChArgs = append(allAggChArgs, chArgs...)
|
||||
sb.SelectMore(fmt.Sprintf("%s AS __result_%d", rewritten, i))
|
||||
}
|
||||
|
||||
sb.From(fmt.Sprintf("%s.%s", DBName, AuditLogsTableName))
|
||||
|
||||
preparedWhereClause, err := b.addFilterCondition(ctx, sb, start, end, query, keys, variables)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var finalSQL string
|
||||
var finalArgs []any
|
||||
|
||||
if query.Limit > 0 && len(query.GroupBy) > 0 {
|
||||
cteSB := sqlbuilder.NewSelectBuilder()
|
||||
cteStmt, err := b.buildScalarQuery(ctx, cteSB, query, start, end, keys, true, variables)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cteFragments = append(cteFragments, fmt.Sprintf("__limit_cte AS (%s)", cteStmt.Query))
|
||||
cteArgs = append(cteArgs, cteStmt.Args)
|
||||
|
||||
tuple := fmt.Sprintf("(%s)", strings.Join(fieldNames, ", "))
|
||||
sb.Where(fmt.Sprintf("%s GLOBAL IN (SELECT %s FROM __limit_cte)", tuple, strings.Join(fieldNames, ", ")))
|
||||
|
||||
sb.GroupBy("ts")
|
||||
sb.GroupBy(querybuilder.GroupByKeys(query.GroupBy)...)
|
||||
if query.Having != nil && query.Having.Expression != "" {
|
||||
rewriter := querybuilder.NewHavingExpressionRewriter()
|
||||
rewrittenExpr, err := rewriter.RewriteForLogs(query.Having.Expression, query.Aggregations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sb.Having(rewrittenExpr)
|
||||
}
|
||||
|
||||
if len(query.Order) != 0 {
|
||||
for _, orderBy := range query.Order {
|
||||
_, ok := aggOrderBy(orderBy, query)
|
||||
if !ok {
|
||||
sb.OrderBy(fmt.Sprintf("`%s` %s", orderBy.Key.Name, orderBy.Direction.StringValue()))
|
||||
}
|
||||
}
|
||||
sb.OrderBy("ts desc")
|
||||
}
|
||||
|
||||
combinedArgs := append(allGroupByArgs, allAggChArgs...)
|
||||
mainSQL, mainArgs := sb.BuildWithFlavor(sqlbuilder.ClickHouse, combinedArgs...)
|
||||
|
||||
finalSQL = querybuilder.CombineCTEs(cteFragments) + mainSQL
|
||||
finalArgs = querybuilder.PrependArgs(cteArgs, mainArgs)
|
||||
} else {
|
||||
sb.GroupBy("ts")
|
||||
sb.GroupBy(querybuilder.GroupByKeys(query.GroupBy)...)
|
||||
if query.Having != nil && query.Having.Expression != "" {
|
||||
rewriter := querybuilder.NewHavingExpressionRewriter()
|
||||
rewrittenExpr, err := rewriter.RewriteForLogs(query.Having.Expression, query.Aggregations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sb.Having(rewrittenExpr)
|
||||
}
|
||||
|
||||
if len(query.Order) != 0 {
|
||||
for _, orderBy := range query.Order {
|
||||
_, ok := aggOrderBy(orderBy, query)
|
||||
if !ok {
|
||||
sb.OrderBy(fmt.Sprintf("`%s` %s", orderBy.Key.Name, orderBy.Direction.StringValue()))
|
||||
}
|
||||
}
|
||||
sb.OrderBy("ts desc")
|
||||
}
|
||||
|
||||
combinedArgs := append(allGroupByArgs, allAggChArgs...)
|
||||
mainSQL, mainArgs := sb.BuildWithFlavor(sqlbuilder.ClickHouse, combinedArgs...)
|
||||
|
||||
finalSQL = querybuilder.CombineCTEs(cteFragments) + mainSQL
|
||||
finalArgs = querybuilder.PrependArgs(cteArgs, mainArgs)
|
||||
}
|
||||
|
||||
stmt := &qbtypes.Statement{
|
||||
Query: finalSQL,
|
||||
Args: finalArgs,
|
||||
}
|
||||
if preparedWhereClause != nil {
|
||||
stmt.Warnings = preparedWhereClause.Warnings
|
||||
stmt.WarningsDocURL = preparedWhereClause.WarningsDocURL
|
||||
}
|
||||
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
func (b *auditQueryStatementBuilder) buildScalarQuery(
|
||||
ctx context.Context,
|
||||
sb *sqlbuilder.SelectBuilder,
|
||||
query qbtypes.QueryBuilderQuery[qbtypes.LogAggregation],
|
||||
start, end uint64,
|
||||
keys map[string][]*telemetrytypes.TelemetryFieldKey,
|
||||
skipResourceCTE bool,
|
||||
variables map[string]qbtypes.VariableItem,
|
||||
) (*qbtypes.Statement, error) {
|
||||
var (
|
||||
cteFragments []string
|
||||
cteArgs [][]any
|
||||
)
|
||||
|
||||
if frag, args, err := b.maybeAttachResourceFilter(ctx, sb, query, start, end, variables); err != nil {
|
||||
return nil, err
|
||||
} else if frag != "" && !skipResourceCTE {
|
||||
cteFragments = append(cteFragments, frag)
|
||||
cteArgs = append(cteArgs, args)
|
||||
}
|
||||
|
||||
allAggChArgs := []any{}
|
||||
|
||||
var allGroupByArgs []any
|
||||
|
||||
for _, gb := range query.GroupBy {
|
||||
expr, args, err := querybuilder.CollisionHandledFinalExpr(ctx, start, end, &gb.TelemetryFieldKey, b.fm, b.cb, keys, telemetrytypes.FieldDataTypeString, b.jsonKeyToKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
colExpr := fmt.Sprintf("toString(%s) AS `%s`", expr, gb.Name)
|
||||
allGroupByArgs = append(allGroupByArgs, args...)
|
||||
sb.SelectMore(colExpr)
|
||||
}
|
||||
|
||||
rateInterval := (end - start) / querybuilder.NsToSeconds
|
||||
|
||||
if len(query.Aggregations) > 0 {
|
||||
for idx := range query.Aggregations {
|
||||
aggExpr := query.Aggregations[idx]
|
||||
rewritten, chArgs, err := b.aggExprRewriter.Rewrite(ctx, start, end, aggExpr.Expression, rateInterval, keys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allAggChArgs = append(allAggChArgs, chArgs...)
|
||||
sb.SelectMore(fmt.Sprintf("%s AS __result_%d", rewritten, idx))
|
||||
}
|
||||
}
|
||||
|
||||
sb.From(fmt.Sprintf("%s.%s", DBName, AuditLogsTableName))
|
||||
|
||||
preparedWhereClause, err := b.addFilterCondition(ctx, sb, start, end, query, keys, variables)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sb.GroupBy(querybuilder.GroupByKeys(query.GroupBy)...)
|
||||
|
||||
if query.Having != nil && query.Having.Expression != "" {
|
||||
rewriter := querybuilder.NewHavingExpressionRewriter()
|
||||
rewrittenExpr, err := rewriter.RewriteForLogs(query.Having.Expression, query.Aggregations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sb.Having(rewrittenExpr)
|
||||
}
|
||||
|
||||
for _, orderBy := range query.Order {
|
||||
idx, ok := aggOrderBy(orderBy, query)
|
||||
if ok {
|
||||
sb.OrderBy(fmt.Sprintf("__result_%d %s", idx, orderBy.Direction.StringValue()))
|
||||
} else {
|
||||
sb.OrderBy(fmt.Sprintf("`%s` %s", orderBy.Key.Name, orderBy.Direction.StringValue()))
|
||||
}
|
||||
}
|
||||
|
||||
if len(query.Order) == 0 {
|
||||
sb.OrderBy("__result_0 DESC")
|
||||
}
|
||||
|
||||
if query.Limit > 0 {
|
||||
sb.Limit(query.Limit)
|
||||
}
|
||||
|
||||
combinedArgs := append(allGroupByArgs, allAggChArgs...)
|
||||
|
||||
mainSQL, mainArgs := sb.BuildWithFlavor(sqlbuilder.ClickHouse, combinedArgs...)
|
||||
|
||||
finalSQL := querybuilder.CombineCTEs(cteFragments) + mainSQL
|
||||
finalArgs := querybuilder.PrependArgs(cteArgs, mainArgs)
|
||||
|
||||
stmt := &qbtypes.Statement{
|
||||
Query: finalSQL,
|
||||
Args: finalArgs,
|
||||
}
|
||||
if preparedWhereClause != nil {
|
||||
stmt.Warnings = preparedWhereClause.Warnings
|
||||
stmt.WarningsDocURL = preparedWhereClause.WarningsDocURL
|
||||
}
|
||||
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
func (b *auditQueryStatementBuilder) addFilterCondition(
|
||||
ctx context.Context,
|
||||
sb *sqlbuilder.SelectBuilder,
|
||||
start, end uint64,
|
||||
query qbtypes.QueryBuilderQuery[qbtypes.LogAggregation],
|
||||
keys map[string][]*telemetrytypes.TelemetryFieldKey,
|
||||
variables map[string]qbtypes.VariableItem,
|
||||
) (*querybuilder.PreparedWhereClause, error) {
|
||||
var preparedWhereClause *querybuilder.PreparedWhereClause
|
||||
var err error
|
||||
|
||||
if query.Filter != nil && query.Filter.Expression != "" {
|
||||
preparedWhereClause, err = querybuilder.PrepareWhereClause(query.Filter.Expression, querybuilder.FilterExprVisitorOpts{
|
||||
Context: ctx,
|
||||
Logger: b.logger,
|
||||
FieldMapper: b.fm,
|
||||
ConditionBuilder: b.cb,
|
||||
FieldKeys: keys,
|
||||
SkipResourceFilter: true,
|
||||
FullTextColumn: b.fullTextColumn,
|
||||
JsonKeyToKey: b.jsonKeyToKey,
|
||||
Variables: variables,
|
||||
StartNs: start,
|
||||
EndNs: end,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if preparedWhereClause != nil {
|
||||
sb.AddWhereClause(preparedWhereClause.WhereClause)
|
||||
}
|
||||
|
||||
startBucket := start/querybuilder.NsToSeconds - querybuilder.BucketAdjustment
|
||||
var endBucket uint64
|
||||
if end != 0 {
|
||||
endBucket = end / querybuilder.NsToSeconds
|
||||
}
|
||||
|
||||
if start != 0 {
|
||||
sb.Where(sb.GE("timestamp", fmt.Sprintf("%d", start)), sb.GE("ts_bucket_start", startBucket))
|
||||
}
|
||||
if end != 0 {
|
||||
sb.Where(sb.L("timestamp", fmt.Sprintf("%d", end)), sb.LE("ts_bucket_start", endBucket))
|
||||
}
|
||||
|
||||
return preparedWhereClause, nil
|
||||
}
|
||||
|
||||
func aggOrderBy(k qbtypes.OrderBy, q qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]) (int, bool) {
|
||||
for i, agg := range q.Aggregations {
|
||||
if k.Key.Name == agg.Alias || k.Key.Name == agg.Expression || k.Key.Name == fmt.Sprintf("%d", i) {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (b *auditQueryStatementBuilder) maybeAttachResourceFilter(
|
||||
ctx context.Context,
|
||||
sb *sqlbuilder.SelectBuilder,
|
||||
query qbtypes.QueryBuilderQuery[qbtypes.LogAggregation],
|
||||
start, end uint64,
|
||||
variables map[string]qbtypes.VariableItem,
|
||||
) (cteSQL string, cteArgs []any, err error) {
|
||||
stmt, err := b.resourceFilterStmtBuilder.Build(ctx, start, end, qbtypes.RequestTypeRaw, query, variables)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
sb.Where("resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter)")
|
||||
|
||||
return fmt.Sprintf("__resource_filter AS (%s)", stmt.Query), stmt.Args, nil
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
package telemetryaudit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes/telemetrytypestest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func auditFieldKeyMap() map[string][]*telemetrytypes.TelemetryFieldKey {
|
||||
key := func(name string, ctx telemetrytypes.FieldContext, dt telemetrytypes.FieldDataType, materialized bool) *telemetrytypes.TelemetryFieldKey {
|
||||
return &telemetrytypes.TelemetryFieldKey{
|
||||
Name: name,
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
FieldContext: ctx,
|
||||
FieldDataType: dt,
|
||||
Materialized: materialized,
|
||||
}
|
||||
}
|
||||
|
||||
attr := telemetrytypes.FieldContextAttribute
|
||||
res := telemetrytypes.FieldContextResource
|
||||
str := telemetrytypes.FieldDataTypeString
|
||||
i64 := telemetrytypes.FieldDataTypeInt64
|
||||
|
||||
return map[string][]*telemetrytypes.TelemetryFieldKey{
|
||||
"service.name": {key("service.name", res, str, false)},
|
||||
"signoz.audit.action": {key("signoz.audit.action", attr, str, true)},
|
||||
"signoz.audit.outcome": {key("signoz.audit.outcome", attr, str, true)},
|
||||
"signoz.audit.principal.email": {key("signoz.audit.principal.email", attr, str, true)},
|
||||
"signoz.audit.principal.id": {key("signoz.audit.principal.id", attr, str, true)},
|
||||
"signoz.audit.principal.type": {key("signoz.audit.principal.type", attr, str, true)},
|
||||
"signoz.audit.resource.kind": {key("signoz.audit.resource.kind", res, str, false)},
|
||||
"signoz.audit.resource.id": {key("signoz.audit.resource.id", res, str, false)},
|
||||
"signoz.audit.action_category": {key("signoz.audit.action_category", attr, str, false)},
|
||||
"signoz.audit.error.type": {key("signoz.audit.error.type", attr, str, false)},
|
||||
"signoz.audit.error.code": {key("signoz.audit.error.code", attr, str, false)},
|
||||
"http.request.method": {key("http.request.method", attr, str, false)},
|
||||
"http.response.status_code": {key("http.response.status_code", attr, i64, false)},
|
||||
}
|
||||
}
|
||||
|
||||
func newTestAuditStatementBuilder() *auditQueryStatementBuilder {
|
||||
mockMetadataStore := telemetrytypestest.NewMockMetadataStore()
|
||||
mockMetadataStore.KeysMap = auditFieldKeyMap()
|
||||
|
||||
fm := NewFieldMapper()
|
||||
cb := NewConditionBuilder(fm)
|
||||
aggExprRewriter := querybuilder.NewAggExprRewriter(instrumentationtest.New().ToProviderSettings(), nil, fm, cb, nil)
|
||||
|
||||
return NewAuditQueryStatementBuilder(
|
||||
instrumentationtest.New().ToProviderSettings(),
|
||||
mockMetadataStore,
|
||||
fm,
|
||||
cb,
|
||||
aggExprRewriter,
|
||||
DefaultFullTextColumn,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func TestStatementBuilder(t *testing.T) {
|
||||
statementBuilder := newTestAuditStatementBuilder()
|
||||
ctx := context.Background()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
requestType qbtypes.RequestType
|
||||
query qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]
|
||||
expected qbtypes.Statement
|
||||
expectedErr error
|
||||
}{
|
||||
// List: all actions by a specific user (materialized principal.id filter)
|
||||
{
|
||||
name: "ListByPrincipalID",
|
||||
requestType: qbtypes.RequestTypeRaw,
|
||||
query: qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Source: telemetrytypes.SourceAudit,
|
||||
Filter: &qbtypes.Filter{
|
||||
Expression: "signoz.audit.principal.id = '019a-1234-abcd-5678'",
|
||||
},
|
||||
Limit: 100,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_audit.distributed_logs_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, event_name, attributes_string, attributes_number, attributes_bool, resource, scope_string FROM signoz_audit.distributed_logs WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND (`attribute_string_signoz$$audit$$principal$$id` = ? AND `attribute_string_signoz$$audit$$principal$$id_exists` = ?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "019a-1234-abcd-5678", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 100},
|
||||
},
|
||||
},
|
||||
// List: all failed actions (materialized outcome filter)
|
||||
{
|
||||
name: "ListByOutcomeFailure",
|
||||
requestType: qbtypes.RequestTypeRaw,
|
||||
query: qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Source: telemetrytypes.SourceAudit,
|
||||
Filter: &qbtypes.Filter{
|
||||
Expression: "signoz.audit.outcome = 'failure'",
|
||||
},
|
||||
Limit: 100,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_audit.distributed_logs_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, event_name, attributes_string, attributes_number, attributes_bool, resource, scope_string FROM signoz_audit.distributed_logs WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND (`attribute_string_signoz$$audit$$outcome` = ? AND `attribute_string_signoz$$audit$$outcome_exists` = ?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "failure", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 100},
|
||||
},
|
||||
},
|
||||
// List: change history of a specific dashboard (two materialized column AND)
|
||||
{
|
||||
name: "ListByResourceKindAndID",
|
||||
requestType: qbtypes.RequestTypeRaw,
|
||||
query: qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Source: telemetrytypes.SourceAudit,
|
||||
Filter: &qbtypes.Filter{
|
||||
Expression: "signoz.audit.resource.kind = 'dashboard' AND signoz.audit.resource.id = '019b-5678-efgh-9012'",
|
||||
},
|
||||
Limit: 100,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_audit.distributed_logs_resource WHERE ((simpleJSONExtractString(labels, 'signoz.audit.resource.kind') = ? AND labels LIKE ? AND labels LIKE ?) AND (simpleJSONExtractString(labels, 'signoz.audit.resource.id') = ? AND labels LIKE ? AND labels LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, event_name, attributes_string, attributes_number, attributes_bool, resource, scope_string FROM signoz_audit.distributed_logs WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"dashboard", "%signoz.audit.resource.kind%", "%signoz.audit.resource.kind\":\"dashboard%", "019b-5678-efgh-9012", "%signoz.audit.resource.id%", "%signoz.audit.resource.id\":\"019b-5678-efgh-9012%", uint64(1747945619), uint64(1747983448), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 100},
|
||||
},
|
||||
},
|
||||
// List: all dashboard deletions (compliance — resource.kind + action AND)
|
||||
{
|
||||
name: "ListByResourceKindAndAction",
|
||||
requestType: qbtypes.RequestTypeRaw,
|
||||
query: qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Source: telemetrytypes.SourceAudit,
|
||||
Filter: &qbtypes.Filter{
|
||||
Expression: "signoz.audit.resource.kind = 'dashboard' AND signoz.audit.action = 'delete'",
|
||||
},
|
||||
Limit: 100,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_audit.distributed_logs_resource WHERE (simpleJSONExtractString(labels, 'signoz.audit.resource.kind') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, event_name, attributes_string, attributes_number, attributes_bool, resource, scope_string FROM signoz_audit.distributed_logs WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND (`attribute_string_signoz$$audit$$action` = ? AND `attribute_string_signoz$$audit$$action_exists` = ?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"dashboard", "%signoz.audit.resource.kind%", "%signoz.audit.resource.kind\":\"dashboard%", uint64(1747945619), uint64(1747983448), "delete", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 100},
|
||||
},
|
||||
},
|
||||
// List: all actions by service accounts (materialized principal.type)
|
||||
{
|
||||
name: "ListByPrincipalType",
|
||||
requestType: qbtypes.RequestTypeRaw,
|
||||
query: qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Source: telemetrytypes.SourceAudit,
|
||||
Filter: &qbtypes.Filter{
|
||||
Expression: "signoz.audit.principal.type = 'service_account'",
|
||||
},
|
||||
Limit: 100,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_audit.distributed_logs_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, event_name, attributes_string, attributes_number, attributes_bool, resource, scope_string FROM signoz_audit.distributed_logs WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND (`attribute_string_signoz$$audit$$principal$$type` = ? AND `attribute_string_signoz$$audit$$principal$$type_exists` = ?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "service_account", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 100},
|
||||
},
|
||||
},
|
||||
// Scalar: alert — count forbidden errors (outcome + action AND)
|
||||
{
|
||||
name: "ScalarCountByOutcomeAndAction",
|
||||
requestType: qbtypes.RequestTypeScalar,
|
||||
query: qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Source: telemetrytypes.SourceAudit,
|
||||
StepInterval: qbtypes.Step{Duration: 60 * time.Second},
|
||||
Filter: &qbtypes.Filter{
|
||||
Expression: "signoz.audit.outcome = 'failure' AND signoz.audit.action = 'update'",
|
||||
},
|
||||
Aggregations: []qbtypes.LogAggregation{
|
||||
{Expression: "count()"},
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_audit.distributed_logs_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT count() AS __result_0 FROM signoz_audit.distributed_logs WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND ((`attribute_string_signoz$$audit$$outcome` = ? AND `attribute_string_signoz$$audit$$outcome_exists` = ?) AND (`attribute_string_signoz$$audit$$action` = ? AND `attribute_string_signoz$$audit$$action_exists` = ?)) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? ORDER BY __result_0 DESC",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "failure", true, "update", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448)},
|
||||
},
|
||||
},
|
||||
// TimeSeries: failures grouped by principal email with top-N limit
|
||||
{
|
||||
name: "TimeSeriesFailuresGroupedByPrincipal",
|
||||
requestType: qbtypes.RequestTypeTimeSeries,
|
||||
query: qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Source: telemetrytypes.SourceAudit,
|
||||
StepInterval: qbtypes.Step{Duration: 60 * time.Second},
|
||||
Aggregations: []qbtypes.LogAggregation{
|
||||
{Expression: "count()"},
|
||||
},
|
||||
Filter: &qbtypes.Filter{
|
||||
Expression: "signoz.audit.outcome = 'failure'",
|
||||
},
|
||||
GroupBy: []qbtypes.GroupByKey{
|
||||
{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "signoz.audit.principal.email"}},
|
||||
},
|
||||
Limit: 5,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_audit.distributed_logs_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(`attribute_string_signoz$$audit$$principal$$email_exists` = ?, `attribute_string_signoz$$audit$$principal$$email`, NULL)) AS `signoz.audit.principal.email`, count() AS __result_0 FROM signoz_audit.distributed_logs WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND (`attribute_string_signoz$$audit$$outcome` = ? AND `attribute_string_signoz$$audit$$outcome_exists` = ?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? GROUP BY `signoz.audit.principal.email` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toString(multiIf(`attribute_string_signoz$$audit$$principal$$email_exists` = ?, `attribute_string_signoz$$audit$$principal$$email`, NULL)) AS `signoz.audit.principal.email`, count() AS __result_0 FROM signoz_audit.distributed_logs WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND (`attribute_string_signoz$$audit$$outcome` = ? AND `attribute_string_signoz$$audit$$outcome_exists` = ?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? AND (`signoz.audit.principal.email`) GLOBAL IN (SELECT `signoz.audit.principal.email` FROM __limit_cte) GROUP BY ts, `signoz.audit.principal.email`",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), true, "failure", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 5, true, "failure", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448)},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
q, err := statementBuilder.Build(ctx, 1747947419000, 1747983448000, testCase.requestType, testCase.query, nil)
|
||||
if testCase.expectedErr != nil {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), testCase.expectedErr.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, testCase.expected.Query, q.Query)
|
||||
require.Equal(t, testCase.expected.Args, q.Args)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package telemetryaudit
|
||||
|
||||
const (
|
||||
DBName = "signoz_audit"
|
||||
AuditLogsTableName = "distributed_logs"
|
||||
AuditLogsLocalTableName = "logs"
|
||||
TagAttributesTableName = "distributed_tag_attributes"
|
||||
TagAttributesLocalTableName = "tag_attributes"
|
||||
LogAttributeKeysTblName = "distributed_logs_attribute_keys"
|
||||
LogResourceKeysTblName = "distributed_logs_resource_keys"
|
||||
LogsResourceTableName = "distributed_logs_resource"
|
||||
)
|
||||
@@ -631,7 +631,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "hasAll(body.user.permissions, ['read', 'write'])",
|
||||
expected: TestExpected{
|
||||
WhereClause: "hasAll(dynamicElement(body_v2.`user.permissions`, 'Array(Nullable(String))'), ?)",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), []any{"read", "write"}, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), []any{[]any{"read", "write"}}, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -757,7 +757,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "hasAny(education[].awards[].participated[].members, ['Piyush', 'Tushar'])",
|
||||
expected: TestExpected{
|
||||
WhereClause: "hasAny(arrayFlatten(arrayConcat(arrayMap(`body_v2.education`->arrayConcat(arrayMap(`body_v2.education[].awards`->arrayConcat(arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(JSON(max_dynamic_types=4, max_dynamic_paths=0))')), arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(Dynamic)'))))), dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')), arrayMap(`body_v2.education[].awards`->arrayConcat(arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=256))')), arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(Dynamic)'))))), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))), ?)",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), []any{"Piyush", "Tushar"}, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), []any{[]any{"Piyush", "Tushar"}}, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -45,7 +45,6 @@ func NewLogQueryStatementBuilder(
|
||||
DBName,
|
||||
LogsResourceV2TableName,
|
||||
telemetrytypes.SignalLogs,
|
||||
telemetrytypes.SourceUnspecified,
|
||||
metadataStore,
|
||||
fullTextColumn,
|
||||
jsonKeyToKey,
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
"github.com/SigNoz/signoz/pkg/telemetryaudit"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrylogs"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrymetrics"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
@@ -28,7 +27,6 @@ import (
|
||||
var (
|
||||
ErrFailedToGetTracesKeys = errors.Newf(errors.TypeInternal, errors.CodeInternal, "failed to get traces keys")
|
||||
ErrFailedToGetLogsKeys = errors.Newf(errors.TypeInternal, errors.CodeInternal, "failed to get logs keys")
|
||||
ErrFailedToGetAuditKeys = errors.Newf(errors.TypeInternal, errors.CodeInternal, "failed to get audit keys")
|
||||
ErrFailedToGetTblStatement = errors.Newf(errors.TypeInternal, errors.CodeInternal, "failed to get tbl statement")
|
||||
ErrFailedToGetMetricsKeys = errors.Newf(errors.TypeInternal, errors.CodeInternal, "failed to get metrics keys")
|
||||
ErrFailedToGetMeterKeys = errors.Newf(errors.TypeInternal, errors.CodeInternal, "failed to get meter keys")
|
||||
@@ -52,11 +50,6 @@ type telemetryMetaStore struct {
|
||||
logAttributeKeysTblName string
|
||||
logResourceKeysTblName string
|
||||
logsV2TblName string
|
||||
auditDBName string
|
||||
auditLogsTblName string
|
||||
auditFieldsTblName string
|
||||
auditAttributeKeysTblName string
|
||||
auditResourceKeysTblName string
|
||||
relatedMetadataDBName string
|
||||
relatedMetadataTblName string
|
||||
columnEvolutionMetadataTblName string
|
||||
@@ -86,11 +79,6 @@ func NewTelemetryMetaStore(
|
||||
logsFieldsTblName string,
|
||||
logAttributeKeysTblName string,
|
||||
logResourceKeysTblName string,
|
||||
auditDBName string,
|
||||
auditLogsTblName string,
|
||||
auditFieldsTblName string,
|
||||
auditAttributeKeysTblName string,
|
||||
auditResourceKeysTblName string,
|
||||
relatedMetadataDBName string,
|
||||
relatedMetadataTblName string,
|
||||
columnEvolutionMetadataTblName string,
|
||||
@@ -113,11 +101,6 @@ func NewTelemetryMetaStore(
|
||||
logsFieldsTblName: logsFieldsTblName,
|
||||
logAttributeKeysTblName: logAttributeKeysTblName,
|
||||
logResourceKeysTblName: logResourceKeysTblName,
|
||||
auditDBName: auditDBName,
|
||||
auditLogsTblName: auditLogsTblName,
|
||||
auditFieldsTblName: auditFieldsTblName,
|
||||
auditAttributeKeysTblName: auditAttributeKeysTblName,
|
||||
auditResourceKeysTblName: auditResourceKeysTblName,
|
||||
relatedMetadataDBName: relatedMetadataDBName,
|
||||
relatedMetadataTblName: relatedMetadataTblName,
|
||||
columnEvolutionMetadataTblName: columnEvolutionMetadataTblName,
|
||||
@@ -609,227 +592,6 @@ func (t *telemetryMetaStore) getLogsKeys(ctx context.Context, fieldKeySelectors
|
||||
return keys, complete, nil
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) auditTblStatementToFieldKeys(ctx context.Context) ([]*telemetrytypes.TelemetryFieldKey, error) {
|
||||
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
|
||||
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
|
||||
instrumentationtypes.CodeNamespace: "metadata",
|
||||
instrumentationtypes.CodeFunctionName: "auditTblStatementToFieldKeys",
|
||||
})
|
||||
|
||||
query := fmt.Sprintf("SHOW CREATE TABLE %s.%s", t.auditDBName, t.auditLogsTblName)
|
||||
statements := []telemetrytypes.ShowCreateTableStatement{}
|
||||
err := t.telemetrystore.ClickhouseDB().Select(ctx, &statements, query)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, errors.TypeInternal, errors.CodeInternal, ErrFailedToGetTblStatement.Error())
|
||||
}
|
||||
|
||||
materialisedKeys, err := ExtractFieldKeysFromTblStatement(statements[0].Statement)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, errors.TypeInternal, errors.CodeInternal, ErrFailedToGetAuditKeys.Error())
|
||||
}
|
||||
|
||||
for idx := range materialisedKeys {
|
||||
materialisedKeys[idx].Signal = telemetrytypes.SignalLogs
|
||||
}
|
||||
|
||||
return materialisedKeys, nil
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) getAuditKeys(ctx context.Context, fieldKeySelectors []*telemetrytypes.FieldKeySelector) ([]*telemetrytypes.TelemetryFieldKey, bool, error) {
|
||||
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
|
||||
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
|
||||
instrumentationtypes.CodeNamespace: "metadata",
|
||||
instrumentationtypes.CodeFunctionName: "getAuditKeys",
|
||||
})
|
||||
|
||||
if len(fieldKeySelectors) == 0 {
|
||||
return nil, true, nil
|
||||
}
|
||||
|
||||
matKeys, err := t.auditTblStatementToFieldKeys(ctx)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
mapOfKeys := make(map[string]*telemetrytypes.TelemetryFieldKey)
|
||||
for _, key := range matKeys {
|
||||
mapOfKeys[key.Name+";"+key.FieldContext.StringValue()+";"+key.FieldDataType.StringValue()] = key
|
||||
}
|
||||
|
||||
var queries []string
|
||||
var allArgs []any
|
||||
|
||||
queryAttributeTable := false
|
||||
queryResourceTable := false
|
||||
|
||||
for _, selector := range fieldKeySelectors {
|
||||
if selector.FieldContext == telemetrytypes.FieldContextUnspecified {
|
||||
queryAttributeTable = true
|
||||
queryResourceTable = true
|
||||
break
|
||||
} else if selector.FieldContext == telemetrytypes.FieldContextAttribute {
|
||||
queryAttributeTable = true
|
||||
} else if selector.FieldContext == telemetrytypes.FieldContextResource {
|
||||
queryResourceTable = true
|
||||
}
|
||||
}
|
||||
|
||||
tablesToQuery := []struct {
|
||||
fieldContext telemetrytypes.FieldContext
|
||||
shouldQuery bool
|
||||
tblName string
|
||||
}{
|
||||
{telemetrytypes.FieldContextAttribute, queryAttributeTable, t.auditDBName + "." + t.auditAttributeKeysTblName},
|
||||
{telemetrytypes.FieldContextResource, queryResourceTable, t.auditDBName + "." + t.auditResourceKeysTblName},
|
||||
}
|
||||
|
||||
for _, table := range tablesToQuery {
|
||||
if !table.shouldQuery {
|
||||
continue
|
||||
}
|
||||
|
||||
fieldContext := table.fieldContext
|
||||
tblName := table.tblName
|
||||
|
||||
sb := sqlbuilder.Select(
|
||||
"name AS tag_key",
|
||||
fmt.Sprintf("'%s' AS tag_type", fieldContext.TagType()),
|
||||
"lower(datatype) AS tag_data_type",
|
||||
fmt.Sprintf("%d AS priority", getPriorityForContext(fieldContext)),
|
||||
).From(tblName)
|
||||
|
||||
var limit int
|
||||
conds := []string{}
|
||||
|
||||
for _, fieldKeySelector := range fieldKeySelectors {
|
||||
if fieldKeySelector.FieldContext != telemetrytypes.FieldContextUnspecified && fieldKeySelector.FieldContext != fieldContext {
|
||||
continue
|
||||
}
|
||||
|
||||
fieldKeyConds := []string{}
|
||||
if fieldKeySelector.SelectorMatchType == telemetrytypes.FieldSelectorMatchTypeExact {
|
||||
fieldKeyConds = append(fieldKeyConds, sb.E("name", fieldKeySelector.Name))
|
||||
} else {
|
||||
fieldKeyConds = append(fieldKeyConds, sb.ILike("name", "%"+escapeForLike(fieldKeySelector.Name)+"%"))
|
||||
}
|
||||
|
||||
if fieldKeySelector.FieldDataType != telemetrytypes.FieldDataTypeUnspecified {
|
||||
fieldKeyConds = append(fieldKeyConds, sb.E("datatype", fieldKeySelector.FieldDataType.TagDataType()))
|
||||
}
|
||||
|
||||
if len(fieldKeyConds) > 0 {
|
||||
conds = append(conds, sb.And(fieldKeyConds...))
|
||||
}
|
||||
limit += fieldKeySelector.Limit
|
||||
}
|
||||
|
||||
if len(conds) > 0 {
|
||||
sb.Where(sb.Or(conds...))
|
||||
}
|
||||
|
||||
sb.GroupBy("name", "datatype")
|
||||
if limit == 0 {
|
||||
limit = 1000
|
||||
}
|
||||
|
||||
query, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
queries = append(queries, query)
|
||||
allArgs = append(allArgs, args...)
|
||||
}
|
||||
|
||||
if len(queries) == 0 {
|
||||
return []*telemetrytypes.TelemetryFieldKey{}, true, nil
|
||||
}
|
||||
|
||||
var limit int
|
||||
for _, fieldKeySelector := range fieldKeySelectors {
|
||||
limit += fieldKeySelector.Limit
|
||||
}
|
||||
if limit == 0 {
|
||||
limit = 1000
|
||||
}
|
||||
|
||||
mainQuery := fmt.Sprintf(`
|
||||
SELECT tag_key, tag_type, tag_data_type, max(priority) as priority
|
||||
FROM (
|
||||
%s
|
||||
) AS combined_results
|
||||
GROUP BY tag_key, tag_type, tag_data_type
|
||||
ORDER BY priority
|
||||
LIMIT %d
|
||||
`, strings.Join(queries, " UNION ALL "), limit+1)
|
||||
|
||||
rows, err := t.telemetrystore.ClickhouseDB().Query(ctx, mainQuery, allArgs...)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, errors.TypeInternal, errors.CodeInternal, ErrFailedToGetAuditKeys.Error())
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
keys := []*telemetrytypes.TelemetryFieldKey{}
|
||||
rowCount := 0
|
||||
searchTexts := []string{}
|
||||
|
||||
for _, fieldKeySelector := range fieldKeySelectors {
|
||||
searchTexts = append(searchTexts, fieldKeySelector.Name)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
rowCount++
|
||||
if rowCount > limit {
|
||||
break
|
||||
}
|
||||
|
||||
var name string
|
||||
var fieldContext telemetrytypes.FieldContext
|
||||
var fieldDataType telemetrytypes.FieldDataType
|
||||
var priority uint8
|
||||
err = rows.Scan(&name, &fieldContext, &fieldDataType, &priority)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, errors.TypeInternal, errors.CodeInternal, ErrFailedToGetAuditKeys.Error())
|
||||
}
|
||||
key, ok := mapOfKeys[name+";"+fieldContext.StringValue()+";"+fieldDataType.StringValue()]
|
||||
|
||||
if !ok {
|
||||
key = &telemetrytypes.TelemetryFieldKey{
|
||||
Name: name,
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
FieldContext: fieldContext,
|
||||
FieldDataType: fieldDataType,
|
||||
}
|
||||
}
|
||||
|
||||
keys = append(keys, key)
|
||||
mapOfKeys[name+";"+fieldContext.StringValue()+";"+fieldDataType.StringValue()] = key
|
||||
}
|
||||
|
||||
if rows.Err() != nil {
|
||||
return nil, false, errors.Wrap(rows.Err(), errors.TypeInternal, errors.CodeInternal, ErrFailedToGetAuditKeys.Error())
|
||||
}
|
||||
|
||||
complete := rowCount <= limit
|
||||
|
||||
// Add intrinsic audit fields (same as logs intrinsics: body, severity_text, etc.)
|
||||
staticKeys := maps.Keys(telemetryaudit.IntrinsicFields)
|
||||
for _, key := range staticKeys {
|
||||
found := false
|
||||
for _, v := range searchTexts {
|
||||
if v == "" || strings.Contains(key, v) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
if field, exists := telemetryaudit.IntrinsicFields[key]; exists {
|
||||
if _, added := mapOfKeys[field.Name+";"+field.FieldContext.StringValue()+";"+field.FieldDataType.StringValue()]; !added {
|
||||
keys = append(keys, &field)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return keys, complete, nil
|
||||
}
|
||||
|
||||
func getPriorityForContext(ctx telemetrytypes.FieldContext) int {
|
||||
switch ctx {
|
||||
case telemetrytypes.FieldContextLog:
|
||||
@@ -1127,11 +889,7 @@ func (t *telemetryMetaStore) GetKeys(ctx context.Context, fieldKeySelector *tele
|
||||
case telemetrytypes.SignalTraces:
|
||||
keys, complete, err = t.getTracesKeys(ctx, selectors)
|
||||
case telemetrytypes.SignalLogs:
|
||||
if fieldKeySelector.Source == telemetrytypes.SourceAudit {
|
||||
keys, complete, err = t.getAuditKeys(ctx, selectors)
|
||||
} else {
|
||||
keys, complete, err = t.getLogsKeys(ctx, selectors)
|
||||
}
|
||||
keys, complete, err = t.getLogsKeys(ctx, selectors)
|
||||
case telemetrytypes.SignalMetrics:
|
||||
if fieldKeySelector.Source == telemetrytypes.SourceMeter {
|
||||
keys, complete, err = t.getMeterSourceMetricKeys(ctx, selectors)
|
||||
@@ -1180,7 +938,6 @@ func (t *telemetryMetaStore) GetKeys(ctx context.Context, fieldKeySelector *tele
|
||||
func (t *telemetryMetaStore) GetKeysMulti(ctx context.Context, fieldKeySelectors []*telemetrytypes.FieldKeySelector) (map[string][]*telemetrytypes.TelemetryFieldKey, bool, error) {
|
||||
|
||||
logsSelectors := []*telemetrytypes.FieldKeySelector{}
|
||||
auditSelectors := []*telemetrytypes.FieldKeySelector{}
|
||||
tracesSelectors := []*telemetrytypes.FieldKeySelector{}
|
||||
metricsSelectors := []*telemetrytypes.FieldKeySelector{}
|
||||
meterSourceMetricsSelectors := []*telemetrytypes.FieldKeySelector{}
|
||||
@@ -1188,11 +945,7 @@ func (t *telemetryMetaStore) GetKeysMulti(ctx context.Context, fieldKeySelectors
|
||||
for _, fieldKeySelector := range fieldKeySelectors {
|
||||
switch fieldKeySelector.Signal {
|
||||
case telemetrytypes.SignalLogs:
|
||||
if fieldKeySelector.Source == telemetrytypes.SourceAudit {
|
||||
auditSelectors = append(auditSelectors, fieldKeySelector)
|
||||
} else {
|
||||
logsSelectors = append(logsSelectors, fieldKeySelector)
|
||||
}
|
||||
logsSelectors = append(logsSelectors, fieldKeySelector)
|
||||
case telemetrytypes.SignalTraces:
|
||||
tracesSelectors = append(tracesSelectors, fieldKeySelector)
|
||||
case telemetrytypes.SignalMetrics:
|
||||
@@ -1212,10 +965,6 @@ func (t *telemetryMetaStore) GetKeysMulti(ctx context.Context, fieldKeySelectors
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
auditKeys, auditComplete, err := t.getAuditKeys(ctx, auditSelectors)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
tracesKeys, tracesComplete, err := t.getTracesKeys(ctx, tracesSelectors)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
@@ -1230,15 +979,12 @@ func (t *telemetryMetaStore) GetKeysMulti(ctx context.Context, fieldKeySelectors
|
||||
return nil, false, err
|
||||
}
|
||||
// Complete only if all queries are complete
|
||||
complete := logsComplete && auditComplete && tracesComplete && metricsComplete
|
||||
complete := logsComplete && tracesComplete && metricsComplete
|
||||
|
||||
mapOfKeys := make(map[string][]*telemetrytypes.TelemetryFieldKey)
|
||||
for _, key := range logsKeys {
|
||||
mapOfKeys[key.Name] = append(mapOfKeys[key.Name], key)
|
||||
}
|
||||
for _, key := range auditKeys {
|
||||
mapOfKeys[key.Name] = append(mapOfKeys[key.Name], key)
|
||||
}
|
||||
for _, key := range tracesKeys {
|
||||
mapOfKeys[key.Name] = append(mapOfKeys[key.Name], key)
|
||||
}
|
||||
@@ -1592,97 +1338,6 @@ func (t *telemetryMetaStore) getLogFieldValues(ctx context.Context, fieldValueSe
|
||||
return values, complete, nil
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) getAuditFieldValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector) (*telemetrytypes.TelemetryFieldValues, bool, error) {
|
||||
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
|
||||
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
|
||||
instrumentationtypes.CodeNamespace: "metadata",
|
||||
instrumentationtypes.CodeFunctionName: "getAuditFieldValues",
|
||||
})
|
||||
|
||||
limit := fieldValueSelector.Limit
|
||||
if limit == 0 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
sb := sqlbuilder.Select("DISTINCT string_value, number_value").From(t.auditDBName + "." + t.auditFieldsTblName)
|
||||
|
||||
if fieldValueSelector.Name != "" {
|
||||
sb.Where(sb.E("tag_key", fieldValueSelector.Name))
|
||||
}
|
||||
|
||||
if fieldValueSelector.FieldContext != telemetrytypes.FieldContextUnspecified {
|
||||
sb.Where(sb.E("tag_type", fieldValueSelector.FieldContext.TagType()))
|
||||
}
|
||||
|
||||
if fieldValueSelector.FieldDataType != telemetrytypes.FieldDataTypeUnspecified {
|
||||
sb.Where(sb.E("tag_data_type", fieldValueSelector.FieldDataType.TagDataType()))
|
||||
}
|
||||
|
||||
if fieldValueSelector.Value != "" {
|
||||
switch fieldValueSelector.FieldDataType {
|
||||
case telemetrytypes.FieldDataTypeString:
|
||||
sb.Where(sb.ILike("string_value", "%"+escapeForLike(fieldValueSelector.Value)+"%"))
|
||||
case telemetrytypes.FieldDataTypeNumber:
|
||||
sb.Where(sb.IsNotNull("number_value"))
|
||||
sb.Where(sb.ILike("toString(number_value)", "%"+escapeForLike(fieldValueSelector.Value)+"%"))
|
||||
case telemetrytypes.FieldDataTypeUnspecified:
|
||||
sb.Where(sb.Or(
|
||||
sb.ILike("string_value", "%"+escapeForLike(fieldValueSelector.Value)+"%"),
|
||||
sb.ILike("toString(number_value)", "%"+escapeForLike(fieldValueSelector.Value)+"%"),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// fetch one extra row to detect whether the result set is complete
|
||||
sb.Limit(limit + 1)
|
||||
|
||||
query, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
|
||||
rows, err := t.telemetrystore.ClickhouseDB().Query(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, errors.TypeInternal, errors.CodeInternal, ErrFailedToGetAuditKeys.Error())
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
values := &telemetrytypes.TelemetryFieldValues{}
|
||||
seen := make(map[string]bool)
|
||||
rowCount := 0
|
||||
totalCount := 0
|
||||
|
||||
for rows.Next() {
|
||||
rowCount++
|
||||
|
||||
var stringValue string
|
||||
var numberValue float64
|
||||
err = rows.Scan(&stringValue, &numberValue)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, errors.TypeInternal, errors.CodeInternal, ErrFailedToGetAuditKeys.Error())
|
||||
}
|
||||
if stringValue != "" && !seen[stringValue] {
|
||||
if totalCount >= limit {
|
||||
break
|
||||
}
|
||||
values.StringValues = append(values.StringValues, stringValue)
|
||||
seen[stringValue] = true
|
||||
totalCount++
|
||||
}
|
||||
if numberValue != 0 {
|
||||
if totalCount >= limit {
|
||||
break
|
||||
}
|
||||
if !seen[fmt.Sprintf("%f", numberValue)] {
|
||||
values.NumberValues = append(values.NumberValues, numberValue)
|
||||
seen[fmt.Sprintf("%f", numberValue)] = true
|
||||
totalCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
complete := rowCount <= limit
|
||||
|
||||
return values, complete, nil
|
||||
}
|
||||
|
||||
// getMetricFieldValues returns field values and whether the result is complete.
|
||||
func (t *telemetryMetaStore) getMetricFieldValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector) (*telemetrytypes.TelemetryFieldValues, bool, error) {
|
||||
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
|
||||
@@ -1973,11 +1628,7 @@ func (t *telemetryMetaStore) GetAllValues(ctx context.Context, fieldValueSelecto
|
||||
case telemetrytypes.SignalTraces:
|
||||
values, complete, err = t.getSpanFieldValues(ctx, fieldValueSelector)
|
||||
case telemetrytypes.SignalLogs:
|
||||
if fieldValueSelector.Source == telemetrytypes.SourceAudit {
|
||||
values, complete, err = t.getAuditFieldValues(ctx, fieldValueSelector)
|
||||
} else {
|
||||
values, complete, err = t.getLogFieldValues(ctx, fieldValueSelector)
|
||||
}
|
||||
values, complete, err = t.getLogFieldValues(ctx, fieldValueSelector)
|
||||
case telemetrytypes.SignalMetrics:
|
||||
if fieldValueSelector.Source == telemetrytypes.SourceMeter {
|
||||
values, complete, err = t.getMeterSourceMetricFieldValues(ctx, fieldValueSelector)
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
|
||||
"github.com/SigNoz/signoz/pkg/telemetryaudit"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrylogs"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrymeter"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrymetrics"
|
||||
@@ -38,11 +37,6 @@ func TestGetFirstSeenFromMetricMetadata(t *testing.T) {
|
||||
telemetrylogs.TagAttributesV2TableName,
|
||||
telemetrylogs.LogAttributeKeysTblName,
|
||||
telemetrylogs.LogResourceKeysTblName,
|
||||
telemetryaudit.DBName,
|
||||
telemetryaudit.AuditLogsTableName,
|
||||
telemetryaudit.TagAttributesTableName,
|
||||
telemetryaudit.LogAttributeKeysTblName,
|
||||
telemetryaudit.LogResourceKeysTblName,
|
||||
DBName,
|
||||
AttributesMetadataLocalTableName,
|
||||
ColumnEvolutionMetadataTableName,
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
|
||||
"github.com/SigNoz/signoz/pkg/telemetryaudit"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrylogs"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrymeter"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrymetrics"
|
||||
@@ -37,11 +36,6 @@ func newTestTelemetryMetaStoreTestHelper(store telemetrystore.TelemetryStore) te
|
||||
telemetrylogs.TagAttributesV2TableName,
|
||||
telemetrylogs.LogAttributeKeysTblName,
|
||||
telemetrylogs.LogResourceKeysTblName,
|
||||
telemetryaudit.DBName,
|
||||
telemetryaudit.AuditLogsTableName,
|
||||
telemetryaudit.TagAttributesTableName,
|
||||
telemetryaudit.LogAttributeKeysTblName,
|
||||
telemetryaudit.LogResourceKeysTblName,
|
||||
DBName,
|
||||
AttributesMetadataLocalTableName,
|
||||
ColumnEvolutionMetadataTableName,
|
||||
|
||||
@@ -9,6 +9,6 @@ const (
|
||||
ColumnEvolutionMetadataTableName = "distributed_column_evolution_metadata"
|
||||
PathTypesTableName = otelcollectorconst.DistributedPathTypesTable
|
||||
// Column Evolution table stores promoted paths as (signal, column_name, field_context, field_name); see signoz-otel-collector metadata_migrations.
|
||||
PromotedPathsTableName = "distributed_column_evolution_metadata"
|
||||
SkipIndexTableName = "system.data_skipping_indices"
|
||||
PromotedPathsTableName = "distributed_column_evolution_metadata"
|
||||
SkipIndexTableName = "system.data_skipping_indices"
|
||||
)
|
||||
|
||||
@@ -21,7 +21,6 @@ type resourceFilterStatementBuilder[T any] struct {
|
||||
conditionBuilder qbtypes.ConditionBuilder
|
||||
metadataStore telemetrytypes.MetadataStore
|
||||
signal telemetrytypes.Signal
|
||||
source telemetrytypes.Source
|
||||
|
||||
fullTextColumn *telemetrytypes.TelemetryFieldKey
|
||||
jsonKeyToKey qbtypes.JsonKeyToFieldFunc
|
||||
@@ -38,7 +37,6 @@ func New[T any](
|
||||
dbName string,
|
||||
tableName string,
|
||||
signal telemetrytypes.Signal,
|
||||
source telemetrytypes.Source,
|
||||
metadataStore telemetrytypes.MetadataStore,
|
||||
fullTextColumn *telemetrytypes.TelemetryFieldKey,
|
||||
jsonKeyToKey qbtypes.JsonKeyToFieldFunc,
|
||||
@@ -54,7 +52,6 @@ func New[T any](
|
||||
conditionBuilder: cb,
|
||||
metadataStore: metadataStore,
|
||||
signal: signal,
|
||||
source: source,
|
||||
fullTextColumn: fullTextColumn,
|
||||
jsonKeyToKey: jsonKeyToKey,
|
||||
}
|
||||
@@ -75,7 +72,6 @@ func (b *resourceFilterStatementBuilder[T]) getKeySelectors(query qbtypes.QueryB
|
||||
continue
|
||||
}
|
||||
keySelectors[idx].Signal = b.signal
|
||||
keySelectors[idx].Source = b.source
|
||||
keySelectors[idx].SelectorMatchType = telemetrytypes.FieldSelectorMatchTypeExact
|
||||
filteredKeySelectors = append(filteredKeySelectors, keySelectors[idx])
|
||||
}
|
||||
|
||||
@@ -375,7 +375,6 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
"signoz_traces",
|
||||
"distributed_traces_v3_resource",
|
||||
telemetrytypes.SignalTraces,
|
||||
telemetrytypes.SourceUnspecified,
|
||||
mockMetadataStore,
|
||||
nil,
|
||||
nil,
|
||||
@@ -593,7 +592,6 @@ func TestResourceFilterStatementBuilder_Logs(t *testing.T) {
|
||||
"signoz_logs",
|
||||
"distributed_logs_v2_resource",
|
||||
telemetrytypes.SignalLogs,
|
||||
telemetrytypes.SourceUnspecified,
|
||||
mockMetadataStore,
|
||||
nil,
|
||||
nil,
|
||||
@@ -655,7 +653,6 @@ func TestResourceFilterStatementBuilder_Variables(t *testing.T) {
|
||||
"signoz_traces",
|
||||
"distributed_traces_v3_resource",
|
||||
telemetrytypes.SignalTraces,
|
||||
telemetrytypes.SourceUnspecified,
|
||||
mockMetadataStore,
|
||||
nil,
|
||||
nil,
|
||||
|
||||
@@ -49,7 +49,6 @@ func NewTraceQueryStatementBuilder(
|
||||
DBName,
|
||||
TracesResourceV3TableName,
|
||||
telemetrytypes.SignalTraces,
|
||||
telemetrytypes.SourceUnspecified,
|
||||
metadataStore,
|
||||
nil,
|
||||
nil,
|
||||
|
||||
@@ -39,7 +39,6 @@ func NewTraceOperatorStatementBuilder(
|
||||
DBName,
|
||||
TracesResourceV3TableName,
|
||||
telemetrytypes.SignalTraces,
|
||||
telemetrytypes.SourceUnspecified,
|
||||
metadataStore,
|
||||
nil,
|
||||
nil,
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package cloudintegrationtypes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
@@ -31,285 +29,16 @@ type AccountConfig struct {
|
||||
AWS *AWSAccountConfig `json:"aws" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AWSAccountConfig struct {
|
||||
Regions []string `json:"regions" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type PostableAccount struct {
|
||||
Config *PostableAccountConfig `json:"config" required:"true"`
|
||||
Credentials *Credentials `json:"credentials" required:"true"`
|
||||
}
|
||||
|
||||
type PostableAccountConfig struct {
|
||||
// as agent version is common for all providers, we can keep it at top level of this struct
|
||||
AgentVersion string
|
||||
Aws *AWSPostableAccountConfig `json:"aws" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type Credentials struct {
|
||||
SigNozAPIURL string `json:"sigNozApiUrl" required:"true"`
|
||||
SigNozAPIKey string `json:"sigNozApiKey" required:"true"` // PAT
|
||||
IngestionURL string `json:"ingestionUrl" required:"true"`
|
||||
IngestionKey string `json:"ingestionKey" required:"true"`
|
||||
}
|
||||
|
||||
type AWSPostableAccountConfig struct {
|
||||
DeploymentRegion string `json:"deploymentRegion" required:"true"`
|
||||
Regions []string `json:"regions" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type GettableAccountWithConnectionArtifact struct {
|
||||
ID valuer.UUID `json:"id" required:"true"`
|
||||
ConnectionArtifact *ConnectionArtifact `json:"connectionArtifact" required:"true"`
|
||||
}
|
||||
|
||||
type ConnectionArtifact struct {
|
||||
// required till new providers are added
|
||||
Aws *AWSConnectionArtifact `json:"aws" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AWSConnectionArtifact struct {
|
||||
ConnectionURL string `json:"connectionUrl" required:"true"`
|
||||
}
|
||||
|
||||
type GetConnectionArtifactRequest = PostableAccount
|
||||
|
||||
type GettableAccounts struct {
|
||||
Accounts []*Account `json:"accounts" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type GettableAccount = Account
|
||||
|
||||
type UpdatableAccount struct {
|
||||
Config *AccountConfig `json:"config" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
func NewAccount(orgID valuer.UUID, provider CloudProviderType, config *AccountConfig) *Account {
|
||||
return &Account{
|
||||
Identifiable: types.Identifiable{
|
||||
ID: valuer.GenerateUUID(),
|
||||
},
|
||||
TimeAuditable: types.TimeAuditable{
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
OrgID: orgID,
|
||||
Provider: provider,
|
||||
Config: config,
|
||||
}
|
||||
}
|
||||
|
||||
func NewAccountFromStorable(storableAccount *StorableCloudIntegration) (*Account, error) {
|
||||
// config can not be empty
|
||||
if storableAccount.Config == "" {
|
||||
return nil, errors.NewInternalf(errors.CodeInternal, "config is empty for account with id: %s", storableAccount.ID)
|
||||
}
|
||||
|
||||
account := &Account{
|
||||
Identifiable: storableAccount.Identifiable,
|
||||
TimeAuditable: storableAccount.TimeAuditable,
|
||||
ProviderAccountID: storableAccount.AccountID,
|
||||
Provider: storableAccount.Provider,
|
||||
RemovedAt: storableAccount.RemovedAt,
|
||||
OrgID: storableAccount.OrgID,
|
||||
Config: new(AccountConfig),
|
||||
}
|
||||
|
||||
switch storableAccount.Provider {
|
||||
case CloudProviderTypeAWS:
|
||||
awsConfig := new(AWSAccountConfig)
|
||||
err := json.Unmarshal([]byte(storableAccount.Config), awsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
account.Config.AWS = awsConfig
|
||||
}
|
||||
|
||||
if storableAccount.LastAgentReport != nil {
|
||||
account.AgentReport = &AgentReport{
|
||||
TimestampMillis: storableAccount.LastAgentReport.TimestampMillis,
|
||||
Data: storableAccount.LastAgentReport.Data,
|
||||
}
|
||||
}
|
||||
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func NewAccountsFromStorables(storableAccounts []*StorableCloudIntegration) ([]*Account, error) {
|
||||
accounts := make([]*Account, 0, len(storableAccounts))
|
||||
for _, storableAccount := range storableAccounts {
|
||||
account, err := NewAccountFromStorable(storableAccount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accounts = append(accounts, account)
|
||||
}
|
||||
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
func (account *Account) Update(config *AccountConfig) error {
|
||||
if account.RemovedAt != nil {
|
||||
return errors.New(errors.TypeUnsupported, ErrCodeCloudIntegrationRemoved, "this operation is not supported for a removed cloud integration account")
|
||||
}
|
||||
account.Config = config
|
||||
account.UpdatedAt = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (account *Account) IsRemoved() bool {
|
||||
return account.RemovedAt != nil
|
||||
}
|
||||
|
||||
func NewAccountConfigFromPostable(provider CloudProviderType, config *PostableAccountConfig) (*AccountConfig, error) {
|
||||
switch provider {
|
||||
case CloudProviderTypeAWS:
|
||||
if config.Aws == nil {
|
||||
return nil, errors.NewInternalf(errors.CodeInternal, "AWS config is nil")
|
||||
}
|
||||
return &AccountConfig{
|
||||
AWS: &AWSAccountConfig{
|
||||
Regions: config.Aws.Regions,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid cloud provider: %s", provider.StringValue())
|
||||
}
|
||||
|
||||
// func NewAccountFromPostableAccount(provider CloudProviderType, account *PostableAccount) (*Account, error) {
|
||||
// req := &Account{
|
||||
// Credentials: account.Credentials,
|
||||
// }
|
||||
|
||||
// switch provider {
|
||||
// case CloudProviderTypeAWS:
|
||||
// req.Config = &ConnectionArtifactRequestConfig{
|
||||
// Aws: &AWSConnectionArtifactRequest{
|
||||
// DeploymentRegion: artifact.Config.Aws.DeploymentRegion,
|
||||
// Regions: artifact.Config.Aws.Regions,
|
||||
// },
|
||||
// }
|
||||
|
||||
// return req, nil
|
||||
// default:
|
||||
// return nil, errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid cloud provider: %s", provider.StringValue())
|
||||
// }
|
||||
// }
|
||||
|
||||
func NewAgentReport(data map[string]any) *AgentReport {
|
||||
return &AgentReport{
|
||||
TimestampMillis: time.Now().UnixMilli(),
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// ToJSON return JSON bytes for the provider's config
|
||||
// thats why not naming it MarshalJSON(), as it will interfere with default JSON marshalling of AccountConfig struct.
|
||||
// NOTE: this entertains first non-null provider's config.
|
||||
func (config *AccountConfig) ToJSON() ([]byte, error) {
|
||||
if config.AWS != nil {
|
||||
return json.Marshal(config.AWS)
|
||||
}
|
||||
|
||||
return nil, errors.NewInternalf(errors.CodeInternal, "no provider account config found")
|
||||
}
|
||||
|
||||
func (config *PostableAccountConfig) AddAgentVersion(agentVersion string) {
|
||||
config.AgentVersion = agentVersion
|
||||
}
|
||||
|
||||
// Validate checks that the connection artifact request has a valid provider-specific block
|
||||
// with non-empty, valid regions and a valid deployment region.
|
||||
func (account *PostableAccount) Validate(provider CloudProviderType) error {
|
||||
if account.Config == nil || account.Credentials == nil {
|
||||
return errors.New(errors.TypeInvalidInput, ErrCodeInvalidInput,
|
||||
"config and credentials are required")
|
||||
}
|
||||
|
||||
if account.Credentials.SigNozAPIURL == "" {
|
||||
return errors.New(errors.TypeInvalidInput, ErrCodeInvalidInput,
|
||||
"sigNozApiURL can not be empty")
|
||||
}
|
||||
|
||||
if account.Credentials.SigNozAPIKey == "" {
|
||||
return errors.New(errors.TypeInvalidInput, ErrCodeInvalidInput,
|
||||
"sigNozApiKey can not be empty")
|
||||
}
|
||||
|
||||
if account.Credentials.IngestionURL == "" {
|
||||
return errors.New(errors.TypeInvalidInput, ErrCodeInvalidInput,
|
||||
"ingestionUrl can not be empty")
|
||||
}
|
||||
|
||||
if account.Credentials.IngestionKey == "" {
|
||||
return errors.New(errors.TypeInvalidInput, ErrCodeInvalidInput,
|
||||
"ingestionKey can not be empty")
|
||||
}
|
||||
|
||||
switch provider {
|
||||
case CloudProviderTypeAWS:
|
||||
if account.Config.Aws == nil {
|
||||
return errors.New(errors.TypeInvalidInput, ErrCodeInvalidInput,
|
||||
"aws configuration is required")
|
||||
}
|
||||
return account.Config.Aws.Validate()
|
||||
}
|
||||
|
||||
return errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid cloud provider: %s", provider)
|
||||
}
|
||||
|
||||
// Validate checks that the AWS connection artifact request has a valid deployment region
|
||||
// and a non-empty list of valid regions.
|
||||
func (req *AWSPostableAccountConfig) Validate() error {
|
||||
if req.DeploymentRegion == "" {
|
||||
return errors.New(errors.TypeInvalidInput, ErrCodeInvalidInput,
|
||||
"deploymentRegion is required")
|
||||
}
|
||||
if _, ok := ValidAWSRegions[req.DeploymentRegion]; !ok {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodeInvalidCloudRegion,
|
||||
"invalid deployment region: %s", req.DeploymentRegion)
|
||||
}
|
||||
|
||||
if len(req.Regions) == 0 {
|
||||
return errors.New(errors.TypeInvalidInput, ErrCodeInvalidInput,
|
||||
"at least one region is required")
|
||||
}
|
||||
for _, region := range req.Regions {
|
||||
if _, ok := ValidAWSRegions[region]; !ok {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodeInvalidCloudRegion,
|
||||
"invalid AWS region: %s", region)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (updatable *UpdatableAccount) Validate(provider CloudProviderType) error {
|
||||
if updatable.Config == nil {
|
||||
return errors.New(errors.TypeInvalidInput, ErrCodeInvalidInput,
|
||||
"config is required")
|
||||
}
|
||||
|
||||
switch provider {
|
||||
case CloudProviderTypeAWS:
|
||||
if updatable.Config.AWS == nil {
|
||||
return errors.New(errors.TypeInvalidInput, ErrCodeInvalidInput,
|
||||
"aws configuration is required")
|
||||
}
|
||||
|
||||
if len(updatable.Config.AWS.Regions) == 0 {
|
||||
return errors.New(errors.TypeInvalidInput, ErrCodeInvalidInput,
|
||||
"at least one region is required")
|
||||
}
|
||||
|
||||
for _, region := range updatable.Config.AWS.Regions {
|
||||
if _, ok := ValidAWSRegions[region]; !ok {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodeInvalidCloudRegion,
|
||||
"invalid AWS region: %s", region)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput,
|
||||
"invalid cloud provider: %s", provider.StringValue())
|
||||
}
|
||||
|
||||
return nil
|
||||
type AWSAccountConfig struct {
|
||||
Regions []string `json:"regions" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
package cloudintegrationtypes
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
type AgentCheckInRequest struct {
|
||||
ProviderAccountID string `json:"providerAccountId" required:"false"`
|
||||
CloudIntegrationID valuer.UUID `json:"cloudIntegrationId" required:"false"`
|
||||
|
||||
Data map[string]any `json:"data" required:"true" nullable:"true"`
|
||||
}
|
||||
|
||||
type PostableAgentCheckIn struct {
|
||||
AgentCheckInRequest
|
||||
// following are backward compatible fields for older running agents, hence snake case JSON keys.
|
||||
// Which get mapped to new fields in AgentCheckInRequest
|
||||
ID string `json:"account_id" required:"false"` // => CloudIntegrationID
|
||||
AccountID string `json:"cloud_account_id" required:"false"` // => ProviderAccountID
|
||||
}
|
||||
|
||||
type AgentCheckInResponse struct {
|
||||
CloudIntegrationID string `json:"cloudIntegrationId" required:"true"`
|
||||
ProviderAccountID string `json:"providerAccountId" required:"true"`
|
||||
IntegrationConfig *ProviderIntegrationConfig `json:"integrationConfig" required:"true"`
|
||||
RemovedAt *time.Time `json:"removedAt" required:"true" nullable:"true"`
|
||||
}
|
||||
|
||||
type GettableAgentCheckIn struct {
|
||||
// Older fields for backward compatibility with existing AWS agents
|
||||
AccountID string `json:"account_id" required:"true"`
|
||||
CloudAccountID string `json:"cloud_account_id" required:"true"`
|
||||
OlderIntegrationConfig *IntegrationConfig `json:"integration_config" required:"true" nullable:"true"`
|
||||
OlderRemovedAt *time.Time `json:"removed_at" required:"true" nullable:"true"`
|
||||
|
||||
AgentCheckInResponse
|
||||
}
|
||||
|
||||
// IntegrationConfig older integration config struct for backward compatibility,
|
||||
// this will be eventually removed once agents are updated to use new struct.
|
||||
type IntegrationConfig struct {
|
||||
EnabledRegions []string `json:"enabled_regions" required:"true" nullable:"false"` // backward compatible
|
||||
Telemetry *OldAWSCollectionStrategy `json:"telemetry" required:"true" nullable:"false"` // backward compatible
|
||||
}
|
||||
|
||||
type ProviderIntegrationConfig struct {
|
||||
AWS *AWSIntegrationConfig `json:"aws" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AWSIntegrationConfig struct {
|
||||
EnabledRegions []string `json:"enabledRegions" required:"true" nullable:"false"`
|
||||
TelemetryCollectionStrategy *AWSTelemetryCollectionStrategy `json:"telemetryCollectionStrategy" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
// NewGettableAgentCheckIn constructs a backward-compatible response from an AgentCheckInResponse.
|
||||
// It populates the old snake_case fields (account_id, cloud_account_id, integration_config, removed_at)
|
||||
// from the new camelCase fields so older agents continue to work unchanged.
|
||||
// The provider parameter controls which provider-specific block is mapped into the legacy integration_config.
|
||||
func NewGettableAgentCheckIn(provider CloudProviderType, resp *AgentCheckInResponse) *GettableAgentCheckIn {
|
||||
gettable := &GettableAgentCheckIn{
|
||||
AccountID: resp.CloudIntegrationID,
|
||||
CloudAccountID: resp.ProviderAccountID,
|
||||
OlderRemovedAt: resp.RemovedAt,
|
||||
AgentCheckInResponse: *resp,
|
||||
}
|
||||
|
||||
switch provider {
|
||||
case CloudProviderTypeAWS:
|
||||
gettable.OlderIntegrationConfig = awsOlderIntegrationConfig(resp.IntegrationConfig)
|
||||
}
|
||||
|
||||
return gettable
|
||||
}
|
||||
|
||||
// Validate checks that the request uses either old fields (account_id, cloud_account_id) or
|
||||
// new fields (cloudIntegrationId, providerAccountId), never a mix of both.
|
||||
func (req *PostableAgentCheckIn) Validate() error {
|
||||
hasOldFields := req.ID != "" || req.AccountID != ""
|
||||
hasNewFields := !req.CloudIntegrationID.IsZero() || req.ProviderAccountID != ""
|
||||
|
||||
if hasOldFields && hasNewFields {
|
||||
return errors.New(errors.TypeInvalidInput, ErrCodeInvalidInput,
|
||||
"request must use either old fields (account_id, cloud_account_id) or new fields (cloudIntegrationId, providerAccountId), not both")
|
||||
}
|
||||
if !hasOldFields && !hasNewFields {
|
||||
return errors.New(errors.TypeInvalidInput, ErrCodeInvalidInput,
|
||||
"request must provide either old fields (account_id, cloud_account_id) or new fields (cloudIntegrationId, providerAccountId)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -13,16 +13,10 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrCodeUnsupported = errors.MustNewCode("cloud_integration_unsupported")
|
||||
ErrCodeInvalidInput = errors.MustNewCode("cloud_integration_invalid_input")
|
||||
ErrCodeCloudIntegrationNotFound = errors.MustNewCode("cloud_integration_not_found")
|
||||
ErrCodeCloudIntegrationAlreadyExists = errors.MustNewCode("cloud_integration_already_exists")
|
||||
ErrCodeCloudIntegrationAlreadyConnected = errors.MustNewCode("cloud_integration_already_connected")
|
||||
ErrCodeCloudIntegrationInvalidConfig = errors.MustNewCode("cloud_integration_invalid_config")
|
||||
ErrCodeCloudIntegrationRemoved = errors.MustNewCode("cloud_integration_removed")
|
||||
ErrCodeCloudIntegrationServiceNotFound = errors.MustNewCode("cloud_integration_service_not_found")
|
||||
ErrCodeCloudIntegrationServiceAlreadyExists = errors.MustNewCode("cloud_integration_service_already_exists")
|
||||
ErrCodeServiceDefinitionNotFound = errors.MustNewCode("service_definition_not_found")
|
||||
)
|
||||
|
||||
// StorableCloudIntegration represents a cloud integration stored in the database.
|
||||
@@ -58,26 +52,6 @@ type StorableCloudIntegrationService struct {
|
||||
CloudIntegrationID valuer.UUID `bun:"cloud_integration_id,type:text"`
|
||||
}
|
||||
|
||||
// Following Service config types are only internally used to store service config in DB and use JSON snake case keys for backward compatibility.
|
||||
|
||||
type StorableServiceConfig struct {
|
||||
AWS *StorableAWSServiceConfig
|
||||
}
|
||||
|
||||
type StorableAWSServiceConfig struct {
|
||||
Logs *StorableAWSLogsServiceConfig `json:"logs,omitempty"`
|
||||
Metrics *StorableAWSMetricsServiceConfig `json:"metrics,omitempty"`
|
||||
}
|
||||
|
||||
type StorableAWSLogsServiceConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
S3Buckets map[string][]string `json:"s3_buckets,omitempty"` // region -> list of buckets in that region
|
||||
}
|
||||
|
||||
type StorableAWSMetricsServiceConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// Scan scans value from DB.
|
||||
func (r *StorableAgentReport) Scan(src any) error {
|
||||
var data []byte
|
||||
@@ -94,6 +68,10 @@ func (r *StorableAgentReport) Scan(src any) error {
|
||||
|
||||
// Value creates value to be stored in DB.
|
||||
func (r *StorableAgentReport) Value() (driver.Value, error) {
|
||||
if r == nil {
|
||||
return nil, errors.NewInternalf(errors.CodeInternal, "agent report is nil")
|
||||
}
|
||||
|
||||
serialized, err := json.Marshal(r)
|
||||
if err != nil {
|
||||
return nil, errors.WrapInternalf(
|
||||
@@ -103,107 +81,3 @@ func (r *StorableAgentReport) Value() (driver.Value, error) {
|
||||
// Return as string instead of []byte to ensure PostgreSQL stores as text, not bytes
|
||||
return string(serialized), nil
|
||||
}
|
||||
|
||||
func NewStorableCloudIntegration(account *Account) (*StorableCloudIntegration, error) {
|
||||
configBytes, err := account.Config.ToJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
storableAccount := &StorableCloudIntegration{
|
||||
Identifiable: account.Identifiable,
|
||||
TimeAuditable: account.TimeAuditable,
|
||||
Provider: account.Provider,
|
||||
Config: string(configBytes),
|
||||
AccountID: account.ProviderAccountID,
|
||||
OrgID: account.OrgID,
|
||||
RemovedAt: account.RemovedAt,
|
||||
}
|
||||
|
||||
if account.AgentReport != nil {
|
||||
storableAccount.LastAgentReport = &StorableAgentReport{
|
||||
TimestampMillis: account.AgentReport.TimestampMillis,
|
||||
Data: account.AgentReport.Data,
|
||||
}
|
||||
}
|
||||
|
||||
return storableAccount, nil
|
||||
}
|
||||
|
||||
// NewStorableCloudIntegrationService creates a new StorableCloudIntegrationService with
|
||||
// generated ID and timestamps from a CloudIntegrationService and its serialized config JSON.
|
||||
func NewStorableCloudIntegrationService(svc *CloudIntegrationService, configJSON string) *StorableCloudIntegrationService {
|
||||
return &StorableCloudIntegrationService{
|
||||
Identifiable: svc.Identifiable,
|
||||
TimeAuditable: svc.TimeAuditable,
|
||||
Type: svc.Type,
|
||||
Config: configJSON,
|
||||
CloudIntegrationID: svc.CloudIntegrationID,
|
||||
}
|
||||
}
|
||||
|
||||
func (account *StorableCloudIntegration) Update(providerAccountID *string, agentReport *AgentReport) {
|
||||
account.AccountID = providerAccountID
|
||||
if agentReport != nil {
|
||||
account.LastAgentReport = &StorableAgentReport{
|
||||
TimestampMillis: agentReport.TimestampMillis,
|
||||
Data: agentReport.Data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// following StorableServiceConfig related functions are helper functions to convert between JSON string and ServiceConfig domain struct.
|
||||
func newStorableServiceConfig(provider CloudProviderType, serviceID ServiceID, serviceConfig *ServiceConfig, supportedSignals *SupportedSignals) *StorableServiceConfig {
|
||||
switch provider {
|
||||
case CloudProviderTypeAWS:
|
||||
storableAWSServiceConfig := new(StorableAWSServiceConfig)
|
||||
|
||||
if supportedSignals.Logs {
|
||||
storableAWSServiceConfig.Logs = &StorableAWSLogsServiceConfig{
|
||||
Enabled: serviceConfig.AWS.Logs.Enabled,
|
||||
}
|
||||
|
||||
if serviceID == AWSServiceS3Sync {
|
||||
storableAWSServiceConfig.Logs.S3Buckets = serviceConfig.AWS.Logs.S3Buckets
|
||||
}
|
||||
}
|
||||
|
||||
if supportedSignals.Metrics {
|
||||
storableAWSServiceConfig.Metrics = &StorableAWSMetricsServiceConfig{
|
||||
Enabled: serviceConfig.AWS.Metrics.Enabled,
|
||||
}
|
||||
}
|
||||
|
||||
return &StorableServiceConfig{AWS: storableAWSServiceConfig}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func newStorableServiceConfigFromJSON(provider CloudProviderType, jsonStr string) (*StorableServiceConfig, error) {
|
||||
switch provider {
|
||||
case CloudProviderTypeAWS:
|
||||
awsConfig := new(StorableAWSServiceConfig)
|
||||
err := json.Unmarshal([]byte(jsonStr), awsConfig)
|
||||
if err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't parse AWS service config JSON")
|
||||
}
|
||||
return &StorableServiceConfig{AWS: awsConfig}, nil
|
||||
default:
|
||||
return nil, errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid cloud provider: %s", provider.StringValue())
|
||||
}
|
||||
}
|
||||
|
||||
func (config *StorableServiceConfig) toJSON(provider CloudProviderType) ([]byte, error) {
|
||||
switch provider {
|
||||
case CloudProviderTypeAWS:
|
||||
jsonBytes, err := json.Marshal(config.AWS)
|
||||
if err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't serialize AWS service config to JSON")
|
||||
}
|
||||
|
||||
return jsonBytes, nil
|
||||
default:
|
||||
return nil, errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid cloud provider: %s", provider.StringValue())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package cloudintegrationtypes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
@@ -16,13 +14,19 @@ var (
|
||||
CloudProviderTypeAzure = CloudProviderType{valuer.NewString("azure")}
|
||||
|
||||
// errors.
|
||||
ErrCodeCloudProviderInvalidInput = errors.MustNewCode("cloud_integration_invalid_cloud_provider")
|
||||
ErrCodeCloudProviderInvalidInput = errors.MustNewCode("invalid_cloud_provider")
|
||||
|
||||
CloudFormationQuickCreateBaseURL = valuer.NewString("https://%s.console.aws.amazon.com/cloudformation/home")
|
||||
AgentCloudFormationTemplateS3Path = valuer.NewString("https://signoz-integrations.s3.us-east-1.amazonaws.com/aws-quickcreate-template-%s.json")
|
||||
AgentCloudFormationBaseStackName = valuer.NewString("signoz-integration")
|
||||
AWSIntegrationUserEmail = valuer.MustNewEmail("aws-integration@signoz.io")
|
||||
AzureIntegrationUserEmail = valuer.MustNewEmail("azure-integration@signoz.io")
|
||||
)
|
||||
|
||||
// CloudIntegrationUserEmails is the list of valid emails for Cloud One Click integrations.
|
||||
// This is used for validation and restrictions in different contexts, across codebase.
|
||||
var CloudIntegrationUserEmails = []valuer.Email{
|
||||
AWSIntegrationUserEmail,
|
||||
AzureIntegrationUserEmail,
|
||||
}
|
||||
|
||||
// NewCloudProvider returns a new CloudProviderType from a string.
|
||||
// It validates the input and returns an error if the input is not valid cloud provider.
|
||||
func NewCloudProvider(provider string) (CloudProviderType, error) {
|
||||
@@ -35,7 +39,3 @@ func NewCloudProvider(provider string) (CloudProviderType, error) {
|
||||
return CloudProviderType{}, errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid cloud provider: %s", provider)
|
||||
}
|
||||
}
|
||||
|
||||
func NewIngestionKeyName(provider CloudProviderType) string {
|
||||
return fmt.Sprintf("%s-integration", provider.StringValue())
|
||||
}
|
||||
|
||||
81
pkg/types/cloudintegrationtypes/connection.go
Normal file
81
pkg/types/cloudintegrationtypes/connection.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package cloudintegrationtypes
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
type ConnectionArtifactRequest struct {
|
||||
// required till new providers are added
|
||||
Aws *AWSConnectionArtifactRequest `json:"aws" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AWSConnectionArtifactRequest struct {
|
||||
DeploymentRegion string `json:"deploymentRegion" required:"true"`
|
||||
Regions []string `json:"regions" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type PostableConnectionArtifact = ConnectionArtifactRequest
|
||||
|
||||
type ConnectionArtifact struct {
|
||||
// required till new providers are added
|
||||
Aws *AWSConnectionArtifact `json:"aws" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AWSConnectionArtifact struct {
|
||||
ConnectionURL string `json:"connectionURL" required:"true"`
|
||||
}
|
||||
|
||||
type GettableAccountWithArtifact struct {
|
||||
ID valuer.UUID `json:"id" required:"true"`
|
||||
Artifact *ConnectionArtifact `json:"connectionArtifact" required:"true"`
|
||||
}
|
||||
|
||||
type AgentCheckInRequest struct {
|
||||
ProviderAccountID string `json:"providerAccountId" required:"false"`
|
||||
CloudIntegrationID string `json:"cloudIntegrationId" required:"false"`
|
||||
|
||||
Data map[string]any `json:"data" required:"true" nullable:"true"`
|
||||
}
|
||||
|
||||
type PostableAgentCheckInRequest struct {
|
||||
AgentCheckInRequest
|
||||
// following are backward compatible fields for older running agents
|
||||
// which gets mapped to new fields in AgentCheckInRequest
|
||||
ID string `json:"account_id" required:"false"` // => CloudIntegrationID
|
||||
AccountID string `json:"cloud_account_id" required:"false"` // => ProviderAccountID
|
||||
}
|
||||
|
||||
type AgentCheckInResponse struct {
|
||||
CloudIntegrationID string `json:"cloudIntegrationId" required:"true"`
|
||||
ProviderAccountID string `json:"providerAccountId" required:"true"`
|
||||
IntegrationConfig *ProviderIntegrationConfig `json:"integrationConfig" required:"true"`
|
||||
RemovedAt *time.Time `json:"removedAt" required:"true" nullable:"true"`
|
||||
}
|
||||
|
||||
type GettableAgentCheckInResponse struct {
|
||||
// Older fields for backward compatibility with existing AWS agents
|
||||
AccountID string `json:"account_id" required:"true"`
|
||||
CloudAccountID string `json:"cloud_account_id" required:"true"`
|
||||
OlderIntegrationConfig *IntegrationConfig `json:"integration_config" required:"true" nullable:"true"`
|
||||
OlderRemovedAt *time.Time `json:"removed_at" required:"true" nullable:"true"`
|
||||
|
||||
AgentCheckInResponse
|
||||
}
|
||||
|
||||
// IntegrationConfig older integration config struct for backward compatibility,
|
||||
// this will be eventually removed once agents are updated to use new struct.
|
||||
type IntegrationConfig struct {
|
||||
EnabledRegions []string `json:"enabled_regions" required:"true" nullable:"false"` // backward compatible
|
||||
Telemetry *AWSCollectionStrategy `json:"telemetry" required:"true" nullable:"false"` // backward compatible
|
||||
}
|
||||
|
||||
type ProviderIntegrationConfig struct {
|
||||
AWS *AWSIntegrationConfig `json:"aws" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AWSIntegrationConfig struct {
|
||||
EnabledRegions []string `json:"enabledRegions" required:"true" nullable:"false"`
|
||||
Telemetry *AWSCollectionStrategy `json:"telemetry" required:"true" nullable:"false"`
|
||||
}
|
||||
@@ -4,7 +4,10 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
)
|
||||
|
||||
var ErrCodeInvalidCloudRegion = errors.MustNewCode("invalid_cloud_region")
|
||||
var (
|
||||
ErrCodeInvalidCloudRegion = errors.MustNewCode("invalid_cloud_region")
|
||||
ErrCodeMismatchCloudProvider = errors.MustNewCode("cloud_provider_mismatch")
|
||||
)
|
||||
|
||||
// List of all valid cloud regions on Amazon Web Services.
|
||||
var ValidAWSRegions = map[string]struct{}{
|
||||
|
||||
@@ -2,7 +2,6 @@ package cloudintegrationtypes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
@@ -26,22 +25,6 @@ type ServiceConfig struct {
|
||||
AWS *AWSServiceConfig `json:"aws" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AWSServiceConfig struct {
|
||||
Logs *AWSServiceLogsConfig `json:"logs"`
|
||||
Metrics *AWSServiceMetricsConfig `json:"metrics"`
|
||||
}
|
||||
|
||||
// AWSServiceLogsConfig is AWS specific logs config for a service
|
||||
// NOTE: the JSON keys are snake case for backward compatibility with existing agents.
|
||||
type AWSServiceLogsConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
S3Buckets map[string][]string `json:"s3Buckets,omitempty"`
|
||||
}
|
||||
|
||||
type AWSServiceMetricsConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// ServiceMetadata helps to quickly list available services and whether it is enabled or not.
|
||||
// As getting complete service definition is a heavy operation and the response is also large,
|
||||
// initial integration page load can be very slow.
|
||||
@@ -62,24 +45,24 @@ type GettableServicesMetadata struct {
|
||||
Services []*ServiceMetadata `json:"services" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
// Service represents a cloud integration service with its definition,
|
||||
// cloud integration service is non nil only when the service entry exists in DB with ANY config (enabled or disabled).
|
||||
type Service struct {
|
||||
ServiceDefinition
|
||||
CloudIntegrationService *CloudIntegrationService `json:"cloudIntegrationService" required:"true" nullable:"true"`
|
||||
ServiceConfig *ServiceConfig `json:"serviceConfig" required:"false" nullable:"false"`
|
||||
}
|
||||
|
||||
type GettableService = Service
|
||||
|
||||
type UpdatableService struct {
|
||||
Config *ServiceConfig `json:"config" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type ServiceDefinition struct {
|
||||
ServiceDefinitionMetadata
|
||||
Overview string `json:"overview" required:"true"` // markdown
|
||||
Assets Assets `json:"assets" required:"true"`
|
||||
SupportedSignals SupportedSignals `json:"supportedSignals" required:"true"`
|
||||
DataCollected DataCollected `json:"dataCollected" required:"true"`
|
||||
TelemetryCollectionStrategy *TelemetryCollectionStrategy `json:"telemetryCollectionStrategy" required:"true" nullable:"false"`
|
||||
Overview string `json:"overview" required:"true"` // markdown
|
||||
Assets Assets `json:"assets" required:"true"`
|
||||
SupportedSignals SupportedSignals `json:"supported_signals" required:"true"`
|
||||
DataCollected DataCollected `json:"dataCollected" required:"true"`
|
||||
Strategy *CollectionStrategy `json:"telemetryCollectionStrategy" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
// SupportedSignals for cloud provider's service.
|
||||
@@ -94,10 +77,26 @@ type DataCollected struct {
|
||||
Metrics []CollectedMetric `json:"metrics"`
|
||||
}
|
||||
|
||||
// TelemetryCollectionStrategy is cloud provider specific configuration for signal collection,
|
||||
// CollectionStrategy is cloud provider specific configuration for signal collection,
|
||||
// this is used by agent to understand the nitty-gritty for collecting telemetry for the cloud provider.
|
||||
type TelemetryCollectionStrategy struct {
|
||||
AWS *AWSTelemetryCollectionStrategy `json:"aws" required:"true" nullable:"false"`
|
||||
type CollectionStrategy struct {
|
||||
AWS *AWSCollectionStrategy `json:"aws" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AWSServiceConfig struct {
|
||||
Logs *AWSServiceLogsConfig `json:"logs"`
|
||||
Metrics *AWSServiceMetricsConfig `json:"metrics"`
|
||||
}
|
||||
|
||||
// AWSServiceLogsConfig is AWS specific logs config for a service
|
||||
// NOTE: the JSON keys are snake case for backward compatibility with existing agents.
|
||||
type AWSServiceLogsConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
S3Buckets map[string][]string `json:"s3_buckets,omitempty"`
|
||||
}
|
||||
|
||||
type AWSServiceMetricsConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// Assets represents the collection of dashboards.
|
||||
@@ -121,65 +120,46 @@ type CollectedMetric struct {
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// OldAWSCollectionStrategy is the backward-compatible snake_case form of AWSCollectionStrategy,
|
||||
// used in the legacy integration_config response field for older agents.
|
||||
type OldAWSCollectionStrategy struct {
|
||||
Provider string `json:"provider"`
|
||||
Metrics *OldAWSMetricsStrategy `json:"aws_metrics,omitempty"`
|
||||
Logs *OldAWSLogsStrategy `json:"aws_logs,omitempty"`
|
||||
S3Buckets map[string][]string `json:"s3_buckets,omitempty"`
|
||||
// AWSCollectionStrategy represents signal collection strategy for AWS services.
|
||||
// this is AWS specific.
|
||||
// NOTE: this structure is still using snake case, for backward compatibility,
|
||||
// with existing agents.
|
||||
type AWSCollectionStrategy struct {
|
||||
Metrics *AWSMetricsStrategy `json:"aws_metrics,omitempty"`
|
||||
Logs *AWSLogsStrategy `json:"aws_logs,omitempty"`
|
||||
S3Buckets map[string][]string `json:"s3_buckets,omitempty"` // Only available in S3 Sync Service Type in AWS
|
||||
}
|
||||
|
||||
// OldAWSMetricsStrategy is the snake_case form of AWSMetricsStrategy for older agents.
|
||||
type OldAWSMetricsStrategy struct {
|
||||
// AWSMetricsStrategy represents metrics collection strategy for AWS services.
|
||||
// this is AWS specific.
|
||||
// NOTE: this structure is still using snake case, for backward compatibility,
|
||||
// with existing agents.
|
||||
type AWSMetricsStrategy struct {
|
||||
// to be used as https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudwatch-metricstream.html#cfn-cloudwatch-metricstream-includefilters
|
||||
StreamFilters []struct {
|
||||
// json tags here are in the shape expected by AWS API as detailed at
|
||||
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudwatch-metricstream-metricstreamfilter.html
|
||||
Namespace string `json:"Namespace"`
|
||||
MetricNames []string `json:"MetricNames,omitempty"`
|
||||
} `json:"cloudwatch_metric_stream_filters"`
|
||||
}
|
||||
|
||||
// OldAWSLogsStrategy is the snake_case form of AWSLogsStrategy for older agents.
|
||||
type OldAWSLogsStrategy struct {
|
||||
// AWSLogsStrategy represents logs collection strategy for AWS services.
|
||||
// this is AWS specific.
|
||||
// NOTE: this structure is still using snake case, for backward compatibility,
|
||||
// with existing agents.
|
||||
type AWSLogsStrategy struct {
|
||||
Subscriptions []struct {
|
||||
// subscribe to all logs groups with specified prefix.
|
||||
// eg: `/aws/rds/`
|
||||
LogGroupNamePrefix string `json:"log_group_name_prefix"`
|
||||
FilterPattern string `json:"filter_pattern"`
|
||||
|
||||
// https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html
|
||||
// "" implies no filtering is required.
|
||||
FilterPattern string `json:"filter_pattern"`
|
||||
} `json:"cloudwatch_logs_subscriptions"`
|
||||
}
|
||||
|
||||
// AWSTelemetryCollectionStrategy represents signal collection strategy for AWS services.
|
||||
type AWSTelemetryCollectionStrategy struct {
|
||||
Metrics *AWSMetricsCollectionStrategy `json:"metrics,omitempty" required:"false" nullable:"false"`
|
||||
Logs *AWSLogsCollectionStrategy `json:"logs,omitempty" required:"false" nullable:"false"`
|
||||
S3Buckets map[string][]string `json:"s3Buckets,omitempty" required:"false"` // Only available in S3 Sync Service Type in AWS
|
||||
}
|
||||
|
||||
// AWSMetricsCollectionStrategy represents metrics collection strategy for AWS services.
|
||||
type AWSMetricsCollectionStrategy struct {
|
||||
// to be used as https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudwatch-metricstream.html#cfn-cloudwatch-metricstream-includefilters
|
||||
StreamFilters []*AWSCloudWatchMetricStreamFilter `json:"streamFilters" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AWSCloudWatchMetricStreamFilter struct {
|
||||
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudwatch-metricstream-metricstreamfilter.html
|
||||
Namespace string `json:"namespace" required:"true"`
|
||||
MetricNames []string `json:"metricNames,omitempty" required:"false" nullable:"false"`
|
||||
}
|
||||
|
||||
// AWSLogsCollectionStrategy represents logs collection strategy for AWS services.
|
||||
type AWSLogsCollectionStrategy struct {
|
||||
Subscriptions []*AWSCloudWatchLogsSubscription `json:"subscriptions" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AWSCloudWatchLogsSubscription struct {
|
||||
// subscribe to all logs groups with specified prefix.
|
||||
// eg: `/aws/rds/`
|
||||
LogGroupNamePrefix string `json:"logGroupNamePrefix" required:"true"`
|
||||
|
||||
// https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html
|
||||
// "" implies no filtering is required
|
||||
FilterPattern string `json:"filterPattern" required:"true"`
|
||||
}
|
||||
|
||||
// Dashboard represents a dashboard definition for cloud integration.
|
||||
// This is used to show available pre-made dashboards for a service,
|
||||
// hence has additional fields like id, title and description.
|
||||
@@ -190,156 +170,12 @@ type Dashboard struct {
|
||||
Definition dashboardtypes.StorableDashboardData `json:"definition,omitempty"`
|
||||
}
|
||||
|
||||
func NewCloudIntegrationService(serviceID ServiceID, cloudIntegrationID valuer.UUID, config *ServiceConfig) *CloudIntegrationService {
|
||||
return &CloudIntegrationService{
|
||||
Identifiable: types.Identifiable{
|
||||
ID: valuer.GenerateUUID(),
|
||||
},
|
||||
TimeAuditable: types.TimeAuditable{
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
Type: serviceID,
|
||||
Config: config,
|
||||
CloudIntegrationID: cloudIntegrationID,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCloudIntegrationServiceFromStorable(stored *StorableCloudIntegrationService, config *ServiceConfig) *CloudIntegrationService {
|
||||
return &CloudIntegrationService{
|
||||
Identifiable: stored.Identifiable,
|
||||
TimeAuditable: stored.TimeAuditable,
|
||||
Type: stored.Type,
|
||||
Config: config,
|
||||
CloudIntegrationID: stored.CloudIntegrationID,
|
||||
}
|
||||
}
|
||||
|
||||
func NewServiceMetadata(definition ServiceDefinition, enabled bool) *ServiceMetadata {
|
||||
return &ServiceMetadata{
|
||||
ServiceDefinitionMetadata: definition.ServiceDefinitionMetadata,
|
||||
Enabled: enabled,
|
||||
}
|
||||
}
|
||||
|
||||
func NewService(def ServiceDefinition, storableService *CloudIntegrationService) *Service {
|
||||
return &Service{
|
||||
ServiceDefinition: def,
|
||||
CloudIntegrationService: storableService,
|
||||
}
|
||||
}
|
||||
|
||||
func NewServiceConfigFromJSON(provider CloudProviderType, jsonString string) (*ServiceConfig, error) {
|
||||
storableServiceConfig, err := newStorableServiceConfigFromJSON(provider, jsonString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch provider {
|
||||
case CloudProviderTypeAWS:
|
||||
awsServiceConfig := new(AWSServiceConfig)
|
||||
|
||||
if storableServiceConfig.AWS.Logs != nil {
|
||||
awsServiceConfig.Logs = &AWSServiceLogsConfig{
|
||||
Enabled: storableServiceConfig.AWS.Logs.Enabled,
|
||||
S3Buckets: storableServiceConfig.AWS.Logs.S3Buckets,
|
||||
}
|
||||
}
|
||||
|
||||
if storableServiceConfig.AWS.Metrics != nil {
|
||||
awsServiceConfig.Metrics = &AWSServiceMetricsConfig{
|
||||
Enabled: storableServiceConfig.AWS.Metrics.Enabled,
|
||||
}
|
||||
}
|
||||
|
||||
return &ServiceConfig{AWS: awsServiceConfig}, nil
|
||||
default:
|
||||
return nil, errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid cloud provider: %s", provider.StringValue())
|
||||
}
|
||||
}
|
||||
|
||||
// Update sets the service config.
|
||||
func (service *CloudIntegrationService) Update(config *ServiceConfig) {
|
||||
service.Config = config
|
||||
service.UpdatedAt = time.Now()
|
||||
}
|
||||
|
||||
// IsServiceEnabled returns true if the service has at least one signal (logs or metrics) enabled
|
||||
// for the given cloud provider.
|
||||
func (config *ServiceConfig) IsServiceEnabled(provider CloudProviderType) bool {
|
||||
switch provider {
|
||||
case CloudProviderTypeAWS:
|
||||
logsEnabled := config.AWS.Logs != nil && config.AWS.Logs.Enabled
|
||||
metricsEnabled := config.AWS.Metrics != nil && config.AWS.Metrics.Enabled
|
||||
return logsEnabled || metricsEnabled
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsMetricsEnabled returns true if metrics are explicitly enabled for the given cloud provider.
|
||||
// Used to gate dashboard availability — dashboards are only shown when metrics are enabled.
|
||||
func (config *ServiceConfig) IsMetricsEnabled(provider CloudProviderType) bool {
|
||||
switch provider {
|
||||
case CloudProviderTypeAWS:
|
||||
return config.AWS.Metrics != nil && config.AWS.Metrics.Enabled
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsLogsEnabled returns true if logs are explicitly enabled for the given cloud provider.
|
||||
func (config *ServiceConfig) IsLogsEnabled(provider CloudProviderType) bool {
|
||||
switch provider {
|
||||
case CloudProviderTypeAWS:
|
||||
return config.AWS.Logs != nil && config.AWS.Logs.Enabled
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (config *ServiceConfig) ToJSON(provider CloudProviderType, serviceID ServiceID, supportedSignals *SupportedSignals) ([]byte, error) {
|
||||
storableServiceConfig := newStorableServiceConfig(provider, serviceID, config, supportedSignals)
|
||||
return storableServiceConfig.toJSON(provider)
|
||||
}
|
||||
|
||||
func (updatableService *UpdatableService) Validate(provider CloudProviderType, serviceID ServiceID) error {
|
||||
switch provider {
|
||||
case CloudProviderTypeAWS:
|
||||
if updatableService.Config.AWS == nil {
|
||||
return errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "AWS config is required for AWS service")
|
||||
}
|
||||
|
||||
if serviceID == AWSServiceS3Sync {
|
||||
if updatableService.Config.AWS.Logs == nil || updatableService.Config.AWS.Logs.S3Buckets == nil {
|
||||
return errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "AWS S3 Sync service requires S3 bucket configuration for logs")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
default:
|
||||
return errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid cloud provider: %s", provider.StringValue())
|
||||
}
|
||||
}
|
||||
// UTILS
|
||||
|
||||
// GetCloudIntegrationDashboardID returns the dashboard id for a cloud integration, given the cloud provider, service id, and dashboard id.
|
||||
// This is used to generate unique dashboard ids for cloud integration, and also to parse the dashboard id to get the cloud provider and service id when needed.
|
||||
func GetCloudIntegrationDashboardID(cloudProvider CloudProviderType, svcID, dashboardID string) string {
|
||||
return fmt.Sprintf("cloud-integration--%s--%s--%s", cloudProvider.StringValue(), svcID, dashboardID)
|
||||
}
|
||||
|
||||
// ParseCloudIntegrationDashboardID parses a dashboard id generated by GetCloudIntegrationDashboardID
|
||||
// into its constituent parts (cloudProvider, serviceID, dashboardID).
|
||||
func ParseCloudIntegrationDashboardID(id string) (CloudProviderType, string, string, error) {
|
||||
parts := strings.SplitN(id, "--", 4)
|
||||
if len(parts) != 4 || parts[0] != "cloud-integration" {
|
||||
return CloudProviderType{}, "", "", errors.New(errors.TypeNotFound, ErrCodeCloudIntegrationNotFound, "invalid cloud integration dashboard id")
|
||||
}
|
||||
provider, err := NewCloudProvider(parts[1])
|
||||
if err != nil {
|
||||
return CloudProviderType{}, "", "", err
|
||||
}
|
||||
return provider, parts[2], parts[3], nil
|
||||
return fmt.Sprintf("cloud-integration--%s--%s--%s", cloudProvider, svcID, dashboardID)
|
||||
}
|
||||
|
||||
// GetDashboardsFromAssets returns the list of dashboards for the cloud provider service from definition.
|
||||
@@ -353,9 +189,9 @@ func GetDashboardsFromAssets(
|
||||
dashboards := make([]*dashboardtypes.Dashboard, 0)
|
||||
|
||||
for _, d := range assets.Dashboards {
|
||||
author := fmt.Sprintf("%s-integration", cloudProvider.StringValue())
|
||||
author := fmt.Sprintf("%s-integration", cloudProvider)
|
||||
dashboards = append(dashboards, &dashboardtypes.Dashboard{
|
||||
ID: d.ID,
|
||||
ID: GetCloudIntegrationDashboardID(cloudProvider, svcID, d.ID),
|
||||
Locked: true,
|
||||
OrgID: orgID,
|
||||
Data: d.Definition,
|
||||
@@ -372,53 +208,3 @@ func GetDashboardsFromAssets(
|
||||
|
||||
return dashboards
|
||||
}
|
||||
|
||||
// awsOlderIntegrationConfig converts a ProviderIntegrationConfig into the legacy snake_case
|
||||
// IntegrationConfig format consumed by older AWS agents. Returns nil if AWS config is absent.
|
||||
func awsOlderIntegrationConfig(cfg *ProviderIntegrationConfig) *IntegrationConfig {
|
||||
if cfg == nil || cfg.AWS == nil {
|
||||
return nil
|
||||
}
|
||||
awsCfg := cfg.AWS
|
||||
|
||||
older := &IntegrationConfig{
|
||||
EnabledRegions: awsCfg.EnabledRegions,
|
||||
}
|
||||
|
||||
if awsCfg.TelemetryCollectionStrategy == nil {
|
||||
return older
|
||||
}
|
||||
|
||||
// Older agents expect a "provider" field and fully snake_case keys inside telemetry.
|
||||
oldTelemetry := &OldAWSCollectionStrategy{
|
||||
Provider: CloudProviderTypeAWS.StringValue(),
|
||||
S3Buckets: awsCfg.TelemetryCollectionStrategy.S3Buckets,
|
||||
}
|
||||
|
||||
if awsCfg.TelemetryCollectionStrategy.Metrics != nil {
|
||||
// Convert camelCase cloudwatchMetricStreamFilters → snake_case cloudwatch_metric_stream_filters
|
||||
oldMetrics := &OldAWSMetricsStrategy{}
|
||||
for _, f := range awsCfg.TelemetryCollectionStrategy.Metrics.StreamFilters {
|
||||
oldMetrics.StreamFilters = append(oldMetrics.StreamFilters, struct {
|
||||
Namespace string `json:"Namespace"`
|
||||
MetricNames []string `json:"MetricNames,omitempty"`
|
||||
}{Namespace: f.Namespace, MetricNames: f.MetricNames})
|
||||
}
|
||||
oldTelemetry.Metrics = oldMetrics
|
||||
}
|
||||
|
||||
if awsCfg.TelemetryCollectionStrategy.Logs != nil {
|
||||
// Convert camelCase cloudwatchLogsSubscriptions → snake_case cloudwatch_logs_subscriptions
|
||||
oldLogs := &OldAWSLogsStrategy{}
|
||||
for _, s := range awsCfg.TelemetryCollectionStrategy.Logs.Subscriptions {
|
||||
oldLogs.Subscriptions = append(oldLogs.Subscriptions, struct {
|
||||
LogGroupNamePrefix string `json:"log_group_name_prefix"`
|
||||
FilterPattern string `json:"filter_pattern"`
|
||||
}{LogGroupNamePrefix: s.LogGroupNamePrefix, FilterPattern: s.FilterPattern})
|
||||
}
|
||||
oldTelemetry.Logs = oldLogs
|
||||
}
|
||||
|
||||
older.Telemetry = oldTelemetry
|
||||
return older
|
||||
}
|
||||
|
||||
@@ -38,8 +38,6 @@ type Store interface {
|
||||
|
||||
// UpdateService updates an existing cloud integration service
|
||||
UpdateService(ctx context.Context, service *StorableCloudIntegrationService) error
|
||||
|
||||
RunInTx(context.Context, func(ctx context.Context) error) error
|
||||
}
|
||||
|
||||
type ServiceDefinitionStore interface {
|
||||
|
||||
210
pkg/types/dashboardtypes/dashboard_v2.go
Normal file
210
pkg/types/dashboardtypes/dashboard_v2.go
Normal file
@@ -0,0 +1,210 @@
|
||||
package dashboardtypes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
qb "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/go-playground/validator/v10"
|
||||
v1 "github.com/perses/perses/pkg/model/api/v1"
|
||||
"github.com/perses/perses/pkg/model/api/v1/common"
|
||||
)
|
||||
|
||||
type StorableDashboardDataV2 = v1.DashboardSpec
|
||||
|
||||
// UnmarshalAndValidateDashboardV2JSON unmarshals the JSON into a StorableDashboardDataV2
|
||||
// (= PostableDashboardV2 = UpdatableDashboardV2) and validates plugin kinds and specs.
|
||||
func UnmarshalAndValidateDashboardV2JSON(data []byte) (*StorableDashboardDataV2, error) {
|
||||
var d StorableDashboardDataV2
|
||||
if err := json.Unmarshal(data, &d); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := validateDashboardV2(d); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
// Plugin kind → spec type factory. Each value is a pointer to the zero value of the
|
||||
// expected spec struct. validatePluginSpec marshals plugin.Spec back to JSON and
|
||||
// unmarshals into the typed struct to catch field-level errors.
|
||||
var (
|
||||
panelPluginSpecs = map[string]func() any{
|
||||
PanelKindTimeSeries: func() any { return new(TimeSeriesPanelSpec) },
|
||||
PanelKindBarChart: func() any { return new(BarChartPanelSpec) },
|
||||
PanelKindNumber: func() any { return new(NumberPanelSpec) },
|
||||
PanelKindPieChart: func() any { return new(PieChartPanelSpec) },
|
||||
PanelKindTable: func() any { return new(TablePanelSpec) },
|
||||
PanelKindHistogram: func() any { return new(HistogramPanelSpec) },
|
||||
PanelKindList: func() any { return new(ListPanelSpec) },
|
||||
}
|
||||
queryPluginSpecs = map[string]func() any{
|
||||
QueryKindBuilder: func() any { return new(BuilderQuerySpec) },
|
||||
QueryKindComposite: func() any { return new(CompositeQuerySpec) },
|
||||
QueryKindFormula: func() any { return new(FormulaSpec) },
|
||||
QueryKindPromQL: func() any { return new(PromQLQuerySpec) },
|
||||
QueryKindClickHouseSQL: func() any { return new(ClickHouseSQLQuerySpec) },
|
||||
QueryKindTraceOperator: func() any { return new(TraceOperatorSpec) },
|
||||
}
|
||||
variablePluginSpecs = map[string]func() any{
|
||||
VariableKindDynamic: func() any { return new(DynamicVariableSpec) },
|
||||
VariableKindQuery: func() any { return new(QueryVariableSpec) },
|
||||
VariableKindCustom: func() any { return new(CustomVariableSpec) },
|
||||
VariableKindTextbox: func() any { return new(TextboxVariableSpec) },
|
||||
}
|
||||
datasourcePluginSpecs = map[string]func() any{
|
||||
DatasourceKindSigNoz: func() any { return new(struct{}) },
|
||||
}
|
||||
|
||||
// allowedQueryKinds maps each panel plugin kind to the query plugin
|
||||
// kinds it supports. Composite sub-query types are mapped to these
|
||||
// same kind strings via compositeSubQueryTypeToPluginKind.
|
||||
allowedQueryKinds = map[string][]string{
|
||||
PanelKindTimeSeries: {QueryKindBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindPromQL, QueryKindClickHouseSQL},
|
||||
PanelKindBarChart: {QueryKindBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindPromQL, QueryKindClickHouseSQL},
|
||||
PanelKindNumber: {QueryKindBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindPromQL, QueryKindClickHouseSQL},
|
||||
PanelKindHistogram: {QueryKindBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindPromQL, QueryKindClickHouseSQL},
|
||||
PanelKindPieChart: {QueryKindBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindClickHouseSQL},
|
||||
PanelKindTable: {QueryKindBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindClickHouseSQL},
|
||||
PanelKindList: {QueryKindBuilder},
|
||||
}
|
||||
|
||||
// compositeSubQueryTypeToPluginKind maps CompositeQuery sub-query type
|
||||
// strings to the equivalent top-level query plugin kind for validation.
|
||||
compositeSubQueryTypeToPluginKind = map[string]string{
|
||||
qb.QueryTypeBuilder.StringValue(): QueryKindBuilder,
|
||||
qb.QueryTypeFormula.StringValue(): QueryKindFormula,
|
||||
qb.QueryTypeTraceOperator.StringValue(): QueryKindTraceOperator,
|
||||
qb.QueryTypePromQL.StringValue(): QueryKindPromQL,
|
||||
qb.QueryTypeClickHouseSQL.StringValue(): QueryKindClickHouseSQL,
|
||||
}
|
||||
)
|
||||
|
||||
func validateDashboardV2(d StorableDashboardDataV2) error {
|
||||
// Validate datasource plugins.
|
||||
for name, ds := range d.Datasources {
|
||||
if err := validatePlugin(ds.Plugin, datasourcePluginSpecs, fmt.Sprintf("spec.datasources.%s.plugin", name)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Validate variable plugins (only ListVariables have plugins; TextVariables do not).
|
||||
for i, v := range d.Variables {
|
||||
plugin, err := extractPluginFromVariable(v)
|
||||
if err != nil {
|
||||
return errors.WrapInvalidInputf(err, ErrCodeDashboardInvalidInput, "spec.variables[%d]", i)
|
||||
}
|
||||
if plugin == nil {
|
||||
continue
|
||||
}
|
||||
if err := validatePlugin(*plugin, variablePluginSpecs, fmt.Sprintf("spec.variables[%d].spec.plugin", i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Validate panel and query plugins.
|
||||
for key, panel := range d.Panels {
|
||||
if panel == nil {
|
||||
continue
|
||||
}
|
||||
path := fmt.Sprintf("spec.panels.%s", key)
|
||||
if err := validatePlugin(panel.Spec.Plugin, panelPluginSpecs, path+".spec.plugin"); err != nil {
|
||||
return err
|
||||
}
|
||||
allowed := allowedQueryKinds[panel.Spec.Plugin.Kind]
|
||||
for qi, query := range panel.Spec.Queries {
|
||||
queryPath := fmt.Sprintf("%s.spec.queries[%d].spec.plugin", path, qi)
|
||||
if err := validatePlugin(query.Spec.Plugin, queryPluginSpecs, queryPath); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateQueryAllowedForPanel(query.Spec.Plugin, allowed, panel.Spec.Plugin.Kind, queryPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validatePlugin(plugin common.Plugin, specs map[string]func() any, path string) error {
|
||||
if plugin.Kind == "" {
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "%s: plugin kind is required", path)
|
||||
}
|
||||
factory, ok := specs[plugin.Kind]
|
||||
if !ok {
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "%s: unknown plugin kind %q", path, plugin.Kind)
|
||||
}
|
||||
if plugin.Spec == nil {
|
||||
return nil
|
||||
}
|
||||
// Re-marshal the spec and unmarshal into the typed struct.
|
||||
specJSON, err := json.Marshal(plugin.Spec)
|
||||
if err != nil {
|
||||
return errors.WrapInvalidInputf(err, ErrCodeDashboardInvalidInput, "%s.spec", path)
|
||||
}
|
||||
target := factory()
|
||||
if err := json.Unmarshal(specJSON, target); err != nil {
|
||||
return errors.WrapInvalidInputf(err, ErrCodeDashboardInvalidInput, "%s.spec", path)
|
||||
}
|
||||
if err := validator.New().Struct(target); err != nil {
|
||||
return errors.WrapInvalidInputf(err, ErrCodeDashboardInvalidInput, "%s.spec", path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateQueryAllowedForPanel checks that the query plugin kind is permitted
|
||||
// for the given panel. For composite queries it recurses into sub-queries.
|
||||
func validateQueryAllowedForPanel(plugin common.Plugin, allowed []string, panelKind string, path string) error {
|
||||
if !slices.Contains(allowed, plugin.Kind) {
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput,
|
||||
"%s: query kind %q is not supported by panel kind %q", path, plugin.Kind, panelKind)
|
||||
}
|
||||
|
||||
// For composite queries, validate each sub-query type.
|
||||
if plugin.Kind == QueryKindComposite && plugin.Spec != nil {
|
||||
specJSON, err := json.Marshal(plugin.Spec)
|
||||
if err != nil {
|
||||
return errors.WrapInvalidInputf(err, ErrCodeDashboardInvalidInput, "%s.spec", path)
|
||||
}
|
||||
var composite struct {
|
||||
Queries []struct {
|
||||
Type string `json:"type"`
|
||||
} `json:"queries"`
|
||||
}
|
||||
if err := json.Unmarshal(specJSON, &composite); err != nil {
|
||||
return errors.WrapInvalidInputf(err, ErrCodeDashboardInvalidInput, "%s.spec", path)
|
||||
}
|
||||
for si, sub := range composite.Queries {
|
||||
pluginKind, ok := compositeSubQueryTypeToPluginKind[sub.Type]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if !slices.Contains(allowed, pluginKind) {
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput,
|
||||
"%s.spec.queries[%d]: sub-query type %q is not supported by panel kind %q",
|
||||
path, si, sub.Type, panelKind)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// extractPluginFromVariable extracts the plugin from a variable.
|
||||
// Returns nil if the variable has no plugin (e.g. TextVariable).
|
||||
func extractPluginFromVariable(v any) (*common.Plugin, error) {
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var raw struct {
|
||||
Spec struct {
|
||||
Plugin *common.Plugin `json:"plugin,omitempty"`
|
||||
} `json:"spec"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return raw.Spec.Plugin, nil
|
||||
}
|
||||
571
pkg/types/dashboardtypes/dashboard_v2_test.go
Normal file
571
pkg/types/dashboardtypes/dashboard_v2_test.go
Normal file
@@ -0,0 +1,571 @@
|
||||
package dashboardtypes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValidateBigExample(t *testing.T) {
|
||||
data, err := os.ReadFile("testdata/perses.json")
|
||||
if err != nil {
|
||||
t.Fatalf("reading example file: %v", err)
|
||||
}
|
||||
if _, err := UnmarshalAndValidateDashboardV2JSON(data); err != nil {
|
||||
t.Fatalf("expected valid dashboard, got error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDashboardWithSections(t *testing.T) {
|
||||
data, err := os.ReadFile("testdata/perses_with_sections.json")
|
||||
if err != nil {
|
||||
t.Fatalf("reading example file: %v", err)
|
||||
}
|
||||
if _, err := UnmarshalAndValidateDashboardV2JSON(data); err != nil {
|
||||
t.Fatalf("expected valid dashboard, got error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidateNotAJSON(t *testing.T) {
|
||||
if _, err := UnmarshalAndValidateDashboardV2JSON([]byte("not json")); err == nil {
|
||||
t.Fatal("expected error for invalid JSON")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateEmptySpec(t *testing.T) {
|
||||
// no variables no panels
|
||||
data := []byte(`{}`)
|
||||
if _, err := UnmarshalAndValidateDashboardV2JSON(data); err != nil {
|
||||
t.Fatalf("expected valid, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateOnlyVariables(t *testing.T) {
|
||||
data := []byte(`{
|
||||
"variables": [
|
||||
{
|
||||
"kind": "ListVariable",
|
||||
"spec": {
|
||||
"name": "service",
|
||||
"allowAllValue": true,
|
||||
"allowMultiple": false,
|
||||
"plugin": {
|
||||
"kind": "SigNozDynamicVariable",
|
||||
"spec": {
|
||||
"name": "service.name",
|
||||
"source": "Metrics"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "TextVariable",
|
||||
"spec": {
|
||||
"name": "mytext",
|
||||
"value": "default",
|
||||
"plugin": {
|
||||
"kind": "SigNozTextboxVariable",
|
||||
"spec": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"layouts": []
|
||||
}`)
|
||||
if _, err := UnmarshalAndValidateDashboardV2JSON(data); err != nil {
|
||||
t.Fatalf("expected valid, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidateUnknownPluginKind(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data string
|
||||
wantContain string
|
||||
}{
|
||||
{
|
||||
name: "unknown panel plugin",
|
||||
data: `{
|
||||
"panels": {
|
||||
"p1": {
|
||||
"kind": "Panel",
|
||||
"spec": {
|
||||
"plugin": {"kind": "NonExistentPanel", "spec": {}}
|
||||
}
|
||||
}
|
||||
},
|
||||
"layouts": []
|
||||
}`,
|
||||
wantContain: "NonExistentPanel",
|
||||
},
|
||||
{
|
||||
name: "unknown query plugin",
|
||||
data: `{
|
||||
"panels": {
|
||||
"p1": {
|
||||
"kind": "Panel",
|
||||
"spec": {
|
||||
"plugin": {"kind": "SigNozTimeSeriesPanel", "spec": {}},
|
||||
"queries": [{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"spec": {
|
||||
"plugin": {"kind": "FakeQueryPlugin", "spec": {}}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"layouts": []
|
||||
}`,
|
||||
wantContain: "FakeQueryPlugin",
|
||||
},
|
||||
{
|
||||
name: "unknown variable plugin",
|
||||
data: `{
|
||||
"variables": [{
|
||||
"kind": "ListVariable",
|
||||
"spec": {
|
||||
"name": "v1",
|
||||
"allowAllValue": false,
|
||||
"allowMultiple": false,
|
||||
"plugin": {"kind": "FakeVariable", "spec": {}}
|
||||
}
|
||||
}],
|
||||
"layouts": []
|
||||
}`,
|
||||
wantContain: "FakeVariable",
|
||||
},
|
||||
{
|
||||
name: "unknown datasource plugin",
|
||||
data: `{
|
||||
"datasources": {
|
||||
"ds1": {
|
||||
"default": true,
|
||||
"plugin": {"kind": "FakeDatasource", "spec": {}}
|
||||
}
|
||||
},
|
||||
"layouts": []
|
||||
}`,
|
||||
wantContain: "FakeDatasource",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := UnmarshalAndValidateDashboardV2JSON([]byte(tt.data))
|
||||
if err == nil {
|
||||
t.Fatalf("expected error containing %q, got nil", tt.wantContain)
|
||||
}
|
||||
if !strings.Contains(err.Error(), tt.wantContain) {
|
||||
t.Fatalf("error should mention %q, got: %v", tt.wantContain, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidateOneInvalidPanel(t *testing.T) {
|
||||
data := []byte(`{
|
||||
"panels": {
|
||||
"good": {
|
||||
"kind": "Panel",
|
||||
"spec": {"plugin": {"kind": "SigNozNumberPanel", "spec": {}}}
|
||||
},
|
||||
"bad": {
|
||||
"kind": "Panel",
|
||||
"spec": {"plugin": {"kind": "FakePanel", "spec": {}}}
|
||||
}
|
||||
},
|
||||
"layouts": []
|
||||
}`)
|
||||
_, err := UnmarshalAndValidateDashboardV2JSON(data)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid panel plugin kind")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "FakePanel") {
|
||||
t.Fatalf("error should mention FakePanel, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidateWrongFieldTypeInPluginSpec(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data string
|
||||
wantContain string
|
||||
}{
|
||||
{
|
||||
name: "wrong type on panel plugin field",
|
||||
data: `{
|
||||
"panels": {
|
||||
"p1": {
|
||||
"kind": "Panel",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "SigNozTimeSeriesPanel",
|
||||
"spec": {"visualization": {"fillSpans": "notabool"}}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"layouts": []
|
||||
}`,
|
||||
wantContain: "fillSpans",
|
||||
},
|
||||
{
|
||||
name: "wrong type on query plugin field",
|
||||
data: `{
|
||||
"panels": {
|
||||
"p1": {
|
||||
"kind": "Panel",
|
||||
"spec": {
|
||||
"plugin": {"kind": "SigNozTimeSeriesPanel", "spec": {}},
|
||||
"queries": [{
|
||||
"kind": "TimeSeriesQuery",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "SigNozPromQLQuery",
|
||||
"spec": {"name": "A", "query": 123}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"layouts": []
|
||||
}`,
|
||||
wantContain: "",
|
||||
},
|
||||
{
|
||||
name: "wrong type on variable plugin field",
|
||||
data: `{
|
||||
"variables": [{
|
||||
"kind": "ListVariable",
|
||||
"spec": {
|
||||
"name": "v",
|
||||
"allowAllValue": false,
|
||||
"allowMultiple": false,
|
||||
"plugin": {
|
||||
"kind": "SigNozDynamicVariable",
|
||||
"spec": {"name": 123, "source": "Metrics"}
|
||||
}
|
||||
}
|
||||
}],
|
||||
"layouts": []
|
||||
}`,
|
||||
wantContain: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := UnmarshalAndValidateDashboardV2JSON([]byte(tt.data))
|
||||
if err == nil {
|
||||
t.Fatal("expected validation error")
|
||||
}
|
||||
if tt.wantContain != "" && !strings.Contains(err.Error(), tt.wantContain) {
|
||||
t.Fatalf("error should mention %q, got: %v", tt.wantContain, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidateBadPanelSpecValues(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data string
|
||||
wantContain string
|
||||
}{
|
||||
{
|
||||
name: "bad time preference",
|
||||
data: `{
|
||||
"panels": {
|
||||
"p1": {
|
||||
"kind": "Panel",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "SigNozTimeSeriesPanel",
|
||||
"spec": {"visualization": {"timePreference": "last2Hr"}}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"layouts": []
|
||||
}`,
|
||||
wantContain: "timePreference",
|
||||
},
|
||||
{
|
||||
name: "bad legend position",
|
||||
data: `{
|
||||
"panels": {
|
||||
"p1": {
|
||||
"kind": "Panel",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "SigNozBarChartPanel",
|
||||
"spec": {"legend": {"position": "top"}}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"layouts": []
|
||||
}`,
|
||||
wantContain: "legend position",
|
||||
},
|
||||
{
|
||||
name: "bad threshold format",
|
||||
data: `{
|
||||
"panels": {
|
||||
"p1": {
|
||||
"kind": "Panel",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "SigNozNumberPanel",
|
||||
"spec": {"thresholds": [{"value": 100, "operator": ">", "color": "Red", "format": "Color"}]}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"layouts": []
|
||||
}`,
|
||||
wantContain: "threshold format",
|
||||
},
|
||||
{
|
||||
name: "bad comparison operator",
|
||||
data: `{
|
||||
"panels": {
|
||||
"p1": {
|
||||
"kind": "Panel",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "SigNozNumberPanel",
|
||||
"spec": {"thresholds": [{"value": 100, "operator": "!=", "color": "Red", "format": "Text"}]}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"layouts": []
|
||||
}`,
|
||||
wantContain: "comparison operator",
|
||||
},
|
||||
{
|
||||
name: "bad precision",
|
||||
data: `{
|
||||
"panels": {
|
||||
"p1": {
|
||||
"kind": "Panel",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "SigNozTimeSeriesPanel",
|
||||
"spec": {"formatting": {"decimalPrecision": 9}}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"layouts": []
|
||||
}`,
|
||||
wantContain: "precision",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := UnmarshalAndValidateDashboardV2JSON([]byte(tt.data))
|
||||
if err == nil {
|
||||
t.Fatalf("expected error containing %q, got nil", tt.wantContain)
|
||||
}
|
||||
if !strings.Contains(err.Error(), tt.wantContain) {
|
||||
t.Fatalf("error should mention %q, got: %v", tt.wantContain, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRequiredFields(t *testing.T) {
|
||||
wrapVariable := func(pluginKind, pluginSpec string) string {
|
||||
return `{
|
||||
"variables": [{
|
||||
"kind": "ListVariable",
|
||||
"spec": {
|
||||
"name": "v",
|
||||
"allowAllValue": false,
|
||||
"allowMultiple": false,
|
||||
"plugin": {"kind": "` + pluginKind + `", "spec": ` + pluginSpec + `}
|
||||
}
|
||||
}],
|
||||
"layouts": []
|
||||
}`
|
||||
}
|
||||
wrapPanel := func(panelKind, panelSpec string) string {
|
||||
return `{
|
||||
"panels": {
|
||||
"p1": {
|
||||
"kind": "Panel",
|
||||
"spec": {
|
||||
"plugin": {"kind": "` + panelKind + `", "spec": ` + panelSpec + `}
|
||||
}
|
||||
}
|
||||
},
|
||||
"layouts": []
|
||||
}`
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
data string
|
||||
wantContain string
|
||||
}{
|
||||
{
|
||||
name: "DynamicVariable missing name",
|
||||
data: wrapVariable("SigNozDynamicVariable", `{"source": "Metrics"}`),
|
||||
wantContain: "Name",
|
||||
},
|
||||
{
|
||||
name: "DynamicVariable missing source",
|
||||
data: wrapVariable("SigNozDynamicVariable", `{"name": "http.method"}`),
|
||||
wantContain: "Source",
|
||||
},
|
||||
{
|
||||
name: "QueryVariable missing queryValue",
|
||||
data: wrapVariable("SigNozQueryVariable", `{}`),
|
||||
wantContain: "QueryValue",
|
||||
},
|
||||
{
|
||||
name: "CustomVariable missing customValue",
|
||||
data: wrapVariable("SigNozCustomVariable", `{}`),
|
||||
wantContain: "CustomValue",
|
||||
},
|
||||
{
|
||||
name: "ThresholdWithLabel missing color",
|
||||
data: wrapPanel("SigNozTimeSeriesPanel", `{"thresholds": [{"value": 100, "label": "high", "color": ""}]}`),
|
||||
wantContain: "Color",
|
||||
},
|
||||
{
|
||||
name: "ThresholdWithLabel missing label",
|
||||
data: wrapPanel("SigNozTimeSeriesPanel", `{"thresholds": [{"value": 100, "color": "Red", "label": ""}]}`),
|
||||
wantContain: "Label",
|
||||
},
|
||||
{
|
||||
name: "ComparisonThreshold missing color",
|
||||
data: wrapPanel("SigNozNumberPanel", `{"thresholds": [{"value": 100, "operator": ">", "format": "Text", "color": ""}]}`),
|
||||
wantContain: "Color",
|
||||
},
|
||||
{
|
||||
name: "TableThreshold missing columnName",
|
||||
data: wrapPanel("SigNozTablePanel", `{"thresholds": [{"value": 100, "operator": ">", "format": "Text", "color": "Red", "columnName": ""}]}`),
|
||||
wantContain: "ColumnName",
|
||||
},
|
||||
{
|
||||
name: "LogField missing name",
|
||||
data: wrapPanel("SigNozListPanel", `{"selectedLogFields": [{"name": "", "type": "log", "dataType": "string"}]}`),
|
||||
wantContain: "Name",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := UnmarshalAndValidateDashboardV2JSON([]byte(tt.data))
|
||||
if err == nil {
|
||||
t.Fatalf("expected error containing %q, got nil", tt.wantContain)
|
||||
}
|
||||
if !strings.Contains(err.Error(), tt.wantContain) {
|
||||
t.Fatalf("error should mention %q, got: %v", tt.wantContain, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeSeriesPanelDefaults(t *testing.T) {
|
||||
data := []byte(`{
|
||||
"panels": {
|
||||
"p1": {
|
||||
"kind": "Panel",
|
||||
"spec": {
|
||||
"plugin": {
|
||||
"kind": "SigNozTimeSeriesPanel",
|
||||
"spec": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"layouts": []
|
||||
}`)
|
||||
var d StorableDashboardDataV2
|
||||
if err := json.Unmarshal(data, &d); err != nil {
|
||||
t.Fatalf("unmarshal failed: %v", err)
|
||||
}
|
||||
|
||||
specJSON, _ := json.Marshal(d.Panels["p1"].Spec.Plugin.Spec)
|
||||
var spec TimeSeriesPanelSpec
|
||||
if err := json.Unmarshal(specJSON, &spec); err != nil {
|
||||
t.Fatalf("unmarshal spec failed: %v", err)
|
||||
}
|
||||
|
||||
if spec.Formatting.DecimalPrecision.Value() != 2 {
|
||||
t.Fatalf("expected DecimalPrecision default 2, got %v", spec.Formatting.DecimalPrecision.Value())
|
||||
}
|
||||
if spec.ChartAppearance.LineInterpolation.Value() != "spline" {
|
||||
t.Fatalf("expected LineInterpolation default spline, got %v", spec.ChartAppearance.LineInterpolation.Value())
|
||||
}
|
||||
if spec.ChartAppearance.LineStyle.Value() != "solid" {
|
||||
t.Fatalf("expected LineStyle default solid, got %v", spec.ChartAppearance.LineStyle.Value())
|
||||
}
|
||||
if spec.ChartAppearance.FillMode.Value() != "solid" {
|
||||
t.Fatalf("expected FillMode default solid, got %v", spec.ChartAppearance.FillMode.Value())
|
||||
}
|
||||
if spec.ChartAppearance.SpanGaps.Value() != true {
|
||||
t.Fatalf("expected SpanGaps default true, got %v", spec.ChartAppearance.SpanGaps.Value())
|
||||
}
|
||||
}
|
||||
|
||||
func TestPanelTypeQueryTypeCompatibility(t *testing.T) {
|
||||
mkQuery := func(panelKind, queryKind, querySpec string) []byte {
|
||||
return []byte(`{
|
||||
"panels": {"p1": {"kind": "Panel", "spec": {
|
||||
"plugin": {"kind": "` + panelKind + `", "spec": {}},
|
||||
"queries": [{"kind": "TimeSeriesQuery", "spec": {"plugin": {"kind": "` + queryKind + `", "spec": ` + querySpec + `}}}]
|
||||
}}},
|
||||
"layouts": []
|
||||
}`)
|
||||
}
|
||||
mkComposite := func(panelKind, subType, subSpec string) []byte {
|
||||
return []byte(`{
|
||||
"panels": {"p1": {"kind": "Panel", "spec": {
|
||||
"plugin": {"kind": "` + panelKind + `", "spec": {}},
|
||||
"queries": [{"kind": "TimeSeriesQuery", "spec": {"plugin": {"kind": "SigNozCompositeQuery", "spec": {
|
||||
"queries": [{"type": "` + subType + `", "spec": ` + subSpec + `}]
|
||||
}}}}]
|
||||
}}},
|
||||
"layouts": []
|
||||
}`)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
data []byte
|
||||
wantErr bool
|
||||
}{
|
||||
// Top-level: allowed
|
||||
{"TimeSeries+PromQL", mkQuery("SigNozTimeSeriesPanel", "SigNozPromQLQuery", `{"name":"A","query":"up"}`), false},
|
||||
{"Table+ClickHouse", mkQuery("SigNozTablePanel", "SigNozClickHouseSQL", `{"name":"A","query":"SELECT 1"}`), false},
|
||||
{"List+Builder", mkQuery("SigNozListPanel", "SigNozBuilderQuery", `{"name":"A","signal":"logs"}`), false},
|
||||
// Top-level: rejected
|
||||
{"Table+PromQL", mkQuery("SigNozTablePanel", "SigNozPromQLQuery", `{"name":"A","query":"up"}`), true},
|
||||
{"List+ClickHouse", mkQuery("SigNozListPanel", "SigNozClickHouseSQL", `{"name":"A","query":"SELECT 1"}`), true},
|
||||
{"List+PromQL", mkQuery("SigNozListPanel", "SigNozPromQLQuery", `{"name":"A","query":"up"}`), true},
|
||||
{"List+Composite", mkQuery("SigNozListPanel", "SigNozCompositeQuery", `{"queries":[]}`), true},
|
||||
{"List+Formula", mkQuery("SigNozListPanel", "SigNozFormula", `{"name":"F1","expression":"A+B"}`), true},
|
||||
// Composite sub-queries
|
||||
{"Table+Composite(promql)", mkComposite("SigNozTablePanel", "promql", `{"name":"A","query":"up"}`), true},
|
||||
{"Table+Composite(clickhouse)", mkComposite("SigNozTablePanel", "clickhouse_sql", `{"name":"A","query":"SELECT 1"}`), false},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
_, err := UnmarshalAndValidateDashboardV2JSON(tc.data)
|
||||
if tc.wantErr && err == nil {
|
||||
t.Fatalf("%s: expected error, got nil", tc.name)
|
||||
}
|
||||
if !tc.wantErr && err != nil {
|
||||
t.Fatalf("%s: expected valid, got: %v", tc.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
541
pkg/types/dashboardtypes/perses_plugin_types.go
Normal file
541
pkg/types/dashboardtypes/perses_plugin_types.go
Normal file
@@ -0,0 +1,541 @@
|
||||
package dashboardtypes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
qb "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
)
|
||||
|
||||
// ══════════════════════════════════════════════
|
||||
// SigNoz variable plugin specs
|
||||
// ══════════════════════════════════════════════
|
||||
|
||||
type VariablePluginKind = string
|
||||
|
||||
const (
|
||||
VariableKindDynamic VariablePluginKind = "SigNozDynamicVariable"
|
||||
VariableKindQuery VariablePluginKind = "SigNozQueryVariable"
|
||||
VariableKindCustom VariablePluginKind = "SigNozCustomVariable"
|
||||
VariableKindTextbox VariablePluginKind = "SigNozTextboxVariable"
|
||||
)
|
||||
|
||||
type DynamicVariableSpec struct {
|
||||
// Name is the name of the attribute being fetched dynamically from the
|
||||
// source. This could be extended to a richer selector in the future.
|
||||
Name string `json:"name" validate:"required" required:"true"`
|
||||
Source string `json:"source" validate:"required" required:"true"`
|
||||
}
|
||||
|
||||
type QueryVariableSpec struct {
|
||||
QueryValue string `json:"queryValue" validate:"required" required:"true"`
|
||||
}
|
||||
|
||||
type CustomVariableSpec struct {
|
||||
CustomValue string `json:"customValue" validate:"required" required:"true"`
|
||||
}
|
||||
|
||||
type TextboxVariableSpec struct{}
|
||||
|
||||
// ══════════════════════════════════════════════
|
||||
// SigNoz query plugin specs — aliased from querybuildertypesv5
|
||||
// ══════════════════════════════════════════════
|
||||
|
||||
type QueryPluginKind = string
|
||||
|
||||
const (
|
||||
QueryKindBuilder QueryPluginKind = "SigNozBuilderQuery"
|
||||
QueryKindComposite QueryPluginKind = "SigNozCompositeQuery"
|
||||
QueryKindFormula QueryPluginKind = "SigNozFormula"
|
||||
QueryKindPromQL QueryPluginKind = "SigNozPromQLQuery"
|
||||
QueryKindClickHouseSQL QueryPluginKind = "SigNozClickHouseSQL"
|
||||
QueryKindTraceOperator QueryPluginKind = "SigNozTraceOperator"
|
||||
)
|
||||
|
||||
type (
|
||||
CompositeQuerySpec = qb.CompositeQuery
|
||||
QueryEnvelope = qb.QueryEnvelope
|
||||
FormulaSpec = qb.QueryBuilderFormula
|
||||
PromQLQuerySpec = qb.PromQuery
|
||||
ClickHouseSQLQuerySpec = qb.ClickHouseQuery
|
||||
TraceOperatorSpec = qb.QueryBuilderTraceOperator
|
||||
)
|
||||
|
||||
// BuilderQuerySpec dispatches to MetricBuilderQuerySpec, LogBuilderQuerySpec,
|
||||
// or TraceBuilderQuerySpec based on the signal field.
|
||||
|
||||
type (
|
||||
MetricBuilderQuerySpec = qb.QueryBuilderQuery[qb.MetricAggregation]
|
||||
LogBuilderQuerySpec = qb.QueryBuilderQuery[qb.LogAggregation]
|
||||
TraceBuilderQuerySpec = qb.QueryBuilderQuery[qb.TraceAggregation]
|
||||
)
|
||||
|
||||
type BuilderQuerySpec struct {
|
||||
Spec any
|
||||
}
|
||||
|
||||
func (b *BuilderQuerySpec) UnmarshalJSON(data []byte) error {
|
||||
var peek struct {
|
||||
Signal string `json:"signal"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &peek); err != nil {
|
||||
return err
|
||||
}
|
||||
switch peek.Signal {
|
||||
case "metrics":
|
||||
var spec MetricBuilderQuerySpec
|
||||
if err := json.Unmarshal(data, &spec); err != nil {
|
||||
return err
|
||||
}
|
||||
b.Spec = spec
|
||||
case "logs":
|
||||
var spec LogBuilderQuerySpec
|
||||
if err := json.Unmarshal(data, &spec); err != nil {
|
||||
return err
|
||||
}
|
||||
b.Spec = spec
|
||||
case "traces":
|
||||
var spec TraceBuilderQuerySpec
|
||||
if err := json.Unmarshal(data, &spec); err != nil {
|
||||
return err
|
||||
}
|
||||
b.Spec = spec
|
||||
default:
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "invalid signal %q: must be metrics, logs, or traces", peek.Signal)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════
|
||||
// SigNoz panel plugin specs
|
||||
// ══════════════════════════════════════════════
|
||||
|
||||
type PanelPluginKind = string
|
||||
|
||||
const (
|
||||
PanelKindTimeSeries PanelPluginKind = "SigNozTimeSeriesPanel"
|
||||
PanelKindBarChart PanelPluginKind = "SigNozBarChartPanel"
|
||||
PanelKindNumber PanelPluginKind = "SigNozNumberPanel"
|
||||
PanelKindPieChart PanelPluginKind = "SigNozPieChartPanel"
|
||||
PanelKindTable PanelPluginKind = "SigNozTablePanel"
|
||||
PanelKindHistogram PanelPluginKind = "SigNozHistogramPanel"
|
||||
PanelKindList PanelPluginKind = "SigNozListPanel"
|
||||
)
|
||||
|
||||
type DatasourcePluginKind = string
|
||||
|
||||
const (
|
||||
DatasourceKindSigNoz DatasourcePluginKind = "SigNozDatasource"
|
||||
)
|
||||
|
||||
type TimeSeriesPanelSpec struct {
|
||||
Visualization TimeSeriesVisualization `json:"visualization"`
|
||||
Formatting PanelFormatting `json:"formatting"`
|
||||
ChartAppearance TimeSeriesChartAppearance `json:"chartAppearance"`
|
||||
Axes Axes `json:"axes"`
|
||||
Legend Legend `json:"legend"`
|
||||
Thresholds []ThresholdWithLabel `json:"thresholds" validate:"dive"`
|
||||
}
|
||||
|
||||
type TimeSeriesChartAppearance struct {
|
||||
LineInterpolation LineInterpolation `json:"lineInterpolation"`
|
||||
ShowPoints bool `json:"showPoints"`
|
||||
LineStyle LineStyle `json:"lineStyle"`
|
||||
FillMode FillMode `json:"fillMode"`
|
||||
SpanGaps SpanGaps `json:"spanGaps"`
|
||||
}
|
||||
|
||||
type BarChartPanelSpec struct {
|
||||
Visualization BarChartVisualization `json:"visualization"`
|
||||
Formatting PanelFormatting `json:"formatting"`
|
||||
Axes Axes `json:"axes"`
|
||||
Legend Legend `json:"legend"`
|
||||
Thresholds []ThresholdWithLabel `json:"thresholds" validate:"dive"`
|
||||
}
|
||||
|
||||
type NumberPanelSpec struct {
|
||||
Visualization BasicVisualization `json:"visualization"`
|
||||
Formatting PanelFormatting `json:"formatting"`
|
||||
Thresholds []ComparisonThreshold `json:"thresholds" validate:"dive"`
|
||||
}
|
||||
|
||||
type PieChartPanelSpec struct {
|
||||
Visualization BasicVisualization `json:"visualization"`
|
||||
Formatting PanelFormatting `json:"formatting"`
|
||||
Legend Legend `json:"legend"`
|
||||
}
|
||||
|
||||
type TablePanelSpec struct {
|
||||
Visualization BasicVisualization `json:"visualization"`
|
||||
Formatting TableFormatting `json:"formatting"`
|
||||
Thresholds []TableThreshold `json:"thresholds" validate:"dive"`
|
||||
}
|
||||
|
||||
type HistogramPanelSpec struct {
|
||||
HistogramBuckets HistogramBuckets `json:"histogramBuckets"`
|
||||
Legend Legend `json:"legend"`
|
||||
}
|
||||
|
||||
type HistogramBuckets struct {
|
||||
BucketCount *float64 `json:"bucketCount"`
|
||||
BucketWidth *float64 `json:"bucketWidth"`
|
||||
MergeAllActiveQueries bool `json:"mergeAllActiveQueries"`
|
||||
}
|
||||
|
||||
type ListPanelSpec struct {
|
||||
SelectedLogFields []LogField `json:"selectedLogFields" validate:"dive"`
|
||||
SelectedTracesFields []telemetrytypes.TelemetryFieldKey `json:"selectedTracesFields"`
|
||||
}
|
||||
|
||||
type LogField struct {
|
||||
Name string `json:"name" validate:"required" required:"true"`
|
||||
Type string `json:"type"`
|
||||
DataType string `json:"dataType"`
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════
|
||||
// Panel common types
|
||||
// ══════════════════════════════════════════════
|
||||
|
||||
type Axes struct {
|
||||
SoftMin *float64 `json:"softMin"`
|
||||
SoftMax *float64 `json:"softMax"`
|
||||
IsLogScale bool `json:"isLogScale"`
|
||||
}
|
||||
|
||||
type BasicVisualization struct {
|
||||
TimePreference TimePreference `json:"timePreference"`
|
||||
}
|
||||
|
||||
type TimeSeriesVisualization struct {
|
||||
BasicVisualization
|
||||
FillSpans bool `json:"fillSpans"`
|
||||
}
|
||||
|
||||
type BarChartVisualization struct {
|
||||
BasicVisualization
|
||||
FillSpans bool `json:"fillSpans"`
|
||||
StackedBarChart bool `json:"stackedBarChart"`
|
||||
}
|
||||
|
||||
type PanelFormatting struct {
|
||||
Unit string `json:"unit"`
|
||||
DecimalPrecision PrecisionOption `json:"decimalPrecision"`
|
||||
}
|
||||
|
||||
type TableFormatting struct {
|
||||
ColumnUnits map[string]string `json:"columnUnits"`
|
||||
DecimalPrecision PrecisionOption `json:"decimalPrecision"`
|
||||
}
|
||||
|
||||
type Legend struct {
|
||||
Position LegendPosition `json:"position"`
|
||||
CustomColors map[string]string `json:"customColors"`
|
||||
}
|
||||
|
||||
type ThresholdWithLabel struct {
|
||||
Value float64 `json:"value"`
|
||||
Unit string `json:"unit"`
|
||||
Color string `json:"color" validate:"required" required:"true"`
|
||||
Label string `json:"label" validate:"required" required:"true"`
|
||||
}
|
||||
|
||||
type ComparisonThreshold struct {
|
||||
Value float64 `json:"value"`
|
||||
Operator ComparisonOperator `json:"operator"`
|
||||
Unit string `json:"unit"`
|
||||
Color string `json:"color" validate:"required" required:"true"`
|
||||
Format ThresholdFormat `json:"format"`
|
||||
}
|
||||
|
||||
type TableThreshold struct {
|
||||
ComparisonThreshold
|
||||
ColumnName string `json:"columnName" validate:"required" required:"true"`
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════
|
||||
// Constrained scalar types (enum validation via custom UnmarshalJSON)
|
||||
// ══════════════════════════════════════════════
|
||||
|
||||
// LineInterpolation: "linear" | "spline" | "stepAfter" | "stepBefore". Default is "spline".
|
||||
type LineInterpolation struct {
|
||||
value string
|
||||
}
|
||||
|
||||
const (
|
||||
LineInterpolationLinear = "linear"
|
||||
LineInterpolationSpline = "spline"
|
||||
LineInterpolationStepAfter = "stepAfter"
|
||||
LineInterpolationStepBefore = "stepBefore"
|
||||
)
|
||||
|
||||
func (li LineInterpolation) Value() string {
|
||||
if li.value == "" {
|
||||
return LineInterpolationSpline
|
||||
}
|
||||
return li.value
|
||||
}
|
||||
|
||||
func (li *LineInterpolation) UnmarshalJSON(data []byte) error {
|
||||
var v string
|
||||
if err := json.Unmarshal(data, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
switch v {
|
||||
case LineInterpolationLinear, LineInterpolationSpline, LineInterpolationStepAfter, LineInterpolationStepBefore:
|
||||
li.value = v
|
||||
return nil
|
||||
default:
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "invalid line interpolation %q: must be linear, spline, stepAfter, or stepBefore", v)
|
||||
}
|
||||
}
|
||||
|
||||
func (li LineInterpolation) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(li.Value())
|
||||
}
|
||||
|
||||
// LineStyle: "solid" | "dashed". Default is "solid".
|
||||
type LineStyle struct {
|
||||
value string
|
||||
}
|
||||
|
||||
const (
|
||||
LineStyleSolid = "solid"
|
||||
LineStyleDashed = "dashed"
|
||||
)
|
||||
|
||||
func (ls LineStyle) Value() string {
|
||||
if ls.value == "" {
|
||||
return LineStyleSolid
|
||||
}
|
||||
return ls.value
|
||||
}
|
||||
|
||||
func (ls *LineStyle) UnmarshalJSON(data []byte) error {
|
||||
var v string
|
||||
if err := json.Unmarshal(data, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
switch v {
|
||||
case LineStyleSolid, LineStyleDashed:
|
||||
ls.value = v
|
||||
return nil
|
||||
default:
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "invalid line style %q: must be solid or dashed", v)
|
||||
}
|
||||
}
|
||||
|
||||
func (ls LineStyle) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(ls.Value())
|
||||
}
|
||||
|
||||
// FillMode: "solid" | "gradient" | "none". Default is "solid".
|
||||
type FillMode struct {
|
||||
value string
|
||||
}
|
||||
|
||||
const (
|
||||
FillModeSolid = "solid"
|
||||
FillModeGradient = "gradient"
|
||||
FillModeNone = "none"
|
||||
)
|
||||
|
||||
func (fm FillMode) Value() string {
|
||||
if fm.value == "" {
|
||||
return FillModeSolid
|
||||
}
|
||||
return fm.value
|
||||
}
|
||||
|
||||
func (fm *FillMode) UnmarshalJSON(data []byte) error {
|
||||
var v string
|
||||
if err := json.Unmarshal(data, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
switch v {
|
||||
case FillModeSolid, FillModeGradient, FillModeNone:
|
||||
fm.value = v
|
||||
return nil
|
||||
default:
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "invalid fill mode %q: must be solid, gradient, or none", v)
|
||||
}
|
||||
}
|
||||
|
||||
func (fm FillMode) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(fm.Value())
|
||||
}
|
||||
|
||||
// TimePreference: "globalTime" | "last5Min" | "last15Min" | "last30Min" | "last1Hr" | "last6Hr" | "last1Day" | "last3Days" | "last1Week" | "last1Month".
|
||||
type TimePreference string
|
||||
|
||||
const (
|
||||
TimePreferenceGlobalTime TimePreference = "globalTime"
|
||||
TimePreferenceLast5Min TimePreference = "last5Min"
|
||||
TimePreferenceLast15Min TimePreference = "last15Min"
|
||||
TimePreferenceLast30Min TimePreference = "last30Min"
|
||||
TimePreferenceLast1Hr TimePreference = "last1Hr"
|
||||
TimePreferenceLast6Hr TimePreference = "last6Hr"
|
||||
TimePreferenceLast1Day TimePreference = "last1Day"
|
||||
TimePreferenceLast3Days TimePreference = "last3Days"
|
||||
TimePreferenceLast1Week TimePreference = "last1Week"
|
||||
TimePreferenceLast1Month TimePreference = "last1Month"
|
||||
)
|
||||
|
||||
func (t *TimePreference) UnmarshalJSON(data []byte) error {
|
||||
var v string
|
||||
if err := json.Unmarshal(data, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
switch TimePreference(v) {
|
||||
case TimePreferenceGlobalTime, TimePreferenceLast5Min, TimePreferenceLast15Min, TimePreferenceLast30Min, TimePreferenceLast1Hr, TimePreferenceLast6Hr, TimePreferenceLast1Day, TimePreferenceLast3Days, TimePreferenceLast1Week, TimePreferenceLast1Month:
|
||||
*t = TimePreference(v)
|
||||
return nil
|
||||
default:
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "invalid timePreference %q", v)
|
||||
}
|
||||
}
|
||||
|
||||
// LegendPosition: "bottom" | "right".
|
||||
type LegendPosition string
|
||||
|
||||
const (
|
||||
LegendPositionBottom LegendPosition = "bottom"
|
||||
LegendPositionRight LegendPosition = "right"
|
||||
)
|
||||
|
||||
func (l *LegendPosition) UnmarshalJSON(data []byte) error {
|
||||
var v string
|
||||
if err := json.Unmarshal(data, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
switch LegendPosition(v) {
|
||||
case LegendPositionBottom, LegendPositionRight:
|
||||
*l = LegendPosition(v)
|
||||
return nil
|
||||
default:
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "invalid legend position %q: must be bottom or right", v)
|
||||
}
|
||||
}
|
||||
|
||||
// ThresholdFormat: "Text" | "Background".
|
||||
type ThresholdFormat string
|
||||
|
||||
const (
|
||||
ThresholdFormatText ThresholdFormat = "Text"
|
||||
ThresholdFormatBackground ThresholdFormat = "Background"
|
||||
)
|
||||
|
||||
func (f *ThresholdFormat) UnmarshalJSON(data []byte) error {
|
||||
var v string
|
||||
if err := json.Unmarshal(data, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
switch ThresholdFormat(v) {
|
||||
case ThresholdFormatText, ThresholdFormatBackground:
|
||||
*f = ThresholdFormat(v)
|
||||
return nil
|
||||
default:
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "invalid threshold format %q: must be Text or Background", v)
|
||||
}
|
||||
}
|
||||
|
||||
// ComparisonOperator: ">" | "<" | ">=" | "<=" | "=".
|
||||
type ComparisonOperator string
|
||||
|
||||
const (
|
||||
ComparisonOperatorGT ComparisonOperator = ">"
|
||||
ComparisonOperatorLT ComparisonOperator = "<"
|
||||
ComparisonOperatorGTE ComparisonOperator = ">="
|
||||
ComparisonOperatorLTE ComparisonOperator = "<="
|
||||
ComparisonOperatorEQ ComparisonOperator = "="
|
||||
)
|
||||
|
||||
func (o *ComparisonOperator) UnmarshalJSON(data []byte) error {
|
||||
var v string
|
||||
if err := json.Unmarshal(data, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
switch ComparisonOperator(v) {
|
||||
case ComparisonOperatorGT, ComparisonOperatorLT, ComparisonOperatorGTE, ComparisonOperatorLTE, ComparisonOperatorEQ:
|
||||
*o = ComparisonOperator(v)
|
||||
return nil
|
||||
default:
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "invalid comparison operator %q", v)
|
||||
}
|
||||
}
|
||||
|
||||
// SpanGaps: bool | number. Default is true.
|
||||
// When true, lines connect across null values. When false, lines break at nulls.
|
||||
// When a number, gaps smaller than that threshold (in seconds) are connected.
|
||||
type SpanGaps struct {
|
||||
value any
|
||||
}
|
||||
|
||||
func (sg SpanGaps) Value() any {
|
||||
if sg.value == nil {
|
||||
return true
|
||||
}
|
||||
return sg.value
|
||||
}
|
||||
|
||||
func (sg *SpanGaps) UnmarshalJSON(data []byte) error {
|
||||
var b bool
|
||||
if err := json.Unmarshal(data, &b); err == nil {
|
||||
sg.value = b
|
||||
return nil
|
||||
}
|
||||
var n float64
|
||||
if err := json.Unmarshal(data, &n); err == nil {
|
||||
if n < 0 {
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "invalid spanGaps %v: numeric value must be non-negative", n)
|
||||
}
|
||||
sg.value = n
|
||||
return nil
|
||||
}
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "invalid spanGaps: must be a bool or a non-negative number")
|
||||
}
|
||||
|
||||
func (sg SpanGaps) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(sg.Value())
|
||||
}
|
||||
|
||||
// PrecisionOption: 0 | 1 | 2 | 3 | 4 | "full". Default is 2.
|
||||
type PrecisionOption struct {
|
||||
value any
|
||||
}
|
||||
|
||||
const PrecisionOptionFull = "full"
|
||||
|
||||
func (p PrecisionOption) Value() any {
|
||||
if p.value == nil {
|
||||
return 2
|
||||
}
|
||||
return p.value
|
||||
}
|
||||
|
||||
func (p *PrecisionOption) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err == nil {
|
||||
if s != PrecisionOptionFull {
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "invalid precision option %q: string value must be %q", s, PrecisionOptionFull)
|
||||
}
|
||||
p.value = s
|
||||
return nil
|
||||
}
|
||||
var n int
|
||||
if err := json.Unmarshal(data, &n); err == nil {
|
||||
switch n {
|
||||
case 0, 1, 2, 3, 4:
|
||||
p.value = n
|
||||
return nil
|
||||
default:
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "invalid precision option %d: must be 0, 1, 2, 3, or 4", n)
|
||||
}
|
||||
}
|
||||
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "invalid precision option: must be an int (0-4) or \"full\"")
|
||||
}
|
||||
|
||||
func (p PrecisionOption) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(p.Value())
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user