diff --git a/.devenv/docker/clickhouse/compose.yaml b/.devenv/docker/clickhouse/compose.yaml index e6cef658c86..51c76c0b469 100644 --- a/.devenv/docker/clickhouse/compose.yaml +++ b/.devenv/docker/clickhouse/compose.yaml @@ -27,8 +27,8 @@ services: - ${PWD}/fs/tmp/var/lib/clickhouse/user_scripts/:/var/lib/clickhouse/user_scripts/ - ${PWD}/../../../deploy/common/clickhouse/custom-function.xml:/etc/clickhouse-server/custom-function.xml ports: - - '127.0.0.1:8123:8123' - - '127.0.0.1:9000:9000' + - "127.0.0.1:8123:8123" + - "127.0.0.1:9000:9000" tty: true healthcheck: test: @@ -47,13 +47,16 @@ services: condition: service_healthy environment: - CLICKHOUSE_SKIP_USER_SETUP=1 + networks: + - default + - signoz-devenv zookeeper: image: signoz/zookeeper:3.7.1 container_name: zookeeper volumes: - ${PWD}/fs/tmp/zookeeper:/bitnami/zookeeper ports: - - '127.0.0.1:2181:2181' + - "127.0.0.1:2181:2181" environment: - ALLOW_ANONYMOUS_LOGIN=yes healthcheck: @@ -74,12 +77,19 @@ services: entrypoint: - /bin/sh command: - - -c - - | - /signoz-otel-collector migrate bootstrap && - /signoz-otel-collector migrate sync up && - /signoz-otel-collector migrate async up + - -c + - | + /signoz-otel-collector migrate bootstrap && + /signoz-otel-collector migrate sync up && + /signoz-otel-collector migrate async up depends_on: clickhouse: condition: service_healthy restart: on-failure + networks: + - default + - signoz-devenv + +networks: + signoz-devenv: + name: signoz-devenv diff --git a/.devenv/docker/signoz-otel-collector/compose.yaml b/.devenv/docker/signoz-otel-collector/compose.yaml index 46e9ba60e8d..23e67735ccd 100644 --- a/.devenv/docker/signoz-otel-collector/compose.yaml +++ b/.devenv/docker/signoz-otel-collector/compose.yaml @@ -3,7 +3,7 @@ services: image: signoz/signoz-otel-collector:v0.142.0 container_name: signoz-otel-collector-dev entrypoint: - - /bin/sh + - /bin/sh command: - -c - | @@ -34,4 +34,11 @@ services: retries: 3 restart: unless-stopped extra_hosts: - - "host.docker.internal:host-gateway" \ No newline at end of file + - "host.docker.internal:host-gateway" + networks: + - default + - signoz-devenv + +networks: + signoz-devenv: + name: signoz-devenv diff --git a/.devenv/docker/signoz-otel-collector/otel-collector-config.yaml b/.devenv/docker/signoz-otel-collector/otel-collector-config.yaml index 43a888fffb7..349f689304a 100644 --- a/.devenv/docker/signoz-otel-collector/otel-collector-config.yaml +++ b/.devenv/docker/signoz-otel-collector/otel-collector-config.yaml @@ -12,10 +12,10 @@ receivers: scrape_configs: - job_name: otel-collector static_configs: - - targets: - - localhost:8888 - labels: - job_name: otel-collector + - targets: + - localhost:8888 + labels: + job_name: otel-collector processors: batch: @@ -29,7 +29,26 @@ processors: signozspanmetrics/delta: metrics_exporter: signozclickhousemetrics metrics_flush_interval: 60s - latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ] + latency_histogram_buckets: + [ + 100us, + 1ms, + 2ms, + 6ms, + 10ms, + 50ms, + 100ms, + 250ms, + 500ms, + 1000ms, + 1400ms, + 2000ms, + 5s, + 10s, + 20s, + 40s, + 60s, + ] dimensions_cache_size: 100000 aggregation_temporality: AGGREGATION_TEMPORALITY_DELTA enable_exp_histogram: true @@ -60,13 +79,13 @@ extensions: exporters: clickhousetraces: - datasource: tcp://host.docker.internal:9000/signoz_traces + datasource: tcp://clickhouse:9000/signoz_traces low_cardinal_exception_grouping: ${env:LOW_CARDINAL_EXCEPTION_GROUPING} use_new_schema: true signozclickhousemetrics: - dsn: tcp://host.docker.internal:9000/signoz_metrics + dsn: tcp://clickhouse:9000/signoz_metrics clickhouselogsexporter: - dsn: tcp://host.docker.internal:9000/signoz_logs + dsn: tcp://clickhouse:9000/signoz_logs timeout: 10s use_new_schema: true @@ -93,4 +112,4 @@ service: logs: receivers: [otlp] processors: [batch] - exporters: [clickhouselogsexporter] \ No newline at end of file + exporters: [clickhouselogsexporter] diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index eddd744ef57..6d549b1731c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -86,6 +86,8 @@ go.mod @therealpandey /pkg/types/alertmanagertypes @srikanthccv /pkg/alertmanager/ @srikanthccv /pkg/ruler/ @srikanthccv +/pkg/modules/rulestatehistory/ @srikanthccv +/pkg/types/rulestatehistorytypes/ @srikanthccv # Correlation-adjacent @@ -105,7 +107,7 @@ go.mod @therealpandey /pkg/modules/authdomain/ @vikrantgupta25 /pkg/modules/role/ @vikrantgupta25 -# IdentN Owners +# IdentN Owners /pkg/identn/ @vikrantgupta25 /pkg/http/middleware/identn.go @vikrantgupta25 diff --git a/.github/workflows/integrationci.yaml b/.github/workflows/integrationci.yaml index 5046aa796f8..59c04986965 100644 --- a/.github/workflows/integrationci.yaml +++ b/.github/workflows/integrationci.yaml @@ -51,6 +51,7 @@ jobs: - alerts - ingestionkeys - rootuser + - serviceaccount sqlstore-provider: - postgres - sqlite diff --git a/.golangci.yml b/.golangci.yml index 5d801f24b47..40d071b12f1 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -6,12 +6,14 @@ linters: - depguard - errcheck - forbidigo + - godot - govet - iface - ineffassign - misspell - nilnil - sloglint + - staticcheck - wastedassign - unparam - unused @@ -35,7 +37,7 @@ linters: - identical sloglint: no-mixed-args: true - kv-only: true + attr-only: true no-global: all context: all static-msg: true diff --git a/.vscode/settings.json b/.vscode/settings.json index 1720c91e72c..d394991d03b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,5 +17,7 @@ }, "[html]": { "editor.defaultFormatter": "vscode.html-language-features" - } + }, + "python-envs.defaultEnvManager": "ms-python.python:system", + "python-envs.pythonProjects": [] } diff --git a/cmd/community/server.go b/cmd/community/server.go index b0b7425ee25..1dd073a1a23 100644 --- a/cmd/community/server.go +++ b/cmd/community/server.go @@ -4,12 +4,16 @@ import ( "context" "log/slog" + "github.com/spf13/cobra" + "github.com/SigNoz/signoz/cmd" "github.com/SigNoz/signoz/pkg/analytics" "github.com/SigNoz/signoz/pkg/authn" "github.com/SigNoz/signoz/pkg/authz" "github.com/SigNoz/signoz/pkg/authz/openfgaauthz" "github.com/SigNoz/signoz/pkg/authz/openfgaschema" + "github.com/SigNoz/signoz/pkg/authz/openfgaserver" + "github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/factory" "github.com/SigNoz/signoz/pkg/gateway" "github.com/SigNoz/signoz/pkg/gateway/noopgateway" @@ -28,7 +32,6 @@ import ( "github.com/SigNoz/signoz/pkg/version" "github.com/SigNoz/signoz/pkg/zeus" "github.com/SigNoz/signoz/pkg/zeus/noopzeus" - "github.com/spf13/cobra" ) func registerServer(parentCmd *cobra.Command, logger *slog.Logger) { @@ -76,8 +79,13 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e func(ctx context.Context, providerSettings factory.ProviderSettings, store authtypes.AuthNStore, licensing licensing.Licensing) (map[authtypes.AuthNProvider]authn.AuthN, error) { return signoz.NewAuthNs(ctx, providerSettings, store, licensing) }, - func(ctx context.Context, sqlstore sqlstore.SQLStore, _ licensing.Licensing, _ dashboard.Module) factory.ProviderFactory[authz.AuthZ, authz.Config] { - return openfgaauthz.NewProviderFactory(sqlstore, openfgaschema.NewSchema().Get(ctx)) + func(ctx context.Context, sqlstore sqlstore.SQLStore, _ licensing.Licensing, _ dashboard.Module) (factory.ProviderFactory[authz.AuthZ, authz.Config], error) { + openfgaDataStore, err := openfgaserver.NewSQLStore(sqlstore) + if err != nil { + return nil, err + } + + return openfgaauthz.NewProviderFactory(sqlstore, openfgaschema.NewSchema().Get(ctx), openfgaDataStore), nil }, func(store sqlstore.SQLStore, settings factory.ProviderSettings, analytics analytics.Analytics, orgGetter organization.Getter, queryParser queryparser.QueryParser, _ querier.Querier, _ licensing.Licensing) dashboard.Module { return impldashboard.NewModule(impldashboard.NewStore(store), settings, analytics, orgGetter, queryParser) @@ -90,37 +98,37 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e }, ) if err != nil { - logger.ErrorContext(ctx, "failed to create signoz", "error", err) + logger.ErrorContext(ctx, "failed to create signoz", errors.Attr(err)) return err } server, err := app.NewServer(config, signoz) if err != nil { - logger.ErrorContext(ctx, "failed to create server", "error", err) + logger.ErrorContext(ctx, "failed to create server", errors.Attr(err)) return err } if err := server.Start(ctx); err != nil { - logger.ErrorContext(ctx, "failed to start server", "error", err) + logger.ErrorContext(ctx, "failed to start server", errors.Attr(err)) return err } signoz.Start(ctx) if err := signoz.Wait(ctx); err != nil { - logger.ErrorContext(ctx, "failed to start signoz", "error", err) + logger.ErrorContext(ctx, "failed to start signoz", errors.Attr(err)) return err } err = server.Stop(ctx) if err != nil { - logger.ErrorContext(ctx, "failed to stop server", "error", err) + logger.ErrorContext(ctx, "failed to stop server", errors.Attr(err)) return err } err = signoz.Stop(ctx) if err != nil { - logger.ErrorContext(ctx, "failed to stop signoz", "error", err) + logger.ErrorContext(ctx, "failed to stop signoz", errors.Attr(err)) return err } diff --git a/cmd/enterprise/server.go b/cmd/enterprise/server.go index d0e28da307a..fc03fd7c5fe 100644 --- a/cmd/enterprise/server.go +++ b/cmd/enterprise/server.go @@ -5,16 +5,19 @@ import ( "log/slog" "time" + "github.com/spf13/cobra" + "github.com/SigNoz/signoz/cmd" "github.com/SigNoz/signoz/ee/authn/callbackauthn/oidccallbackauthn" "github.com/SigNoz/signoz/ee/authn/callbackauthn/samlcallbackauthn" "github.com/SigNoz/signoz/ee/authz/openfgaauthz" - eequerier "github.com/SigNoz/signoz/ee/querier" "github.com/SigNoz/signoz/ee/authz/openfgaschema" + "github.com/SigNoz/signoz/ee/authz/openfgaserver" "github.com/SigNoz/signoz/ee/gateway/httpgateway" enterpriselicensing "github.com/SigNoz/signoz/ee/licensing" "github.com/SigNoz/signoz/ee/licensing/httplicensing" "github.com/SigNoz/signoz/ee/modules/dashboard/impldashboard" + eequerier "github.com/SigNoz/signoz/ee/querier" enterpriseapp "github.com/SigNoz/signoz/ee/query-service/app" "github.com/SigNoz/signoz/ee/sqlschema/postgressqlschema" "github.com/SigNoz/signoz/ee/sqlstore/postgressqlstore" @@ -23,6 +26,7 @@ import ( "github.com/SigNoz/signoz/pkg/analytics" "github.com/SigNoz/signoz/pkg/authn" "github.com/SigNoz/signoz/pkg/authz" + "github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/factory" "github.com/SigNoz/signoz/pkg/gateway" "github.com/SigNoz/signoz/pkg/licensing" @@ -38,7 +42,6 @@ import ( "github.com/SigNoz/signoz/pkg/types/authtypes" "github.com/SigNoz/signoz/pkg/version" "github.com/SigNoz/signoz/pkg/zeus" - "github.com/spf13/cobra" ) func registerServer(parentCmd *cobra.Command, logger *slog.Logger) { @@ -69,7 +72,7 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e // add enterprise sqlstore factories to the community sqlstore factories sqlstoreFactories := signoz.NewSQLStoreProviderFactories() if err := sqlstoreFactories.Add(postgressqlstore.NewFactory(sqlstorehook.NewLoggingFactory(), sqlstorehook.NewInstrumentationFactory())); err != nil { - logger.ErrorContext(ctx, "failed to add postgressqlstore factory", "error", err) + logger.ErrorContext(ctx, "failed to add postgressqlstore factory", errors.Attr(err)) return err } @@ -116,8 +119,13 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e return authNs, nil }, - func(ctx context.Context, sqlstore sqlstore.SQLStore, licensing licensing.Licensing, dashboardModule dashboard.Module) factory.ProviderFactory[authz.AuthZ, authz.Config] { - return openfgaauthz.NewProviderFactory(sqlstore, openfgaschema.NewSchema().Get(ctx), licensing, dashboardModule) + func(ctx context.Context, sqlstore sqlstore.SQLStore, licensing licensing.Licensing, dashboardModule dashboard.Module) (factory.ProviderFactory[authz.AuthZ, authz.Config], error) { + openfgaDataStore, err := openfgaserver.NewSQLStore(sqlstore) + if err != nil { + return nil, err + } + return openfgaauthz.NewProviderFactory(sqlstore, openfgaschema.NewSchema().Get(ctx), openfgaDataStore, licensing, dashboardModule), nil + }, func(store sqlstore.SQLStore, settings factory.ProviderSettings, analytics analytics.Analytics, orgGetter organization.Getter, queryParser queryparser.QueryParser, querier querier.Querier, licensing licensing.Licensing) dashboard.Module { return impldashboard.NewModule(pkgimpldashboard.NewStore(store), settings, analytics, orgGetter, queryParser, querier, licensing) @@ -132,37 +140,37 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e ) if err != nil { - logger.ErrorContext(ctx, "failed to create signoz", "error", err) + logger.ErrorContext(ctx, "failed to create signoz", errors.Attr(err)) return err } server, err := enterpriseapp.NewServer(config, signoz) if err != nil { - logger.ErrorContext(ctx, "failed to create server", "error", err) + logger.ErrorContext(ctx, "failed to create server", errors.Attr(err)) return err } if err := server.Start(ctx); err != nil { - logger.ErrorContext(ctx, "failed to start server", "error", err) + logger.ErrorContext(ctx, "failed to start server", errors.Attr(err)) return err } signoz.Start(ctx) if err := signoz.Wait(ctx); err != nil { - logger.ErrorContext(ctx, "failed to start signoz", "error", err) + logger.ErrorContext(ctx, "failed to start signoz", errors.Attr(err)) return err } err = server.Stop(ctx) if err != nil { - logger.ErrorContext(ctx, "failed to stop server", "error", err) + logger.ErrorContext(ctx, "failed to stop server", errors.Attr(err)) return err } err = signoz.Stop(ctx) if err != nil { - logger.ErrorContext(ctx, "failed to stop signoz", "error", err) + logger.ErrorContext(ctx, "failed to stop signoz", errors.Attr(err)) return err } diff --git a/cmd/root.go b/cmd/root.go index 9e7aee7b04f..e601aa513eb 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -4,8 +4,10 @@ import ( "log/slog" "os" - "github.com/SigNoz/signoz/pkg/version" "github.com/spf13/cobra" + + "github.com/SigNoz/signoz/pkg/errors" + "github.com/SigNoz/signoz/pkg/version" ) var RootCmd = &cobra.Command{ @@ -20,7 +22,7 @@ var RootCmd = &cobra.Command{ func Execute(logger *slog.Logger) { err := RootCmd.Execute() if err != nil { - logger.ErrorContext(RootCmd.Context(), "error running command", "error", err) + logger.ErrorContext(RootCmd.Context(), "error running command", errors.Attr(err)) os.Exit(1) } } diff --git a/conf/cache-config.yml b/conf/cache-config.yml deleted file mode 100644 index b1bd329584c..00000000000 --- a/conf/cache-config.yml +++ /dev/null @@ -1,4 +0,0 @@ -provider: "inmemory" -inmemory: - ttl: 60m - cleanupInterval: 10m diff --git a/conf/example.yaml b/conf/example.yaml index 4bce8dc3e6a..77fe4fb8852 100644 --- a/conf/example.yaml +++ b/conf/example.yaml @@ -39,6 +39,13 @@ instrumentation: host: "0.0.0.0" port: 9090 +##################### PProf ##################### +pprof: + # Whether to enable the pprof server. + enabled: true + # The address on which the pprof server listens. + address: 0.0.0.0:6060 + ##################### Web ##################### web: # Whether to enable the web frontend @@ -78,10 +85,12 @@ sqlstore: sqlite: # The path to the SQLite database file. path: /var/lib/signoz/signoz.db - # Mode is the mode to use for the sqlite database. + # The journal mode for the sqlite database. Supported values: delete, wal. mode: delete - # BusyTimeout is the timeout for the sqlite database to wait for a lock. + # The timeout for the sqlite database to wait for a lock. busy_timeout: 10s + # The default transaction locking behavior. Supported values: deferred, immediate, exclusive. + transaction_mode: deferred ##################### APIServer ##################### apiserver: @@ -137,6 +146,8 @@ telemetrystore: ##################### Prometheus ##################### prometheus: + # The maximum time a PromQL query is allowed to run before being aborted. + timeout: 2m active_query_tracker: # Whether to enable the active query tracker. enabled: true @@ -343,3 +354,13 @@ identn: impersonation: # toggle impersonation identN, when enabled, all requests will impersonate the root user enabled: false + +##################### Service Account ##################### +serviceaccount: + email: + # email domain for the service account principal + domain: signozserviceaccount.com + + analytics: + # toggle service account analytics + enabled: true diff --git a/conf/prometheus.yml b/conf/prometheus.yml deleted file mode 100644 index 6513cd0c3f8..00000000000 --- a/conf/prometheus.yml +++ /dev/null @@ -1,25 +0,0 @@ -# my global config -global: - scrape_interval: 5s # Set the scrape interval to every 15 seconds. Default is every 1 minute. - evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. - # scrape_timeout is set to the global default (10s). - -# Alertmanager configuration -alerting: - alertmanagers: - - static_configs: - - targets: - - 127.0.0.1:9093 - -# Load rules once and periodically evaluate them according to the global 'evaluation_interval'. -rule_files: - # - "first_rules.yml" - # - "second_rules.yml" - - 'alerts.yml' - -# A scrape configuration containing exactly one endpoint to scrape: -# Here it's Prometheus itself. -scrape_configs: [] - -remote_read: - - url: tcp://localhost:9000/signoz_metrics diff --git a/deploy/docker-swarm/docker-compose.ha.yaml b/deploy/docker-swarm/docker-compose.ha.yaml index 6751af436c7..4cc5e6380ac 100644 --- a/deploy/docker-swarm/docker-compose.ha.yaml +++ b/deploy/docker-swarm/docker-compose.ha.yaml @@ -190,7 +190,7 @@ services: # - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml signoz: !!merge <<: *db-depend - image: signoz/signoz:v0.116.1 + image: signoz/signoz:v0.117.1 ports: - "8080:8080" # signoz port # - "6060:6060" # pprof port diff --git a/deploy/docker-swarm/docker-compose.yaml b/deploy/docker-swarm/docker-compose.yaml index 69a6024f122..21ab7f2a068 100644 --- a/deploy/docker-swarm/docker-compose.yaml +++ b/deploy/docker-swarm/docker-compose.yaml @@ -117,7 +117,7 @@ services: # - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml signoz: !!merge <<: *db-depend - image: signoz/signoz:v0.116.1 + image: signoz/signoz:v0.117.1 ports: - "8080:8080" # signoz port volumes: diff --git a/deploy/docker/docker-compose.ha.yaml b/deploy/docker/docker-compose.ha.yaml index c274aef522c..78d1bd45d26 100644 --- a/deploy/docker/docker-compose.ha.yaml +++ b/deploy/docker/docker-compose.ha.yaml @@ -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.116.1} + image: signoz/signoz:${VERSION:-v0.117.1} container_name: signoz ports: - "8080:8080" # signoz port diff --git a/deploy/docker/docker-compose.yaml b/deploy/docker/docker-compose.yaml index 3c1f7140ba5..3ef03e09e23 100644 --- a/deploy/docker/docker-compose.yaml +++ b/deploy/docker/docker-compose.yaml @@ -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.116.1} + image: signoz/signoz:${VERSION:-v0.117.1} container_name: signoz ports: - "8080:8080" # signoz port diff --git a/docs/api/openapi.yml b/docs/api/openapi.yml index ca8e219a226..6a0950ab122 100644 --- a/docs/api/openapi.yml +++ b/docs/api/openapi.yml @@ -327,6 +327,27 @@ components: nullable: true type: array type: object + AuthtypesStorableRole: + properties: + createdAt: + format: date-time + type: string + description: + type: string + id: + type: string + name: + type: string + orgId: + type: string + type: + type: string + updatedAt: + format: date-time + type: string + required: + - id + type: object AuthtypesTransaction: properties: object: @@ -342,1645 +363,1404 @@ components: config: $ref: '#/components/schemas/AuthtypesAuthDomainConfig' type: object - DashboardtypesDashboard: + AuthtypesUserRole: properties: createdAt: format: date-time type: string - createdBy: - type: string - data: - $ref: '#/components/schemas/DashboardtypesStorableDashboardData' id: type: string - locked: - type: boolean - org_id: + role: + $ref: '#/components/schemas/AuthtypesStorableRole' + roleId: type: string updatedAt: format: date-time type: string - updatedBy: + userId: type: string + required: + - id type: object - DashboardtypesGettablePublicDasbhboard: + AuthtypesUserWithRoles: properties: - defaultTimeRange: + createdAt: + format: date-time type: string - publicPath: + displayName: type: string - timeRangeEnabled: - type: boolean - type: object - DashboardtypesGettablePublicDashboardData: - properties: - dashboard: - $ref: '#/components/schemas/DashboardtypesDashboard' - publicDashboard: - $ref: '#/components/schemas/DashboardtypesGettablePublicDasbhboard' - type: object - DashboardtypesPostablePublicDashboard: - properties: - defaultTimeRange: + email: type: string - timeRangeEnabled: - type: boolean - type: object - DashboardtypesStorableDashboardData: - additionalProperties: {} - type: object - DashboardtypesUpdatablePublicDashboard: - properties: - defaultTimeRange: + id: type: string - timeRangeEnabled: + isRoot: type: boolean - type: object - ErrorsJSON: - properties: - code: + orgId: type: string - errors: - items: - $ref: '#/components/schemas/ErrorsResponseerroradditional' - type: array - message: + status: type: string - url: + updatedAt: + format: date-time type: string + userRoles: + items: + $ref: '#/components/schemas/AuthtypesUserRole' + nullable: true + type: array required: - - code - - message + - id type: object - ErrorsResponseerroradditional: + CloudintegrationtypesAWSAccountConfig: properties: - message: - type: string + regions: + items: + type: string + type: array + required: + - regions type: object - FeaturetypesGettableFeature: + CloudintegrationtypesAWSCollectionStrategy: properties: - defaultVariant: - type: string - description: - type: string - kind: - type: string - name: - type: string - resolvedValue: {} - stage: - type: string - variants: - additionalProperties: {} - nullable: true + aws_logs: + $ref: '#/components/schemas/CloudintegrationtypesAWSLogsStrategy' + aws_metrics: + $ref: '#/components/schemas/CloudintegrationtypesAWSMetricsStrategy' + s3_buckets: + additionalProperties: + items: + type: string + type: array type: object type: object - GatewaytypesGettableCreatedIngestionKey: + CloudintegrationtypesAWSConnectionArtifact: properties: - id: - type: string - value: + connectionURL: type: string required: - - id - - value + - connectionURL type: object - GatewaytypesGettableCreatedIngestionKeyLimit: + CloudintegrationtypesAWSConnectionArtifactRequest: properties: - id: + deploymentRegion: type: string + regions: + items: + type: string + type: array required: - - id + - deploymentRegion + - regions type: object - GatewaytypesGettableIngestionKeys: + CloudintegrationtypesAWSIntegrationConfig: properties: - _pagination: - $ref: '#/components/schemas/GatewaytypesPagination' - keys: + enabledRegions: items: - $ref: '#/components/schemas/GatewaytypesIngestionKey' - nullable: true + type: string type: array + telemetry: + $ref: '#/components/schemas/CloudintegrationtypesAWSCollectionStrategy' + required: + - enabledRegions + - telemetry type: object - GatewaytypesIngestionKey: + CloudintegrationtypesAWSLogsStrategy: properties: - created_at: - format: date-time - type: string - expires_at: - format: date-time - type: string - id: - type: string - limits: + cloudwatch_logs_subscriptions: items: - $ref: '#/components/schemas/GatewaytypesLimit' + properties: + filter_pattern: + type: string + log_group_name_prefix: + type: string + type: object nullable: true type: array - name: - type: string - tags: + type: object + CloudintegrationtypesAWSMetricsStrategy: + properties: + cloudwatch_metric_stream_filters: items: - type: string + properties: + MetricNames: + items: + type: string + type: array + Namespace: + type: string + type: object nullable: true type: array - updated_at: - format: date-time - type: string - value: - type: string - workspace_id: - type: string type: object - GatewaytypesLimit: + CloudintegrationtypesAWSServiceConfig: properties: + logs: + $ref: '#/components/schemas/CloudintegrationtypesAWSServiceLogsConfig' + metrics: + $ref: '#/components/schemas/CloudintegrationtypesAWSServiceMetricsConfig' + type: object + CloudintegrationtypesAWSServiceLogsConfig: + properties: + enabled: + type: boolean + s3_buckets: + additionalProperties: + items: + type: string + type: array + type: object + type: object + CloudintegrationtypesAWSServiceMetricsConfig: + properties: + enabled: + type: boolean + type: object + CloudintegrationtypesAccount: + properties: + agentReport: + $ref: '#/components/schemas/CloudintegrationtypesAgentReport' config: - $ref: '#/components/schemas/GatewaytypesLimitConfig' - created_at: + $ref: '#/components/schemas/CloudintegrationtypesAccountConfig' + createdAt: format: date-time type: string id: type: string - key_id: + orgId: type: string - metric: - $ref: '#/components/schemas/GatewaytypesLimitMetric' - signal: + provider: type: string - tags: - items: - type: string + providerAccountId: nullable: true - type: array - updated_at: + type: string + removedAt: + format: date-time + nullable: true + type: string + updatedAt: format: date-time type: string + required: + - id + - providerAccountId + - provider + - removedAt + - agentReport + - orgId + - config type: object - GatewaytypesLimitConfig: - properties: - day: - $ref: '#/components/schemas/GatewaytypesLimitValue' - second: - $ref: '#/components/schemas/GatewaytypesLimitValue' - type: object - GatewaytypesLimitMetric: + CloudintegrationtypesAccountConfig: properties: - day: - $ref: '#/components/schemas/GatewaytypesLimitMetricValue' - second: - $ref: '#/components/schemas/GatewaytypesLimitMetricValue' + aws: + $ref: '#/components/schemas/CloudintegrationtypesAWSAccountConfig' + required: + - aws type: object - GatewaytypesLimitMetricValue: + CloudintegrationtypesAgentReport: + nullable: true properties: - count: - format: int64 - type: integer - size: + data: + additionalProperties: {} + nullable: true + type: object + timestampMillis: format: int64 type: integer + required: + - timestampMillis + - data type: object - GatewaytypesLimitValue: + CloudintegrationtypesAssets: properties: - count: - nullable: true - type: integer - size: + dashboards: + items: + $ref: '#/components/schemas/CloudintegrationtypesDashboard' nullable: true - type: integer + type: array type: object - GatewaytypesPagination: + CloudintegrationtypesCollectedLogAttribute: properties: - page: - type: integer - pages: - type: integer - per_page: - type: integer - total: - type: integer + name: + type: string + path: + type: string + type: + type: string type: object - GatewaytypesPostableIngestionKey: + CloudintegrationtypesCollectedMetric: properties: - expires_at: - format: date-time + description: type: string name: type: string - tags: - items: - type: string - nullable: true - type: array - required: - - name - type: object - GatewaytypesPostableIngestionKeyLimit: - properties: - config: - $ref: '#/components/schemas/GatewaytypesLimitConfig' - signal: - type: string - tags: - items: - type: string - nullable: true - type: array - type: object - GatewaytypesUpdatableIngestionKeyLimit: - properties: - config: - $ref: '#/components/schemas/GatewaytypesLimitConfig' - tags: - items: - type: string - nullable: true - type: array - required: - - config - type: object - GlobaltypesAPIKeyConfig: - properties: - enabled: - type: boolean - type: object - GlobaltypesConfig: - properties: - external_url: + type: type: string - identN: - $ref: '#/components/schemas/GlobaltypesIdentNConfig' - ingestion_url: + unit: type: string type: object - GlobaltypesIdentNConfig: + CloudintegrationtypesCollectionStrategy: properties: - apikey: - $ref: '#/components/schemas/GlobaltypesAPIKeyConfig' - impersonation: - $ref: '#/components/schemas/GlobaltypesImpersonationConfig' - tokenizer: - $ref: '#/components/schemas/GlobaltypesTokenizerConfig' + aws: + $ref: '#/components/schemas/CloudintegrationtypesAWSCollectionStrategy' + required: + - aws type: object - GlobaltypesImpersonationConfig: + CloudintegrationtypesConnectionArtifact: properties: - enabled: - type: boolean + aws: + $ref: '#/components/schemas/CloudintegrationtypesAWSConnectionArtifact' + required: + - aws type: object - GlobaltypesTokenizerConfig: + CloudintegrationtypesConnectionArtifactRequest: properties: - enabled: - type: boolean + aws: + $ref: '#/components/schemas/CloudintegrationtypesAWSConnectionArtifactRequest' + required: + - aws type: object - MetricsexplorertypesListMetric: + CloudintegrationtypesDashboard: properties: + definition: + $ref: '#/components/schemas/DashboardtypesStorableDashboardData' description: type: string - isMonotonic: - type: boolean - metricName: + id: type: string - temporality: - $ref: '#/components/schemas/MetrictypesTemporality' - type: - $ref: '#/components/schemas/MetrictypesType' - unit: + title: type: string - required: - - metricName - - description - - type - - unit - - temporality - - isMonotonic type: object - MetricsexplorertypesListMetricsResponse: + CloudintegrationtypesDataCollected: properties: + logs: + items: + $ref: '#/components/schemas/CloudintegrationtypesCollectedLogAttribute' + nullable: true + type: array metrics: items: - $ref: '#/components/schemas/MetricsexplorertypesListMetric' + $ref: '#/components/schemas/CloudintegrationtypesCollectedMetric' nullable: true type: array - required: - - metrics type: object - MetricsexplorertypesMetricAlert: + CloudintegrationtypesGettableAccountWithArtifact: properties: - alertId: - type: string - alertName: + connectionArtifact: + $ref: '#/components/schemas/CloudintegrationtypesConnectionArtifact' + id: type: string required: - - alertName - - alertId + - id + - connectionArtifact type: object - MetricsexplorertypesMetricAlertsResponse: + CloudintegrationtypesGettableAccounts: properties: - alerts: + accounts: items: - $ref: '#/components/schemas/MetricsexplorertypesMetricAlert' - nullable: true + $ref: '#/components/schemas/CloudintegrationtypesAccount' type: array required: - - alerts + - accounts type: object - MetricsexplorertypesMetricAttribute: + CloudintegrationtypesGettableAgentCheckInResponse: properties: - key: + account_id: type: string - valueCount: - minimum: 0 - type: integer - values: - items: - type: string + cloud_account_id: + type: string + cloudIntegrationId: + type: string + integration_config: + $ref: '#/components/schemas/CloudintegrationtypesIntegrationConfig' + integrationConfig: + $ref: '#/components/schemas/CloudintegrationtypesProviderIntegrationConfig' + providerAccountId: + type: string + removed_at: + format: date-time + nullable: true + type: string + removedAt: + format: date-time nullable: true + type: string + required: + - account_id + - cloud_account_id + - integration_config + - removed_at + - cloudIntegrationId + - providerAccountId + - integrationConfig + - removedAt + type: object + CloudintegrationtypesGettableServicesMetadata: + properties: + services: + items: + $ref: '#/components/schemas/CloudintegrationtypesServiceMetadata' type: array required: - - key - - values - - valueCount + - services type: object - MetricsexplorertypesMetricAttributesResponse: + CloudintegrationtypesIntegrationConfig: + nullable: true properties: - attributes: + enabled_regions: items: - $ref: '#/components/schemas/MetricsexplorertypesMetricAttribute' - nullable: true + type: string type: array - totalKeys: - format: int64 - type: integer + telemetry: + $ref: '#/components/schemas/CloudintegrationtypesAWSCollectionStrategy' required: - - attributes - - totalKeys + - enabled_regions + - telemetry type: object - MetricsexplorertypesMetricDashboard: + CloudintegrationtypesPostableAgentCheckInRequest: properties: - dashboardId: - type: string - dashboardName: + account_id: type: string - widgetId: + cloud_account_id: type: string - widgetName: + cloudIntegrationId: type: string - required: - - dashboardName - - dashboardId - - widgetId - - widgetName - type: object - MetricsexplorertypesMetricDashboardsResponse: - properties: - dashboards: - items: - $ref: '#/components/schemas/MetricsexplorertypesMetricDashboard' + data: + additionalProperties: {} nullable: true - type: array + type: object + providerAccountId: + type: string required: - - dashboards + - data type: object - MetricsexplorertypesMetricHighlightsResponse: + CloudintegrationtypesProviderIntegrationConfig: properties: - activeTimeSeries: - minimum: 0 - type: integer - dataPoints: - minimum: 0 - type: integer - lastReceived: - minimum: 0 - type: integer - totalTimeSeries: - minimum: 0 - type: integer + aws: + $ref: '#/components/schemas/CloudintegrationtypesAWSIntegrationConfig' required: - - dataPoints - - lastReceived - - totalTimeSeries - - activeTimeSeries + - aws type: object - MetricsexplorertypesMetricMetadata: + CloudintegrationtypesService: properties: - description: + assets: + $ref: '#/components/schemas/CloudintegrationtypesAssets' + dataCollected: + $ref: '#/components/schemas/CloudintegrationtypesDataCollected' + icon: type: string - isMonotonic: - type: boolean - temporality: - $ref: '#/components/schemas/MetrictypesTemporality' - type: - $ref: '#/components/schemas/MetrictypesType' - unit: + id: + type: string + overview: + type: string + serviceConfig: + $ref: '#/components/schemas/CloudintegrationtypesServiceConfig' + supported_signals: + $ref: '#/components/schemas/CloudintegrationtypesSupportedSignals' + telemetryCollectionStrategy: + $ref: '#/components/schemas/CloudintegrationtypesCollectionStrategy' + title: type: string required: - - description - - type - - unit - - temporality - - isMonotonic + - id + - title + - icon + - overview + - assets + - supported_signals + - dataCollected + - telemetryCollectionStrategy + type: object + CloudintegrationtypesServiceConfig: + properties: + aws: + $ref: '#/components/schemas/CloudintegrationtypesAWSServiceConfig' + required: + - aws type: object - MetricsexplorertypesStat: + CloudintegrationtypesServiceMetadata: properties: - description: + enabled: + type: boolean + icon: type: string - metricName: + id: type: string - samples: - minimum: 0 - type: integer - timeseries: - minimum: 0 - type: integer - type: - $ref: '#/components/schemas/MetrictypesType' - unit: + title: type: string required: - - metricName - - description - - type - - unit - - timeseries - - samples + - id + - title + - icon + - enabled type: object - MetricsexplorertypesStatsRequest: + CloudintegrationtypesSupportedSignals: properties: - end: - format: int64 - type: integer - filter: - $ref: '#/components/schemas/Querybuildertypesv5Filter' - limit: - type: integer - offset: - type: integer - orderBy: - $ref: '#/components/schemas/Querybuildertypesv5OrderBy' - start: - format: int64 - type: integer - required: - - start - - end - - limit + logs: + type: boolean + metrics: + type: boolean type: object - MetricsexplorertypesStatsResponse: + CloudintegrationtypesUpdatableAccount: properties: - metrics: - items: - $ref: '#/components/schemas/MetricsexplorertypesStat' - nullable: true - type: array - total: - minimum: 0 - type: integer + config: + $ref: '#/components/schemas/CloudintegrationtypesAccountConfig' required: - - metrics - - total + - config type: object - MetricsexplorertypesTreemapEntry: + CloudintegrationtypesUpdatableService: properties: - metricName: - type: string - percentage: - format: double - type: number - totalValue: - minimum: 0 - type: integer + config: + $ref: '#/components/schemas/CloudintegrationtypesServiceConfig' required: - - metricName - - percentage - - totalValue + - config type: object - MetricsexplorertypesTreemapMode: - enum: - - timeseries - - samples - type: string - MetricsexplorertypesTreemapRequest: + DashboardtypesDashboard: properties: - end: - format: int64 - type: integer - filter: - $ref: '#/components/schemas/Querybuildertypesv5Filter' - limit: - type: integer - mode: - $ref: '#/components/schemas/MetricsexplorertypesTreemapMode' - start: - format: int64 - type: integer - required: - - start - - end - - limit - - mode + createdAt: + format: date-time + type: string + createdBy: + type: string + data: + $ref: '#/components/schemas/DashboardtypesStorableDashboardData' + id: + type: string + locked: + type: boolean + org_id: + type: string + updatedAt: + format: date-time + type: string + updatedBy: + type: string type: object - MetricsexplorertypesTreemapResponse: + DashboardtypesGettablePublicDasbhboard: properties: - samples: - items: - $ref: '#/components/schemas/MetricsexplorertypesTreemapEntry' - nullable: true - type: array - timeseries: - items: - $ref: '#/components/schemas/MetricsexplorertypesTreemapEntry' - nullable: true - type: array - required: - - timeseries - - samples + defaultTimeRange: + type: string + publicPath: + type: string + timeRangeEnabled: + type: boolean type: object - MetricsexplorertypesUpdateMetricMetadataRequest: + DashboardtypesGettablePublicDashboardData: properties: - description: + dashboard: + $ref: '#/components/schemas/DashboardtypesDashboard' + publicDashboard: + $ref: '#/components/schemas/DashboardtypesGettablePublicDasbhboard' + type: object + DashboardtypesPostablePublicDashboard: + properties: + defaultTimeRange: type: string - isMonotonic: + timeRangeEnabled: type: boolean - metricName: + type: object + DashboardtypesStorableDashboardData: + additionalProperties: {} + type: object + DashboardtypesUpdatablePublicDashboard: + properties: + defaultTimeRange: type: string - temporality: - $ref: '#/components/schemas/MetrictypesTemporality' - type: - $ref: '#/components/schemas/MetrictypesType' - unit: + timeRangeEnabled: + type: boolean + type: object + ErrorsJSON: + properties: + code: + type: string + errors: + items: + $ref: '#/components/schemas/ErrorsResponseerroradditional' + type: array + message: + type: string + url: type: string required: - - metricName - - type - - description - - unit - - temporality - - isMonotonic + - code + - message type: object - MetrictypesComparisonSpaceAggregationParam: + ErrorsResponseerroradditional: properties: - operator: + message: type: string - threshold: - format: double - type: number - required: - - operator - - threshold type: object - MetrictypesSpaceAggregation: - enum: - - sum - - avg - - min - - max - - count - - p50 - - p75 - - p90 - - p95 - - p99 - type: string - MetrictypesTemporality: - enum: - - delta - - cumulative - - unspecified - type: string - MetrictypesTimeAggregation: - enum: - - latest - - sum - - avg - - min - - max - - count - - count_distinct - - rate - - increase - type: string - MetrictypesType: - enum: - - gauge - - sum - - histogram - - summary - - exponentialhistogram - type: string - PreferencetypesPreference: + FactoryResponse: properties: - allowedScopes: - items: - type: string - nullable: true - type: array - allowedValues: - items: - type: string + healthy: + type: boolean + services: + additionalProperties: + items: + type: string + type: array nullable: true - type: array - defaultValue: - $ref: '#/components/schemas/PreferencetypesValue' + type: object + type: object + FeaturetypesGettableFeature: + properties: + defaultVariant: + type: string description: type: string + kind: + type: string name: type: string - value: - $ref: '#/components/schemas/PreferencetypesValue' - valueType: + resolvedValue: {} + stage: type: string + variants: + additionalProperties: {} + nullable: true + type: object type: object - PreferencetypesUpdatablePreference: + GatewaytypesGettableCreatedIngestionKey: properties: - value: {} + id: + type: string + value: + type: string + required: + - id + - value type: object - PreferencetypesValue: + GatewaytypesGettableCreatedIngestionKeyLimit: + properties: + id: + type: string + required: + - id type: object - PromotetypesPromotePath: + GatewaytypesGettableIngestionKeys: properties: - indexes: + _pagination: + $ref: '#/components/schemas/GatewaytypesPagination' + keys: items: - $ref: '#/components/schemas/PromotetypesWrappedIndex' + $ref: '#/components/schemas/GatewaytypesIngestionKey' + nullable: true type: array - path: - type: string - promote: - type: boolean type: object - PromotetypesWrappedIndex: + GatewaytypesIngestionKey: properties: - column_type: + created_at: + format: date-time type: string - granularity: - type: integer - type: + expires_at: + format: date-time type: string - type: object - Querybuildertypesv5AggregationBucket: - properties: - alias: + id: type: string - anomalyScores: + limits: items: - $ref: '#/components/schemas/Querybuildertypesv5TimeSeries' + $ref: '#/components/schemas/GatewaytypesLimit' + nullable: true type: array - index: - type: integer - lowerBoundSeries: + name: + type: string + tags: items: - $ref: '#/components/schemas/Querybuildertypesv5TimeSeries' + type: string + nullable: true type: array - meta: - properties: - unit: - type: string - type: object - predictedSeries: - items: - $ref: '#/components/schemas/Querybuildertypesv5TimeSeries' - type: array - series: - items: - $ref: '#/components/schemas/Querybuildertypesv5TimeSeries' - nullable: true - type: array - upperBoundSeries: - items: - $ref: '#/components/schemas/Querybuildertypesv5TimeSeries' - type: array - type: object - Querybuildertypesv5Bucket: - properties: - step: - format: double - type: number - type: object - Querybuildertypesv5ClickHouseQuery: - properties: - disabled: - type: boolean - legend: + updated_at: + format: date-time type: string - name: + value: type: string - query: + workspace_id: type: string type: object - Querybuildertypesv5ColumnDescriptor: + GatewaytypesLimit: properties: - aggregationIndex: - format: int64 - type: integer - columnType: - $ref: '#/components/schemas/Querybuildertypesv5ColumnType' - description: + config: + $ref: '#/components/schemas/GatewaytypesLimitConfig' + created_at: + format: date-time type: string - fieldContext: - $ref: '#/components/schemas/TelemetrytypesFieldContext' - fieldDataType: - $ref: '#/components/schemas/TelemetrytypesFieldDataType' - meta: - properties: - unit: - type: string - type: object - name: + id: type: string - queryName: + key_id: type: string + metric: + $ref: '#/components/schemas/GatewaytypesLimitMetric' signal: - $ref: '#/components/schemas/TelemetrytypesSignal' - unit: type: string - required: - - name - type: object - Querybuildertypesv5ColumnType: - enum: - - group - - aggregation - type: string - Querybuildertypesv5CompositeQuery: - description: Composite query containing one or more query envelopes. Each query - envelope specifies its type and corresponding spec. - properties: - queries: + tags: items: - $ref: '#/components/schemas/Querybuildertypesv5QueryEnvelope' + type: string nullable: true type: array + updated_at: + format: date-time + type: string type: object - Querybuildertypesv5ExecStats: - description: Execution statistics for the query, including rows scanned, bytes - scanned, and duration. + GatewaytypesLimitConfig: properties: - bytesScanned: - minimum: 0 - type: integer - durationMs: - minimum: 0 - type: integer - rowsScanned: - minimum: 0 - type: integer - stepIntervals: - additionalProperties: - minimum: 0 - type: integer - type: object + day: + $ref: '#/components/schemas/GatewaytypesLimitValue' + second: + $ref: '#/components/schemas/GatewaytypesLimitValue' type: object - Querybuildertypesv5Filter: + GatewaytypesLimitMetric: properties: - expression: - type: string + day: + $ref: '#/components/schemas/GatewaytypesLimitMetricValue' + second: + $ref: '#/components/schemas/GatewaytypesLimitMetricValue' type: object - Querybuildertypesv5FormatOptions: + GatewaytypesLimitMetricValue: properties: - fillGaps: - type: boolean - formatTableResultForUI: - type: boolean + count: + format: int64 + type: integer + size: + format: int64 + type: integer type: object - Querybuildertypesv5Function: + GatewaytypesLimitValue: properties: - args: - items: - $ref: '#/components/schemas/Querybuildertypesv5FunctionArg' - type: array - name: - $ref: '#/components/schemas/Querybuildertypesv5FunctionName' + count: + nullable: true + type: integer + size: + nullable: true + type: integer type: object - Querybuildertypesv5FunctionArg: + GatewaytypesPagination: properties: - name: - type: string - value: {} + page: + type: integer + pages: + type: integer + per_page: + type: integer + total: + type: integer type: object - Querybuildertypesv5FunctionName: - enum: - - cutoffmin - - cutoffmax - - clampmin - - clampmax - - absolute - - runningdiff - - log2 - - log10 - - cumulativesum - - ewma3 - - ewma5 - - ewma7 - - median3 - - median5 - - median7 - - timeshift - - anomaly - - fillzero - type: string - Querybuildertypesv5GroupByKey: + GatewaytypesPostableIngestionKey: properties: - description: + expires_at: + format: date-time type: string - fieldContext: - $ref: '#/components/schemas/TelemetrytypesFieldContext' - fieldDataType: - $ref: '#/components/schemas/TelemetrytypesFieldDataType' name: type: string - signal: - $ref: '#/components/schemas/TelemetrytypesSignal' - unit: - type: string + tags: + items: + type: string + nullable: true + type: array required: - name type: object - Querybuildertypesv5Having: + GatewaytypesPostableIngestionKeyLimit: properties: - expression: + config: + $ref: '#/components/schemas/GatewaytypesLimitConfig' + signal: type: string + tags: + items: + type: string + nullable: true + type: array type: object - Querybuildertypesv5Label: - properties: - key: - $ref: '#/components/schemas/TelemetrytypesTelemetryFieldKey' - value: {} - type: object - Querybuildertypesv5LimitBy: + GatewaytypesUpdatableIngestionKeyLimit: properties: - keys: + config: + $ref: '#/components/schemas/GatewaytypesLimitConfig' + tags: items: type: string nullable: true type: array - value: - type: string + required: + - config type: object - Querybuildertypesv5LogAggregation: + GlobaltypesAPIKeyConfig: properties: - alias: - type: string - expression: - type: string + enabled: + type: boolean type: object - Querybuildertypesv5MetricAggregation: + GlobaltypesConfig: properties: - comparisonSpaceAggregationParam: - $ref: '#/components/schemas/MetrictypesComparisonSpaceAggregationParam' - metricName: + external_url: + type: string + identN: + $ref: '#/components/schemas/GlobaltypesIdentNConfig' + ingestion_url: type: string - reduceTo: - $ref: '#/components/schemas/Querybuildertypesv5ReduceTo' - spaceAggregation: - $ref: '#/components/schemas/MetrictypesSpaceAggregation' - temporality: - $ref: '#/components/schemas/MetrictypesTemporality' - timeAggregation: - $ref: '#/components/schemas/MetrictypesTimeAggregation' type: object - Querybuildertypesv5OrderBy: + GlobaltypesIdentNConfig: properties: - direction: - $ref: '#/components/schemas/Querybuildertypesv5OrderDirection' - key: - $ref: '#/components/schemas/Querybuildertypesv5OrderByKey' + apikey: + $ref: '#/components/schemas/GlobaltypesAPIKeyConfig' + impersonation: + $ref: '#/components/schemas/GlobaltypesImpersonationConfig' + tokenizer: + $ref: '#/components/schemas/GlobaltypesTokenizerConfig' type: object - Querybuildertypesv5OrderByKey: + GlobaltypesImpersonationConfig: properties: - description: - type: string - fieldContext: - $ref: '#/components/schemas/TelemetrytypesFieldContext' - fieldDataType: - $ref: '#/components/schemas/TelemetrytypesFieldDataType' - name: - type: string - signal: - $ref: '#/components/schemas/TelemetrytypesSignal' - unit: - type: string - required: - - name + enabled: + type: boolean type: object - Querybuildertypesv5OrderDirection: - enum: - - asc - - desc - type: string - Querybuildertypesv5PromQuery: + GlobaltypesTokenizerConfig: properties: - disabled: - type: boolean - legend: - type: string - name: - type: string - query: - type: string - stats: + enabled: type: boolean - step: - $ref: '#/components/schemas/Querybuildertypesv5Step' type: object - Querybuildertypesv5QueryBuilderFormula: + MetricsexplorertypesInspectMetricsRequest: properties: - disabled: - type: boolean - expression: - type: string - functions: - items: - $ref: '#/components/schemas/Querybuildertypesv5Function' - type: array - having: - $ref: '#/components/schemas/Querybuildertypesv5Having' - legend: - type: string - limit: + end: + format: int64 type: integer - name: + filter: + $ref: '#/components/schemas/Querybuildertypesv5Filter' + metricName: type: string - order: - items: - $ref: '#/components/schemas/Querybuildertypesv5OrderBy' - type: array + start: + format: int64 + type: integer + required: + - metricName + - start + - end type: object - Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5LogAggregation: + MetricsexplorertypesInspectMetricsResponse: properties: - aggregations: + series: items: - $ref: '#/components/schemas/Querybuildertypesv5LogAggregation' + $ref: '#/components/schemas/Querybuildertypesv5TimeSeries' + nullable: true type: array - cursor: + required: + - series + type: object + MetricsexplorertypesListMetric: + properties: + description: type: string - disabled: + isMonotonic: type: boolean - filter: - $ref: '#/components/schemas/Querybuildertypesv5Filter' - functions: - items: - $ref: '#/components/schemas/Querybuildertypesv5Function' - type: array - groupBy: - items: - $ref: '#/components/schemas/Querybuildertypesv5GroupByKey' - type: array - having: - $ref: '#/components/schemas/Querybuildertypesv5Having' - legend: + metricName: type: string - limit: - type: integer - limitBy: - $ref: '#/components/schemas/Querybuildertypesv5LimitBy' - name: + temporality: + $ref: '#/components/schemas/MetrictypesTemporality' + type: + $ref: '#/components/schemas/MetrictypesType' + unit: type: string - offset: - type: integer - order: - items: - $ref: '#/components/schemas/Querybuildertypesv5OrderBy' - type: array - secondaryAggregations: - items: - $ref: '#/components/schemas/Querybuildertypesv5SecondaryAggregation' - type: array - selectFields: - items: - $ref: '#/components/schemas/TelemetrytypesTelemetryFieldKey' - type: array - signal: - $ref: '#/components/schemas/TelemetrytypesSignal' - source: - $ref: '#/components/schemas/TelemetrytypesSource' - stepInterval: - $ref: '#/components/schemas/Querybuildertypesv5Step' + required: + - metricName + - description + - type + - unit + - temporality + - isMonotonic type: object - Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5MetricAggregation: + MetricsexplorertypesListMetricsResponse: properties: - aggregations: + metrics: items: - $ref: '#/components/schemas/Querybuildertypesv5MetricAggregation' + $ref: '#/components/schemas/MetricsexplorertypesListMetric' + nullable: true type: array - cursor: + required: + - metrics + type: object + MetricsexplorertypesMetricAlert: + properties: + alertId: type: string - disabled: - type: boolean - filter: - $ref: '#/components/schemas/Querybuildertypesv5Filter' - functions: - items: - $ref: '#/components/schemas/Querybuildertypesv5Function' - type: array - groupBy: + alertName: + type: string + required: + - alertName + - alertId + type: object + MetricsexplorertypesMetricAlertsResponse: + properties: + alerts: items: - $ref: '#/components/schemas/Querybuildertypesv5GroupByKey' + $ref: '#/components/schemas/MetricsexplorertypesMetricAlert' + nullable: true type: array - having: - $ref: '#/components/schemas/Querybuildertypesv5Having' - legend: - type: string - limit: - type: integer - limitBy: - $ref: '#/components/schemas/Querybuildertypesv5LimitBy' - name: + required: + - alerts + type: object + MetricsexplorertypesMetricAttribute: + properties: + key: type: string - offset: + valueCount: + minimum: 0 type: integer - order: - items: - $ref: '#/components/schemas/Querybuildertypesv5OrderBy' - type: array - secondaryAggregations: - items: - $ref: '#/components/schemas/Querybuildertypesv5SecondaryAggregation' - type: array - selectFields: + values: items: - $ref: '#/components/schemas/TelemetrytypesTelemetryFieldKey' + type: string + nullable: true type: array - signal: - $ref: '#/components/schemas/TelemetrytypesSignal' - source: - $ref: '#/components/schemas/TelemetrytypesSource' - stepInterval: - $ref: '#/components/schemas/Querybuildertypesv5Step' + required: + - key + - values + - valueCount type: object - Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5TraceAggregation: + MetricsexplorertypesMetricAttributesResponse: properties: - aggregations: + attributes: items: - $ref: '#/components/schemas/Querybuildertypesv5TraceAggregation' + $ref: '#/components/schemas/MetricsexplorertypesMetricAttribute' + nullable: true type: array - cursor: + totalKeys: + format: int64 + type: integer + required: + - attributes + - totalKeys + type: object + MetricsexplorertypesMetricDashboard: + properties: + dashboardId: type: string - disabled: - type: boolean - filter: - $ref: '#/components/schemas/Querybuildertypesv5Filter' - functions: - items: - $ref: '#/components/schemas/Querybuildertypesv5Function' - type: array - groupBy: - items: - $ref: '#/components/schemas/Querybuildertypesv5GroupByKey' - type: array - having: - $ref: '#/components/schemas/Querybuildertypesv5Having' - legend: + dashboardName: type: string - limit: - type: integer - limitBy: - $ref: '#/components/schemas/Querybuildertypesv5LimitBy' - name: + widgetId: type: string - offset: - type: integer - order: - items: - $ref: '#/components/schemas/Querybuildertypesv5OrderBy' - type: array - secondaryAggregations: - items: - $ref: '#/components/schemas/Querybuildertypesv5SecondaryAggregation' - type: array - selectFields: - items: - $ref: '#/components/schemas/TelemetrytypesTelemetryFieldKey' - type: array - signal: - $ref: '#/components/schemas/TelemetrytypesSignal' - source: - $ref: '#/components/schemas/TelemetrytypesSource' - stepInterval: - $ref: '#/components/schemas/Querybuildertypesv5Step' + widgetName: + type: string + required: + - dashboardName + - dashboardId + - widgetId + - widgetName type: object - Querybuildertypesv5QueryBuilderTraceOperator: + MetricsexplorertypesMetricDashboardsResponse: properties: - aggregations: + dashboards: items: - $ref: '#/components/schemas/Querybuildertypesv5TraceAggregation' + $ref: '#/components/schemas/MetricsexplorertypesMetricDashboard' + nullable: true type: array - cursor: + required: + - dashboards + type: object + MetricsexplorertypesMetricHighlightsResponse: + properties: + activeTimeSeries: + minimum: 0 + type: integer + dataPoints: + minimum: 0 + type: integer + lastReceived: + minimum: 0 + type: integer + totalTimeSeries: + minimum: 0 + type: integer + required: + - dataPoints + - lastReceived + - totalTimeSeries + - activeTimeSeries + type: object + MetricsexplorertypesMetricMetadata: + properties: + description: type: string - disabled: + isMonotonic: type: boolean - expression: + temporality: + $ref: '#/components/schemas/MetrictypesTemporality' + type: + $ref: '#/components/schemas/MetrictypesType' + unit: + type: string + required: + - description + - type + - unit + - temporality + - isMonotonic + type: object + MetricsexplorertypesMetricsOnboardingResponse: + properties: + hasMetrics: + type: boolean + required: + - hasMetrics + type: object + MetricsexplorertypesStat: + properties: + description: + type: string + metricName: + type: string + samples: + minimum: 0 + type: integer + timeseries: + minimum: 0 + type: integer + type: + $ref: '#/components/schemas/MetrictypesType' + unit: type: string + required: + - metricName + - description + - type + - unit + - timeseries + - samples + type: object + MetricsexplorertypesStatsRequest: + properties: + end: + format: int64 + type: integer filter: $ref: '#/components/schemas/Querybuildertypesv5Filter' - functions: - items: - $ref: '#/components/schemas/Querybuildertypesv5Function' - type: array - groupBy: - items: - $ref: '#/components/schemas/Querybuildertypesv5GroupByKey' - type: array - having: - $ref: '#/components/schemas/Querybuildertypesv5Having' - legend: - type: string limit: type: integer - name: - type: string offset: type: integer - order: - items: - $ref: '#/components/schemas/Querybuildertypesv5OrderBy' - type: array - returnSpansFrom: - type: string - selectFields: - items: - $ref: '#/components/schemas/TelemetrytypesTelemetryFieldKey' - type: array - stepInterval: - $ref: '#/components/schemas/Querybuildertypesv5Step' + orderBy: + $ref: '#/components/schemas/Querybuildertypesv5OrderBy' + start: + format: int64 + type: integer + required: + - start + - end + - limit type: object - Querybuildertypesv5QueryData: - oneOf: - - $ref: '#/components/schemas/Querybuildertypesv5TimeSeriesData' - - $ref: '#/components/schemas/Querybuildertypesv5ScalarData' - - $ref: '#/components/schemas/Querybuildertypesv5RawData' + MetricsexplorertypesStatsResponse: properties: - results: - items: {} + metrics: + items: + $ref: '#/components/schemas/MetricsexplorertypesStat' nullable: true type: array + total: + minimum: 0 + type: integer + required: + - metrics + - total type: object - Querybuildertypesv5QueryEnvelope: - oneOf: - - $ref: '#/components/schemas/Querybuildertypesv5QueryEnvelopeBuilderTrace' - - $ref: '#/components/schemas/Querybuildertypesv5QueryEnvelopeBuilderLog' - - $ref: '#/components/schemas/Querybuildertypesv5QueryEnvelopeBuilderMetric' - - $ref: '#/components/schemas/Querybuildertypesv5QueryEnvelopeFormula' - - $ref: '#/components/schemas/Querybuildertypesv5QueryEnvelopeTraceOperator' - - $ref: '#/components/schemas/Querybuildertypesv5QueryEnvelopePromQL' - - $ref: '#/components/schemas/Querybuildertypesv5QueryEnvelopeClickHouseSQL' - properties: - spec: {} - type: - $ref: '#/components/schemas/Querybuildertypesv5QueryType' - type: object - Querybuildertypesv5QueryEnvelopeBuilderLog: + MetricsexplorertypesTreemapEntry: properties: - spec: - $ref: '#/components/schemas/Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5LogAggregation' - type: - $ref: '#/components/schemas/Querybuildertypesv5QueryType' + metricName: + type: string + percentage: + format: double + type: number + totalValue: + minimum: 0 + type: integer + required: + - metricName + - percentage + - totalValue type: object - Querybuildertypesv5QueryEnvelopeBuilderMetric: + MetricsexplorertypesTreemapMode: + enum: + - timeseries + - samples + type: string + MetricsexplorertypesTreemapRequest: properties: - spec: - $ref: '#/components/schemas/Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5MetricAggregation' - type: - $ref: '#/components/schemas/Querybuildertypesv5QueryType' + end: + format: int64 + type: integer + filter: + $ref: '#/components/schemas/Querybuildertypesv5Filter' + limit: + type: integer + mode: + $ref: '#/components/schemas/MetricsexplorertypesTreemapMode' + start: + format: int64 + type: integer + required: + - start + - end + - limit + - mode type: object - Querybuildertypesv5QueryEnvelopeBuilderTrace: + MetricsexplorertypesTreemapResponse: properties: - spec: - $ref: '#/components/schemas/Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5TraceAggregation' - type: - $ref: '#/components/schemas/Querybuildertypesv5QueryType' + samples: + items: + $ref: '#/components/schemas/MetricsexplorertypesTreemapEntry' + nullable: true + type: array + timeseries: + items: + $ref: '#/components/schemas/MetricsexplorertypesTreemapEntry' + nullable: true + type: array + required: + - timeseries + - samples type: object - Querybuildertypesv5QueryEnvelopeClickHouseSQL: + MetricsexplorertypesUpdateMetricMetadataRequest: properties: - spec: - $ref: '#/components/schemas/Querybuildertypesv5ClickHouseQuery' + description: + type: string + isMonotonic: + type: boolean + metricName: + type: string + temporality: + $ref: '#/components/schemas/MetrictypesTemporality' type: - $ref: '#/components/schemas/Querybuildertypesv5QueryType' + $ref: '#/components/schemas/MetrictypesType' + unit: + type: string + required: + - metricName + - type + - description + - unit + - temporality + - isMonotonic type: object - Querybuildertypesv5QueryEnvelopeFormula: + MetrictypesComparisonSpaceAggregationParam: properties: - spec: - $ref: '#/components/schemas/Querybuildertypesv5QueryBuilderFormula' - type: - $ref: '#/components/schemas/Querybuildertypesv5QueryType' - type: object - Querybuildertypesv5QueryEnvelopePromQL: - properties: - spec: - $ref: '#/components/schemas/Querybuildertypesv5PromQuery' - type: - $ref: '#/components/schemas/Querybuildertypesv5QueryType' - type: object - Querybuildertypesv5QueryEnvelopeTraceOperator: - properties: - spec: - $ref: '#/components/schemas/Querybuildertypesv5QueryBuilderTraceOperator' - type: - $ref: '#/components/schemas/Querybuildertypesv5QueryType' - type: object - Querybuildertypesv5QueryRangeRequest: - description: Request body for the v5 query range endpoint. Supports builder - queries (traces, logs, metrics), formulas, joins, trace operators, PromQL, - and ClickHouse SQL queries. - properties: - compositeQuery: - $ref: '#/components/schemas/Querybuildertypesv5CompositeQuery' - end: - minimum: 0 - type: integer - formatOptions: - $ref: '#/components/schemas/Querybuildertypesv5FormatOptions' - noCache: - type: boolean - requestType: - $ref: '#/components/schemas/Querybuildertypesv5RequestType' - schemaVersion: + operator: type: string - start: - minimum: 0 - type: integer - variables: - additionalProperties: - $ref: '#/components/schemas/Querybuildertypesv5VariableItem' - type: object - type: object - Querybuildertypesv5QueryRangeResponse: - description: 'Response from the v5 query range endpoint. The data.results array - contains typed results depending on the requestType: TimeSeriesData for time_series, - ScalarData for scalar, or RawData for raw requests.' - properties: - data: - $ref: '#/components/schemas/Querybuildertypesv5QueryData' - meta: - $ref: '#/components/schemas/Querybuildertypesv5ExecStats' - type: - $ref: '#/components/schemas/Querybuildertypesv5RequestType' - warning: - $ref: '#/components/schemas/Querybuildertypesv5QueryWarnData' + threshold: + format: double + type: number + required: + - operator + - threshold type: object - Querybuildertypesv5QueryType: + MetrictypesSpaceAggregation: enum: - - builder_query - - builder_formula - - builder_trace_operator - - clickhouse_sql - - promql + - sum + - avg + - min + - max + - count + - p50 + - p75 + - p90 + - p95 + - p99 type: string - Querybuildertypesv5QueryWarnData: - properties: - message: - type: string - url: - type: string - warnings: - items: - $ref: '#/components/schemas/Querybuildertypesv5QueryWarnDataAdditional' - type: array - type: object - Querybuildertypesv5QueryWarnDataAdditional: - properties: - message: - type: string - type: object - Querybuildertypesv5RawData: - properties: - nextCursor: - type: string - queryName: - type: string - rows: - items: - $ref: '#/components/schemas/Querybuildertypesv5RawRow' - nullable: true - type: array - type: object - Querybuildertypesv5RawRow: - properties: - data: - additionalProperties: {} - nullable: true - type: object - timestamp: - format: date-time - type: string - type: object - Querybuildertypesv5ReduceTo: + MetrictypesTemporality: + enum: + - delta + - cumulative + - unspecified + type: string + MetrictypesTimeAggregation: enum: + - latest - sum - - count - avg - min - max - - last - - median + - count + - count_distinct + - rate + - increase type: string - Querybuildertypesv5RequestType: + MetrictypesType: enum: - - scalar - - time_series - - raw - - raw_stream - - trace + - gauge + - sum + - histogram + - summary + - exponentialhistogram type: string - Querybuildertypesv5ScalarData: + PreferencetypesPreference: properties: - columns: + allowedScopes: items: - $ref: '#/components/schemas/Querybuildertypesv5ColumnDescriptor' + type: string nullable: true type: array - data: + allowedValues: items: - items: {} - type: array + type: string nullable: true type: array - queryName: + defaultValue: + $ref: '#/components/schemas/PreferencetypesValue' + description: + type: string + name: + type: string + value: + $ref: '#/components/schemas/PreferencetypesValue' + valueType: type: string type: object - Querybuildertypesv5SecondaryAggregation: + PreferencetypesUpdatablePreference: properties: - alias: + value: {} + type: object + PreferencetypesValue: + type: object + PromotetypesPromotePath: + properties: + indexes: + items: + $ref: '#/components/schemas/PromotetypesWrappedIndex' + type: array + path: type: string - expression: + promote: + type: boolean + type: object + PromotetypesWrappedIndex: + properties: + column_type: type: string - groupBy: + granularity: + type: integer + type: + type: string + type: object + Querybuildertypesv5AggregationBucket: + properties: + alias: + type: string + anomalyScores: items: - $ref: '#/components/schemas/Querybuildertypesv5GroupByKey' + $ref: '#/components/schemas/Querybuildertypesv5TimeSeries' type: array - limit: + index: type: integer - limitBy: - $ref: '#/components/schemas/Querybuildertypesv5LimitBy' - order: + lowerBoundSeries: items: - $ref: '#/components/schemas/Querybuildertypesv5OrderBy' + $ref: '#/components/schemas/Querybuildertypesv5TimeSeries' type: array - stepInterval: - $ref: '#/components/schemas/Querybuildertypesv5Step' - type: object - Querybuildertypesv5Step: - description: Step interval. Accepts a Go duration string (e.g., "60s", "1m", - "1h") or a number representing seconds (e.g., 60). - oneOf: - - description: Duration string (e.g., "60s", "5m", "1h"). - example: 60s - type: string - - description: Duration in seconds. - example: 60 - type: number - Querybuildertypesv5TimeSeries: - properties: - labels: + meta: + properties: + unit: + type: string + type: object + predictedSeries: items: - $ref: '#/components/schemas/Querybuildertypesv5Label' + $ref: '#/components/schemas/Querybuildertypesv5TimeSeries' type: array - values: + series: items: - $ref: '#/components/schemas/Querybuildertypesv5TimeSeriesValue' + $ref: '#/components/schemas/Querybuildertypesv5TimeSeries' nullable: true type: array - type: object - Querybuildertypesv5TimeSeriesData: - properties: - aggregations: + upperBoundSeries: items: - $ref: '#/components/schemas/Querybuildertypesv5AggregationBucket' - nullable: true + $ref: '#/components/schemas/Querybuildertypesv5TimeSeries' type: array - queryName: - type: string type: object - Querybuildertypesv5TimeSeriesValue: + Querybuildertypesv5Bucket: properties: - bucket: - $ref: '#/components/schemas/Querybuildertypesv5Bucket' - partial: - type: boolean - timestamp: - format: int64 - type: integer - value: + step: format: double type: number - values: - items: - format: double - type: number - type: array type: object - Querybuildertypesv5TraceAggregation: + Querybuildertypesv5ClickHouseQuery: properties: - alias: + disabled: + type: boolean + legend: type: string - expression: + name: type: string - type: object - Querybuildertypesv5VariableItem: - properties: - type: - $ref: '#/components/schemas/Querybuildertypesv5VariableType' - value: {} - type: object - Querybuildertypesv5VariableType: - enum: - - query - - dynamic - - custom - - text - type: string - RenderErrorResponse: - properties: - error: - $ref: '#/components/schemas/ErrorsJSON' - status: + query: type: string - required: - - status - - error type: object - ServiceaccounttypesFactorAPIKey: + Querybuildertypesv5ColumnDescriptor: properties: - createdAt: - format: date-time - type: string - expiresAt: - minimum: 0 + aggregationIndex: + format: int64 type: integer - id: - type: string - key: - type: string - lastObservedAt: - format: date-time + columnType: + $ref: '#/components/schemas/Querybuildertypesv5ColumnType' + description: type: string + fieldContext: + $ref: '#/components/schemas/TelemetrytypesFieldContext' + fieldDataType: + $ref: '#/components/schemas/TelemetrytypesFieldDataType' + meta: + properties: + unit: + type: string + type: object name: type: string - serviceAccountId: + queryName: type: string - updatedAt: - format: date-time + signal: + $ref: '#/components/schemas/TelemetrytypesSignal' + unit: type: string required: - - id - - key - - expiresAt - - lastObservedAt - - serviceAccountId + - name type: object - ServiceaccounttypesGettableFactorAPIKeyWithKey: + Querybuildertypesv5ColumnType: + enum: + - group + - aggregation + type: string + Querybuildertypesv5CompositeQuery: + description: Composite query containing one or more query envelopes. Each query + envelope specifies its type and corresponding spec. properties: - id: - type: string - key: - type: string - required: - - id - - key + queries: + items: + $ref: '#/components/schemas/Querybuildertypesv5QueryEnvelope' + nullable: true + type: array type: object - ServiceaccounttypesPostableFactorAPIKey: + Querybuildertypesv5ExecStats: + description: Execution statistics for the query, including rows scanned, bytes + scanned, and duration. properties: - expiresAt: + bytesScanned: minimum: 0 type: integer - name: - type: string - required: - - name - - expiresAt + durationMs: + minimum: 0 + type: integer + rowsScanned: + minimum: 0 + type: integer + stepIntervals: + additionalProperties: + minimum: 0 + type: integer + type: object type: object - ServiceaccounttypesPostableServiceAccount: + Querybuildertypesv5Filter: properties: - email: - type: string - name: + expression: type: string - roles: + type: object + Querybuildertypesv5FormatOptions: + properties: + fillGaps: + type: boolean + formatTableResultForUI: + type: boolean + type: object + Querybuildertypesv5Function: + properties: + args: items: - type: string + $ref: '#/components/schemas/Querybuildertypesv5FunctionArg' type: array - required: - - name - - email - - roles + name: + $ref: '#/components/schemas/Querybuildertypesv5FunctionName' type: object - ServiceaccounttypesServiceAccount: + Querybuildertypesv5FunctionArg: properties: - createdAt: - format: date-time - type: string - deletedAt: - format: date-time - type: string - email: - type: string - id: - type: string name: type: string - orgId: + value: {} + type: object + Querybuildertypesv5FunctionName: + enum: + - cutoffmin + - cutoffmax + - clampmin + - clampmax + - absolute + - runningdiff + - log2 + - log10 + - cumulativesum + - ewma3 + - ewma5 + - ewma7 + - median3 + - median5 + - median7 + - timeshift + - anomaly + - fillzero + type: string + Querybuildertypesv5GroupByKey: + properties: + description: type: string - roles: - items: - type: string - type: array - status: + fieldContext: + $ref: '#/components/schemas/TelemetrytypesFieldContext' + fieldDataType: + $ref: '#/components/schemas/TelemetrytypesFieldDataType' + name: type: string - updatedAt: - format: date-time + signal: + $ref: '#/components/schemas/TelemetrytypesSignal' + unit: type: string required: - - id - name - - email - - roles - - status - - orgId - - deletedAt type: object - ServiceaccounttypesUpdatableFactorAPIKey: + Querybuildertypesv5Having: properties: - expiresAt: - minimum: 0 - type: integer - name: + expression: type: string - required: - - name - - expiresAt type: object - ServiceaccounttypesUpdatableServiceAccount: + Querybuildertypesv5Label: properties: - email: - type: string - name: - type: string - roles: + key: + $ref: '#/components/schemas/TelemetrytypesTelemetryFieldKey' + value: {} + type: object + Querybuildertypesv5LimitBy: + properties: + keys: items: type: string + nullable: true type: array - required: - - name - - email - - roles + value: + type: string type: object - ServiceaccounttypesUpdatableServiceAccountStatus: + Querybuildertypesv5LogAggregation: properties: - status: + alias: + type: string + expression: type: string - required: - - status type: object - TelemetrytypesFieldContext: - enum: - - metric - - log - - span - - resource - - attribute - - body - type: string - TelemetrytypesFieldDataType: - enum: - - string - - bool - - float64 - - int64 - - number - type: string - TelemetrytypesGettableFieldKeys: + Querybuildertypesv5MetricAggregation: properties: - complete: - type: boolean - keys: - additionalProperties: - items: - $ref: '#/components/schemas/TelemetrytypesTelemetryFieldKey' - type: array - nullable: true - type: object - required: - - keys - - complete + comparisonSpaceAggregationParam: + $ref: '#/components/schemas/MetrictypesComparisonSpaceAggregationParam' + metricName: + type: string + reduceTo: + $ref: '#/components/schemas/Querybuildertypesv5ReduceTo' + spaceAggregation: + $ref: '#/components/schemas/MetrictypesSpaceAggregation' + temporality: + $ref: '#/components/schemas/MetrictypesTemporality' + timeAggregation: + $ref: '#/components/schemas/MetrictypesTimeAggregation' type: object - TelemetrytypesGettableFieldValues: + Querybuildertypesv5OrderBy: properties: - complete: - type: boolean - values: - $ref: '#/components/schemas/TelemetrytypesTelemetryFieldValues' - required: - - values - - complete + direction: + $ref: '#/components/schemas/Querybuildertypesv5OrderDirection' + key: + $ref: '#/components/schemas/Querybuildertypesv5OrderByKey' type: object - TelemetrytypesSignal: - enum: - - traces - - logs - - metrics - type: string - TelemetrytypesSource: - enum: - - meter - type: string - TelemetrytypesTelemetryFieldKey: + Querybuildertypesv5OrderByKey: properties: description: type: string @@ -1997,346 +1777,2673 @@ components: required: - name type: object - TelemetrytypesTelemetryFieldValues: - properties: - boolValues: - items: - type: boolean - type: array - numberValues: - items: - format: double - type: number - type: array - relatedValues: - items: - type: string - type: array - stringValues: - items: - type: string - type: array - type: object - TypesChangePasswordRequest: + Querybuildertypesv5OrderDirection: + enum: + - asc + - desc + type: string + Querybuildertypesv5PromQuery: properties: - newPassword: + disabled: + type: boolean + legend: type: string - oldPassword: + name: type: string - userId: + query: type: string + stats: + type: boolean + step: + $ref: '#/components/schemas/Querybuildertypesv5Step' type: object - TypesGettableAPIKey: + Querybuildertypesv5QueryBuilderFormula: properties: - createdAt: - format: date-time - type: string - createdBy: + disabled: + type: boolean + expression: type: string - createdByUser: - $ref: '#/components/schemas/TypesUser' - expiresAt: - format: int64 - type: integer - id: + functions: + items: + $ref: '#/components/schemas/Querybuildertypesv5Function' + type: array + having: + $ref: '#/components/schemas/Querybuildertypesv5Having' + legend: type: string - lastUsed: - format: int64 + limit: type: integer name: type: string - revoked: - type: boolean - role: - type: string - token: - type: string - updatedAt: - format: date-time - type: string - updatedBy: - type: string - updatedByUser: - $ref: '#/components/schemas/TypesUser' - userId: - type: string - required: - - id - type: object - TypesIdentifiable: - properties: - id: - type: string - required: - - id + order: + items: + $ref: '#/components/schemas/Querybuildertypesv5OrderBy' + type: array type: object - TypesInvite: + Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5LogAggregation: properties: - createdAt: - format: date-time - type: string - email: - type: string - id: + aggregations: + items: + $ref: '#/components/schemas/Querybuildertypesv5LogAggregation' + type: array + cursor: type: string - inviteLink: + disabled: + type: boolean + filter: + $ref: '#/components/schemas/Querybuildertypesv5Filter' + functions: + items: + $ref: '#/components/schemas/Querybuildertypesv5Function' + type: array + groupBy: + items: + $ref: '#/components/schemas/Querybuildertypesv5GroupByKey' + type: array + having: + $ref: '#/components/schemas/Querybuildertypesv5Having' + legend: type: string + limit: + type: integer + limitBy: + $ref: '#/components/schemas/Querybuildertypesv5LimitBy' name: type: string - orgId: - type: string - role: - type: string - token: - type: string - updatedAt: - format: date-time - type: string - required: - - id + offset: + type: integer + order: + items: + $ref: '#/components/schemas/Querybuildertypesv5OrderBy' + type: array + secondaryAggregations: + items: + $ref: '#/components/schemas/Querybuildertypesv5SecondaryAggregation' + type: array + selectFields: + items: + $ref: '#/components/schemas/TelemetrytypesTelemetryFieldKey' + type: array + signal: + $ref: '#/components/schemas/TelemetrytypesSignal' + source: + $ref: '#/components/schemas/TelemetrytypesSource' + stepInterval: + $ref: '#/components/schemas/Querybuildertypesv5Step' type: object - TypesOrganization: + Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5MetricAggregation: properties: - alias: - type: string - createdAt: - format: date-time - type: string - displayName: + aggregations: + items: + $ref: '#/components/schemas/Querybuildertypesv5MetricAggregation' + type: array + cursor: type: string - id: + disabled: + type: boolean + filter: + $ref: '#/components/schemas/Querybuildertypesv5Filter' + functions: + items: + $ref: '#/components/schemas/Querybuildertypesv5Function' + type: array + groupBy: + items: + $ref: '#/components/schemas/Querybuildertypesv5GroupByKey' + type: array + having: + $ref: '#/components/schemas/Querybuildertypesv5Having' + legend: type: string - key: - minimum: 0 + limit: type: integer + limitBy: + $ref: '#/components/schemas/Querybuildertypesv5LimitBy' name: type: string - updatedAt: - format: date-time - type: string - required: - - id - type: object - TypesPostableAPIKey: - properties: - expiresInDays: - format: int64 + offset: type: integer - name: - type: string - role: - type: string - type: object - TypesPostableBulkInviteRequest: - properties: - invites: + order: items: - $ref: '#/components/schemas/TypesPostableInvite' + $ref: '#/components/schemas/Querybuildertypesv5OrderBy' type: array - required: - - invites - type: object - TypesPostableForgotPassword: - properties: - email: - type: string - frontendBaseURL: - type: string - orgId: - type: string - required: - - orgId - - email + secondaryAggregations: + items: + $ref: '#/components/schemas/Querybuildertypesv5SecondaryAggregation' + type: array + selectFields: + items: + $ref: '#/components/schemas/TelemetrytypesTelemetryFieldKey' + type: array + signal: + $ref: '#/components/schemas/TelemetrytypesSignal' + source: + $ref: '#/components/schemas/TelemetrytypesSource' + stepInterval: + $ref: '#/components/schemas/Querybuildertypesv5Step' type: object - TypesPostableInvite: + Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5TraceAggregation: properties: - email: + aggregations: + items: + $ref: '#/components/schemas/Querybuildertypesv5TraceAggregation' + type: array + cursor: type: string - frontendBaseUrl: + disabled: + type: boolean + filter: + $ref: '#/components/schemas/Querybuildertypesv5Filter' + functions: + items: + $ref: '#/components/schemas/Querybuildertypesv5Function' + type: array + groupBy: + items: + $ref: '#/components/schemas/Querybuildertypesv5GroupByKey' + type: array + having: + $ref: '#/components/schemas/Querybuildertypesv5Having' + legend: type: string + limit: + type: integer + limitBy: + $ref: '#/components/schemas/Querybuildertypesv5LimitBy' name: type: string - role: - type: string + offset: + type: integer + order: + items: + $ref: '#/components/schemas/Querybuildertypesv5OrderBy' + type: array + secondaryAggregations: + items: + $ref: '#/components/schemas/Querybuildertypesv5SecondaryAggregation' + type: array + selectFields: + items: + $ref: '#/components/schemas/TelemetrytypesTelemetryFieldKey' + type: array + signal: + $ref: '#/components/schemas/TelemetrytypesSignal' + source: + $ref: '#/components/schemas/TelemetrytypesSource' + stepInterval: + $ref: '#/components/schemas/Querybuildertypesv5Step' type: object - TypesPostableResetPassword: + Querybuildertypesv5QueryBuilderTraceOperator: properties: - password: - type: string - token: + aggregations: + items: + $ref: '#/components/schemas/Querybuildertypesv5TraceAggregation' + type: array + cursor: type: string - type: object - TypesResetPasswordToken: - properties: - expiresAt: - format: date-time + disabled: + type: boolean + expression: type: string - id: + filter: + $ref: '#/components/schemas/Querybuildertypesv5Filter' + functions: + items: + $ref: '#/components/schemas/Querybuildertypesv5Function' + type: array + groupBy: + items: + $ref: '#/components/schemas/Querybuildertypesv5GroupByKey' + type: array + having: + $ref: '#/components/schemas/Querybuildertypesv5Having' + legend: type: string - passwordId: + limit: + type: integer + name: type: string - token: + offset: + type: integer + order: + items: + $ref: '#/components/schemas/Querybuildertypesv5OrderBy' + type: array + returnSpansFrom: type: string - required: - - id + selectFields: + items: + $ref: '#/components/schemas/TelemetrytypesTelemetryFieldKey' + type: array + stepInterval: + $ref: '#/components/schemas/Querybuildertypesv5Step' + type: object + Querybuildertypesv5QueryData: + oneOf: + - $ref: '#/components/schemas/Querybuildertypesv5TimeSeriesData' + - $ref: '#/components/schemas/Querybuildertypesv5ScalarData' + - $ref: '#/components/schemas/Querybuildertypesv5RawData' + properties: + results: + items: {} + nullable: true + type: array type: object - TypesStorableAPIKey: + Querybuildertypesv5QueryEnvelope: + oneOf: + - $ref: '#/components/schemas/Querybuildertypesv5QueryEnvelopeBuilderTrace' + - $ref: '#/components/schemas/Querybuildertypesv5QueryEnvelopeBuilderLog' + - $ref: '#/components/schemas/Querybuildertypesv5QueryEnvelopeBuilderMetric' + - $ref: '#/components/schemas/Querybuildertypesv5QueryEnvelopeFormula' + - $ref: '#/components/schemas/Querybuildertypesv5QueryEnvelopeTraceOperator' + - $ref: '#/components/schemas/Querybuildertypesv5QueryEnvelopePromQL' + - $ref: '#/components/schemas/Querybuildertypesv5QueryEnvelopeClickHouseSQL' properties: - createdAt: - format: date-time - type: string - createdBy: - type: string - id: - type: string - name: - type: string - revoked: - type: boolean - role: - type: string - token: - type: string - updatedAt: - format: date-time - type: string - updatedBy: - type: string - userId: - type: string - required: - - id + spec: {} + type: + $ref: '#/components/schemas/Querybuildertypesv5QueryType' type: object - TypesUser: + Querybuildertypesv5QueryEnvelopeBuilderLog: properties: - createdAt: - format: date-time - type: string - displayName: - type: string - email: - type: string - id: - type: string - isRoot: - type: boolean - orgId: - type: string - role: - type: string - status: - type: string - updatedAt: - format: date-time - type: string - required: - - id + spec: + $ref: '#/components/schemas/Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5LogAggregation' + type: + $ref: '#/components/schemas/Querybuildertypesv5QueryType' type: object - ZeustypesGettableHost: + Querybuildertypesv5QueryEnvelopeBuilderMetric: properties: - hosts: - items: - $ref: '#/components/schemas/ZeustypesHost' - nullable: true - type: array - name: - type: string - state: - type: string - tier: - type: string - required: - - name - - state - - tier - - hosts + spec: + $ref: '#/components/schemas/Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5MetricAggregation' + type: + $ref: '#/components/schemas/Querybuildertypesv5QueryType' type: object - ZeustypesHost: + Querybuildertypesv5QueryEnvelopeBuilderTrace: properties: - is_default: - type: boolean - name: - type: string - url: - type: string - required: - - name - - is_default - - url + spec: + $ref: '#/components/schemas/Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5TraceAggregation' + type: + $ref: '#/components/schemas/Querybuildertypesv5QueryType' type: object - ZeustypesPostableHost: + Querybuildertypesv5QueryEnvelopeClickHouseSQL: properties: - name: - type: string - required: - - name + spec: + $ref: '#/components/schemas/Querybuildertypesv5ClickHouseQuery' + type: + $ref: '#/components/schemas/Querybuildertypesv5QueryType' type: object - ZeustypesPostableProfile: + Querybuildertypesv5QueryEnvelopeFormula: properties: - existing_observability_tool: - type: string - has_existing_observability_tool: - type: boolean - logs_scale_per_day_in_gb: - format: int64 + spec: + $ref: '#/components/schemas/Querybuildertypesv5QueryBuilderFormula' + type: + $ref: '#/components/schemas/Querybuildertypesv5QueryType' + type: object + Querybuildertypesv5QueryEnvelopePromQL: + properties: + spec: + $ref: '#/components/schemas/Querybuildertypesv5PromQuery' + type: + $ref: '#/components/schemas/Querybuildertypesv5QueryType' + type: object + Querybuildertypesv5QueryEnvelopeTraceOperator: + properties: + spec: + $ref: '#/components/schemas/Querybuildertypesv5QueryBuilderTraceOperator' + type: + $ref: '#/components/schemas/Querybuildertypesv5QueryType' + type: object + Querybuildertypesv5QueryRangeRequest: + description: Request body for the v5 query range endpoint. Supports builder + queries (traces, logs, metrics), formulas, joins, trace operators, PromQL, + and ClickHouse SQL queries. + properties: + compositeQuery: + $ref: '#/components/schemas/Querybuildertypesv5CompositeQuery' + end: + minimum: 0 + type: integer + formatOptions: + $ref: '#/components/schemas/Querybuildertypesv5FormatOptions' + noCache: + type: boolean + requestType: + $ref: '#/components/schemas/Querybuildertypesv5RequestType' + schemaVersion: + type: string + start: + minimum: 0 + type: integer + variables: + additionalProperties: + $ref: '#/components/schemas/Querybuildertypesv5VariableItem' + type: object + type: object + Querybuildertypesv5QueryRangeResponse: + description: 'Response from the v5 query range endpoint. The data.results array + contains typed results depending on the requestType: TimeSeriesData for time_series, + ScalarData for scalar, or RawData for raw requests.' + properties: + data: + $ref: '#/components/schemas/Querybuildertypesv5QueryData' + meta: + $ref: '#/components/schemas/Querybuildertypesv5ExecStats' + type: + $ref: '#/components/schemas/Querybuildertypesv5RequestType' + warning: + $ref: '#/components/schemas/Querybuildertypesv5QueryWarnData' + type: object + Querybuildertypesv5QueryType: + enum: + - builder_query + - builder_formula + - builder_trace_operator + - clickhouse_sql + - promql + type: string + Querybuildertypesv5QueryWarnData: + properties: + message: + type: string + url: + type: string + warnings: + items: + $ref: '#/components/schemas/Querybuildertypesv5QueryWarnDataAdditional' + type: array + type: object + Querybuildertypesv5QueryWarnDataAdditional: + properties: + message: + type: string + type: object + Querybuildertypesv5RawData: + properties: + nextCursor: + type: string + queryName: + type: string + rows: + items: + $ref: '#/components/schemas/Querybuildertypesv5RawRow' + nullable: true + type: array + type: object + Querybuildertypesv5RawRow: + properties: + data: + additionalProperties: {} + nullable: true + type: object + timestamp: + format: date-time + type: string + type: object + Querybuildertypesv5ReduceTo: + enum: + - sum + - count + - avg + - min + - max + - last + - median + type: string + Querybuildertypesv5RequestType: + enum: + - scalar + - time_series + - raw + - raw_stream + - trace + type: string + Querybuildertypesv5ScalarData: + properties: + columns: + items: + $ref: '#/components/schemas/Querybuildertypesv5ColumnDescriptor' + nullable: true + type: array + data: + items: + items: {} + type: array + nullable: true + type: array + queryName: + type: string + type: object + Querybuildertypesv5SecondaryAggregation: + properties: + alias: + type: string + expression: + type: string + groupBy: + items: + $ref: '#/components/schemas/Querybuildertypesv5GroupByKey' + type: array + limit: + type: integer + limitBy: + $ref: '#/components/schemas/Querybuildertypesv5LimitBy' + order: + items: + $ref: '#/components/schemas/Querybuildertypesv5OrderBy' + type: array + stepInterval: + $ref: '#/components/schemas/Querybuildertypesv5Step' + type: object + Querybuildertypesv5Step: + description: Step interval. Accepts a Go duration string (e.g., "60s", "1m", + "1h") or a number representing seconds (e.g., 60). + oneOf: + - description: Duration string (e.g., "60s", "5m", "1h"). + example: 60s + type: string + - description: Duration in seconds. + example: 60 + type: number + Querybuildertypesv5TimeSeries: + properties: + labels: + items: + $ref: '#/components/schemas/Querybuildertypesv5Label' + type: array + values: + items: + $ref: '#/components/schemas/Querybuildertypesv5TimeSeriesValue' + nullable: true + type: array + type: object + Querybuildertypesv5TimeSeriesData: + properties: + aggregations: + items: + $ref: '#/components/schemas/Querybuildertypesv5AggregationBucket' + nullable: true + type: array + queryName: + type: string + type: object + Querybuildertypesv5TimeSeriesValue: + properties: + bucket: + $ref: '#/components/schemas/Querybuildertypesv5Bucket' + partial: + type: boolean + timestamp: + format: int64 + type: integer + value: + format: double + type: number + values: + items: + format: double + type: number + type: array + type: object + Querybuildertypesv5TraceAggregation: + properties: + alias: + type: string + expression: + type: string + type: object + Querybuildertypesv5VariableItem: + properties: + type: + $ref: '#/components/schemas/Querybuildertypesv5VariableType' + value: {} + type: object + Querybuildertypesv5VariableType: + enum: + - query + - dynamic + - custom + - text + type: string + RenderErrorResponse: + properties: + error: + $ref: '#/components/schemas/ErrorsJSON' + status: + type: string + required: + - status + - error + type: object + RulestatehistorytypesGettableRuleStateHistory: + properties: + fingerprint: + minimum: 0 + type: integer + labels: + items: + $ref: '#/components/schemas/Querybuildertypesv5Label' + nullable: true + type: array + overallState: + $ref: '#/components/schemas/RuletypesAlertState' + overallStateChanged: + type: boolean + ruleId: + type: string + ruleName: + type: string + state: + $ref: '#/components/schemas/RuletypesAlertState' + stateChanged: + type: boolean + unixMilli: + format: int64 + type: integer + value: + format: double + type: number + required: + - ruleId + - ruleName + - overallState + - overallStateChanged + - state + - stateChanged + - unixMilli + - labels + - fingerprint + - value + type: object + RulestatehistorytypesGettableRuleStateHistoryContributor: + properties: + count: + minimum: 0 + type: integer + fingerprint: + minimum: 0 + type: integer + labels: + items: + $ref: '#/components/schemas/Querybuildertypesv5Label' + nullable: true + type: array + relatedLogsLink: + type: string + relatedTracesLink: + type: string + required: + - fingerprint + - labels + - count + type: object + RulestatehistorytypesGettableRuleStateHistoryStats: + properties: + currentAvgResolutionTime: + format: double + type: number + currentAvgResolutionTimeSeries: + $ref: '#/components/schemas/Querybuildertypesv5TimeSeries' + currentTriggersSeries: + $ref: '#/components/schemas/Querybuildertypesv5TimeSeries' + pastAvgResolutionTime: + format: double + type: number + pastAvgResolutionTimeSeries: + $ref: '#/components/schemas/Querybuildertypesv5TimeSeries' + pastTriggersSeries: + $ref: '#/components/schemas/Querybuildertypesv5TimeSeries' + totalCurrentTriggers: + minimum: 0 + type: integer + totalPastTriggers: + minimum: 0 + type: integer + required: + - totalCurrentTriggers + - totalPastTriggers + - currentTriggersSeries + - pastTriggersSeries + - currentAvgResolutionTime + - pastAvgResolutionTime + - currentAvgResolutionTimeSeries + - pastAvgResolutionTimeSeries + type: object + RulestatehistorytypesGettableRuleStateTimeline: + properties: + items: + items: + $ref: '#/components/schemas/RulestatehistorytypesGettableRuleStateHistory' + nullable: true + type: array + nextCursor: + type: string + total: + minimum: 0 + type: integer + required: + - items + - total + type: object + RulestatehistorytypesGettableRuleStateWindow: + properties: + end: + format: int64 + type: integer + start: + format: int64 + type: integer + state: + $ref: '#/components/schemas/RuletypesAlertState' + required: + - state + - start + - end + type: object + RuletypesAlertState: + enum: + - inactive + - pending + - recovering + - firing + - nodata + - disabled + type: string + ServiceaccounttypesGettableFactorAPIKey: + properties: + createdAt: + format: date-time + type: string + expiresAt: + minimum: 0 + type: integer + id: + type: string + lastObservedAt: + format: date-time + type: string + name: + type: string + serviceAccountId: + type: string + updatedAt: + format: date-time + type: string + required: + - id + - expiresAt + - lastObservedAt + - serviceAccountId + type: object + ServiceaccounttypesGettableFactorAPIKeyWithKey: + properties: + id: + type: string + key: + type: string + required: + - id + - key + type: object + ServiceaccounttypesPostableFactorAPIKey: + properties: + expiresAt: + minimum: 0 + type: integer + name: + type: string + required: + - name + - expiresAt + type: object + ServiceaccounttypesPostableServiceAccount: + properties: + name: + type: string + required: + - name + type: object + ServiceaccounttypesPostableServiceAccountRole: + properties: + id: + type: string + required: + - id + type: object + ServiceaccounttypesServiceAccount: + properties: + createdAt: + format: date-time + type: string + email: + type: string + id: + type: string + name: + type: string + orgId: + type: string + status: + type: string + updatedAt: + format: date-time + type: string + required: + - id + - name + - email + - status + - orgId + type: object + ServiceaccounttypesServiceAccountRole: + properties: + createdAt: + format: date-time + type: string + id: + type: string + role: + $ref: '#/components/schemas/AuthtypesRole' + roleId: + type: string + serviceAccountId: + type: string + updatedAt: + format: date-time + type: string + required: + - id + - serviceAccountId + - roleId + - role + type: object + ServiceaccounttypesServiceAccountWithRoles: + properties: + createdAt: + format: date-time + type: string + email: + type: string + id: + type: string + name: + type: string + orgId: + type: string + serviceAccountRoles: + items: + $ref: '#/components/schemas/ServiceaccounttypesServiceAccountRole' + nullable: true + type: array + status: + type: string + updatedAt: + format: date-time + type: string + required: + - id + - name + - email + - status + - orgId + - serviceAccountRoles + type: object + ServiceaccounttypesUpdatableFactorAPIKey: + properties: + expiresAt: + minimum: 0 + type: integer + name: + type: string + required: + - name + - expiresAt + type: object + TelemetrytypesFieldContext: + enum: + - metric + - log + - span + - resource + - attribute + - body + type: string + TelemetrytypesFieldDataType: + enum: + - string + - bool + - float64 + - int64 + - number + type: string + TelemetrytypesGettableFieldKeys: + properties: + complete: + type: boolean + keys: + additionalProperties: + items: + $ref: '#/components/schemas/TelemetrytypesTelemetryFieldKey' + type: array + nullable: true + type: object + required: + - keys + - complete + type: object + TelemetrytypesGettableFieldValues: + properties: + complete: + type: boolean + values: + $ref: '#/components/schemas/TelemetrytypesTelemetryFieldValues' + required: + - values + - complete + type: object + TelemetrytypesSignal: + enum: + - traces + - logs + - metrics + type: string + TelemetrytypesSource: + enum: + - meter + type: string + TelemetrytypesTelemetryFieldKey: + properties: + description: + type: string + fieldContext: + $ref: '#/components/schemas/TelemetrytypesFieldContext' + fieldDataType: + $ref: '#/components/schemas/TelemetrytypesFieldDataType' + name: + type: string + signal: + $ref: '#/components/schemas/TelemetrytypesSignal' + unit: + type: string + required: + - name + type: object + TelemetrytypesTelemetryFieldValues: + properties: + boolValues: + items: + type: boolean + type: array + numberValues: + items: + format: double + type: number + type: array + relatedValues: + items: + type: string + type: array + stringValues: + items: + type: string + type: array + type: object + TypesChangePasswordRequest: + properties: + newPassword: + type: string + oldPassword: + type: string + userId: + type: string + type: object + TypesDeprecatedUser: + properties: + createdAt: + format: date-time + type: string + displayName: + type: string + email: + type: string + id: + type: string + isRoot: + type: boolean + orgId: + type: string + role: + type: string + status: + type: string + updatedAt: + format: date-time + type: string + required: + - id + type: object + TypesIdentifiable: + properties: + id: + type: string + required: + - id + type: object + TypesInvite: + properties: + createdAt: + format: date-time + type: string + email: + type: string + id: + type: string + inviteLink: + type: string + name: + type: string + orgId: + type: string + role: + type: string + token: + type: string + updatedAt: + format: date-time + type: string + required: + - id + type: object + TypesOrganization: + properties: + alias: + type: string + createdAt: + format: date-time + type: string + displayName: + type: string + id: + type: string + key: + minimum: 0 + type: integer + name: + type: string + updatedAt: + format: date-time + type: string + required: + - id + type: object + TypesPostableBulkInviteRequest: + properties: + invites: + items: + $ref: '#/components/schemas/TypesPostableInvite' + type: array + required: + - invites + type: object + TypesPostableForgotPassword: + properties: + email: + type: string + frontendBaseURL: + type: string + orgId: + type: string + required: + - orgId + - email + type: object + TypesPostableInvite: + properties: + email: + type: string + frontendBaseUrl: + type: string + name: + type: string + role: + type: string + type: object + TypesPostableResetPassword: + properties: + password: + type: string + token: + type: string + type: object + TypesPostableRole: + properties: + name: + type: string + required: + - name + type: object + TypesResetPasswordToken: + properties: + expiresAt: + format: date-time + type: string + id: + type: string + passwordId: + type: string + token: + type: string + required: + - id + type: object + TypesUpdatableUser: + properties: + displayName: + type: string + required: + - displayName + type: object + TypesUser: + properties: + createdAt: + format: date-time + type: string + displayName: + type: string + email: + type: string + id: + type: string + isRoot: + type: boolean + orgId: + type: string + status: + type: string + updatedAt: + format: date-time + type: string + required: + - id + type: object + ZeustypesGettableHost: + properties: + hosts: + items: + $ref: '#/components/schemas/ZeustypesHost' + nullable: true + type: array + name: + type: string + state: + type: string + tier: + type: string + required: + - name + - state + - tier + - hosts + type: object + ZeustypesHost: + properties: + is_default: + type: boolean + name: + type: string + url: + type: string + required: + - name + - is_default + - url + type: object + ZeustypesPostableHost: + properties: + name: + type: string + required: + - name + type: object + ZeustypesPostableProfile: + properties: + existing_observability_tool: + type: string + has_existing_observability_tool: + type: boolean + logs_scale_per_day_in_gb: + format: int64 + type: integer + number_of_hosts: + format: int64 + type: integer + number_of_services: + format: int64 + type: integer + reasons_for_interest_in_signoz: + items: + type: string + nullable: true + type: array + timeline_for_migrating_to_signoz: + type: string + uses_otel: + type: boolean + where_did_you_discover_signoz: + type: string + required: + - uses_otel + - has_existing_observability_tool + - existing_observability_tool + - reasons_for_interest_in_signoz + - logs_scale_per_day_in_gb + - number_of_services + - number_of_hosts + - where_did_you_discover_signoz + - timeline_for_migrating_to_signoz + type: object + securitySchemes: + api_key: + description: API Keys + in: header + name: SigNoz-Api-Key + type: apiKey + tokenizer: + bearerFormat: Tokenizer + description: Tokens generated by the tokenizer + scheme: bearer + type: http +info: + description: OpenTelemetry-Native Logs, Metrics and Traces in a single pane + title: SigNoz + version: "" +openapi: 3.0.3 +paths: + /api/v1/authz/check: + post: + deprecated: false + description: Checks if the authenticated user has permissions for given transactions + operationId: AuthzCheck + requestBody: + content: + application/json: + schema: + items: + $ref: '#/components/schemas/AuthtypesTransaction' + type: array + responses: + "200": + content: + application/json: + schema: + properties: + data: + items: + $ref: '#/components/schemas/AuthtypesGettableTransaction' + type: array + status: + type: string + required: + - status + - data + type: object + description: OK + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + summary: Check permissions + tags: + - authz + /api/v1/authz/resources: + get: + deprecated: false + description: Gets all the available resources + operationId: AuthzResources + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/AuthtypesGettableResources' + status: + type: string + required: + - status + - data + type: object + description: OK + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + summary: Get resources + tags: + - authz + /api/v1/changePassword/{id}: + post: + deprecated: false + description: This endpoint changes the password by id + operationId: ChangePassword + parameters: + - in: path + name: id + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TypesChangePasswordRequest' + responses: + "204": + description: No Content + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + security: + - api_key: + - ADMIN + - tokenizer: + - ADMIN + summary: Change password + tags: + - users + /api/v1/cloud-integrations/{cloud_provider}/agent-check-in: + post: + deprecated: true + description: '[Deprecated] This endpoint is called by the deployed agent to + check in' + operationId: AgentCheckInDeprecated + parameters: + - in: path + name: cloud_provider + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CloudintegrationtypesPostableAgentCheckInRequest' + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/CloudintegrationtypesGettableAgentCheckInResponse' + 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: + - VIEWER + - tokenizer: + - VIEWER + summary: Agent check-in + tags: + - cloudintegration + /api/v1/cloud_integrations/{cloud_provider}/accounts: + get: + deprecated: false + description: This endpoint lists the accounts for the specified cloud provider + operationId: ListAccounts + parameters: + - in: path + name: cloud_provider + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/CloudintegrationtypesGettableAccounts' + 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: List accounts + tags: + - cloudintegration + post: + deprecated: false + description: This endpoint creates a new cloud integration account for the specified + cloud provider + operationId: CreateAccount + parameters: + - in: path + name: cloud_provider + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CloudintegrationtypesConnectionArtifactRequest' + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/CloudintegrationtypesGettableAccountWithArtifact' + 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: Create account + tags: + - cloudintegration + /api/v1/cloud_integrations/{cloud_provider}/accounts/{id}: + delete: + deprecated: false + description: This endpoint disconnects an account for the specified cloud provider + operationId: DisconnectAccount + parameters: + - in: path + name: cloud_provider + required: true + schema: + type: string + - in: path + name: id + required: true + schema: + type: string + 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: Disconnect account + tags: + - cloudintegration + get: + deprecated: false + description: This endpoint gets an account for the specified cloud provider + operationId: GetAccount + parameters: + - in: path + name: cloud_provider + required: true + schema: + type: string + - in: path + name: id + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/CloudintegrationtypesAccount' + status: + type: string + required: + - status + - data + type: object + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + security: + - api_key: + - ADMIN + - tokenizer: + - ADMIN + summary: Get account + tags: + - cloudintegration + put: + deprecated: false + description: This endpoint updates an account for the specified cloud provider + operationId: UpdateAccount + parameters: + - in: path + name: cloud_provider + required: true + schema: + type: string + - in: path + name: id + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CloudintegrationtypesUpdatableAccount' + 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 account + tags: + - cloudintegration + /api/v1/cloud_integrations/{cloud_provider}/accounts/check_in: + post: + deprecated: false + description: This endpoint is called by the deployed agent to check in + operationId: AgentCheckIn + parameters: + - in: path + name: cloud_provider + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CloudintegrationtypesPostableAgentCheckInRequest' + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/CloudintegrationtypesGettableAgentCheckInResponse' + 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: + - VIEWER + - tokenizer: + - VIEWER + summary: Agent check-in + tags: + - cloudintegration + /api/v1/cloud_integrations/{cloud_provider}/services: + get: + deprecated: false + description: This endpoint lists the services metadata for the specified cloud + provider + operationId: ListServicesMetadata + parameters: + - in: path + name: cloud_provider + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/CloudintegrationtypesGettableServicesMetadata' + 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: List services metadata + tags: + - cloudintegration + /api/v1/cloud_integrations/{cloud_provider}/services/{service_id}: + get: + deprecated: false + description: This endpoint gets a service for the specified cloud provider + operationId: GetService + parameters: + - in: path + name: cloud_provider + required: true + schema: + type: string + - in: path + name: service_id + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/CloudintegrationtypesService' + 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 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 + description: This endpoint creates a session for a user using google callback + operationId: CreateSessionByGoogleCallback + responses: + "303": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/AuthtypesGettableToken' + status: + type: string + required: + - status + - data + type: object + description: See Other + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + summary: Create session by google callback + tags: + - sessions + /api/v1/complete/oidc: + get: + deprecated: false + description: This endpoint creates a session for a user using oidc callback + operationId: CreateSessionByOIDCCallback + responses: + "303": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/AuthtypesGettableToken' + status: + type: string + required: + - status + - data + type: object + description: See Other + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found + "451": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unavailable For Legal Reasons + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + summary: Create session by oidc callback + tags: + - sessions + /api/v1/complete/saml: + post: + deprecated: false + description: This endpoint creates a session for a user using saml callback + operationId: CreateSessionBySAMLCallback + parameters: + - in: query + name: RelayState + schema: + type: string + - in: query + name: SAMLResponse + schema: + type: string + requestBody: + content: + application/x-www-form-urlencoded: + schema: + properties: + RelayState: + type: string + SAMLResponse: + type: string + type: object + responses: + "303": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/AuthtypesGettableToken' + status: + type: string + required: + - status + - data + type: object + description: See Other + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found + "451": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unavailable For Legal Reasons + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + summary: Create session by saml callback + tags: + - sessions + /api/v1/dashboards/{id}/public: + delete: + deprecated: false + description: This endpoint deletes the public sharing config and disables the + public sharing of a dashboard + operationId: DeletePublicDashboard + parameters: + - in: path + name: id + required: true + schema: + type: string + responses: + "204": + content: + application/json: + schema: + type: string + 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: Delete public dashboard + tags: + - dashboard + get: + deprecated: false + description: This endpoint returns public sharing config for a dashboard + operationId: GetPublicDashboard + parameters: + - in: path + name: id + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/DashboardtypesGettablePublicDasbhboard' + 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 public dashboard + tags: + - dashboard + post: + deprecated: false + description: This endpoint creates public sharing config and enables public + sharing of the dashboard + operationId: CreatePublicDashboard + parameters: + - in: path + name: id + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DashboardtypesPostablePublicDashboard' + responses: + "201": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/TypesIdentifiable' + status: + type: string + required: + - status + - data + type: object + description: Created + "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: Create public dashboard + tags: + - dashboard + put: + deprecated: false + description: This endpoint updates the public sharing config for a dashboard + operationId: UpdatePublicDashboard + parameters: + - in: path + name: id + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DashboardtypesUpdatablePublicDashboard' + responses: + "204": + content: + application/json: + schema: + type: string + 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 public dashboard + tags: + - dashboard + /api/v1/domains: + get: + deprecated: false + description: This endpoint lists all auth domains + operationId: ListAuthDomains + responses: + "200": + content: + application/json: + schema: + properties: + data: + items: + $ref: '#/components/schemas/AuthtypesGettableAuthDomain' + type: array + 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: List all auth domains + tags: + - authdomains + post: + deprecated: false + description: This endpoint creates an auth domain + operationId: CreateAuthDomain + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AuthtypesPostableAuthDomain' + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/AuthtypesGettableAuthDomain' + status: + type: string + required: + - status + - data + type: object + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Forbidden + "409": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Conflict + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + security: + - api_key: + - ADMIN + - tokenizer: + - ADMIN + summary: Create auth domain + tags: + - authdomains + /api/v1/domains/{id}: + delete: + deprecated: false + description: This endpoint deletes an auth domain + operationId: DeleteAuthDomain + parameters: + - in: path + name: id + required: true + schema: + type: string + responses: + "204": + description: No Content + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "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: Delete auth domain + tags: + - authdomains + put: + deprecated: false + description: This endpoint updates an auth domain + operationId: UpdateAuthDomain + parameters: + - in: path + name: id + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AuthtypesUpdateableAuthDomain' + responses: + "204": + description: No Content + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Forbidden + "409": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Conflict + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + security: + - api_key: + - ADMIN + - tokenizer: + - ADMIN + summary: Update auth domain + tags: + - authdomains + /api/v1/export_raw_data: + post: + deprecated: false + description: This endpoints allows complex query exporting raw data for traces + and logs + operationId: HandleExportRawDataPOST + parameters: + - description: The output format for the export. + in: query + name: format + schema: + default: csv + description: The output format for the export. + enum: + - csv + - jsonl + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Querybuildertypesv5QueryRangeRequest' + responses: + "200": + content: + application/json: + schema: + type: string + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "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: + - VIEWER + - tokenizer: + - VIEWER + summary: Export raw data + tags: + - logs + - traces + /api/v1/fields/keys: + get: + deprecated: false + description: This endpoint returns field keys + operationId: GetFieldsKeys + parameters: + - in: query + name: signal + schema: + $ref: '#/components/schemas/TelemetrytypesSignal' + - in: query + name: source + schema: + $ref: '#/components/schemas/TelemetrytypesSource' + - in: query + name: limit + schema: + type: integer + - in: query + name: startUnixMilli + schema: + format: int64 + type: integer + - in: query + name: endUnixMilli + schema: + format: int64 + type: integer + - in: query + name: fieldContext + schema: + $ref: '#/components/schemas/TelemetrytypesFieldContext' + - in: query + name: fieldDataType + schema: + $ref: '#/components/schemas/TelemetrytypesFieldDataType' + - in: query + name: metricName + schema: + type: string + - in: query + name: searchText + schema: + type: string + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/TelemetrytypesGettableFieldKeys' + 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: + - VIEWER + - tokenizer: + - VIEWER + summary: Get field keys + tags: + - fields + /api/v1/fields/values: + get: + deprecated: false + description: This endpoint returns field values + operationId: GetFieldsValues + parameters: + - in: query + name: signal + schema: + $ref: '#/components/schemas/TelemetrytypesSignal' + - in: query + name: source + schema: + $ref: '#/components/schemas/TelemetrytypesSource' + - in: query + name: limit + schema: type: integer - number_of_hosts: + - in: query + name: startUnixMilli + schema: format: int64 type: integer - number_of_services: + - in: query + name: endUnixMilli + schema: format: int64 type: integer - reasons_for_interest_in_signoz: - items: - type: string - nullable: true - type: array - timeline_for_migrating_to_signoz: + - in: query + name: fieldContext + schema: + $ref: '#/components/schemas/TelemetrytypesFieldContext' + - in: query + name: fieldDataType + schema: + $ref: '#/components/schemas/TelemetrytypesFieldDataType' + - in: query + name: metricName + schema: type: string - uses_otel: - type: boolean - where_did_you_discover_signoz: + - in: query + name: searchText + schema: type: string - required: - - uses_otel - - has_existing_observability_tool - - existing_observability_tool - - reasons_for_interest_in_signoz - - logs_scale_per_day_in_gb - - number_of_services - - number_of_hosts - - where_did_you_discover_signoz - - timeline_for_migrating_to_signoz - type: object - securitySchemes: - api_key: - description: API Keys - in: header - name: SigNoz-Api-Key - type: apiKey - tokenizer: - bearerFormat: Tokenizer - description: Tokens generated by the tokenizer - scheme: bearer - type: http -info: - description: OpenTelemetry-Native Logs, Metrics and Traces in a single pane - title: SigNoz - version: "" -openapi: 3.0.3 -paths: - /api/v1/authz/check: - post: + - in: query + name: name + schema: + type: string + - in: query + name: existingQuery + schema: + type: string + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/TelemetrytypesGettableFieldValues' + 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: + - VIEWER + - tokenizer: + - VIEWER + summary: Get field values + tags: + - fields + /api/v1/getResetPasswordToken/{id}: + get: deprecated: false - description: Checks if the authenticated user has permissions for given transactions - operationId: AuthzCheck - requestBody: - content: - application/json: - schema: - items: - $ref: '#/components/schemas/AuthtypesTransaction' - type: array + description: This endpoint returns the reset password token by id + operationId: GetResetPasswordToken + parameters: + - in: path + name: id + required: true + schema: + type: string responses: "200": content: @@ -2344,9 +4451,7 @@ paths: schema: properties: data: - items: - $ref: '#/components/schemas/AuthtypesGettableTransaction' - type: array + $ref: '#/components/schemas/TypesResetPasswordToken' status: type: string required: @@ -2354,63 +4459,149 @@ paths: - data type: object description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error - summary: Check permissions + security: + - api_key: + - ADMIN + - tokenizer: + - ADMIN + summary: Get reset password token tags: - - authz - /api/v1/authz/resources: + - users + /api/v1/global/config: get: deprecated: false - description: Gets all the available resources - operationId: AuthzResources + description: This endpoint returns global config + operationId: GetGlobalConfig + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/GlobaltypesConfig' + status: + type: string + required: + - status + - data + type: object + description: OK + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + summary: Get global config + tags: + - global + /api/v1/invite: + post: + deprecated: false + description: This endpoint creates an invite for a user + operationId: CreateInvite + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TypesPostableInvite' responses: - "200": + "201": content: application/json: schema: properties: data: - $ref: '#/components/schemas/AuthtypesGettableResources' + $ref: '#/components/schemas/TypesInvite' status: type: string required: - status - data type: object - description: OK + description: Created + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Forbidden + "409": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Conflict "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error - summary: Get resources + security: + - api_key: + - ADMIN + - tokenizer: + - ADMIN + summary: Create invite tags: - - authz - /api/v1/changePassword/{id}: + - users + /api/v1/invite/bulk: post: deprecated: false - description: This endpoint changes the password by id - operationId: ChangePassword - parameters: - - in: path - name: id - required: true - schema: - type: string + description: This endpoint creates a bulk invite for a user + operationId: CreateBulkInvite requestBody: content: application/json: schema: - $ref: '#/components/schemas/TypesChangePasswordRequest' + $ref: '#/components/schemas/TypesPostableBulkInviteRequest' responses: - "204": - description: No Content + "201": + description: Created "400": content: application/json: @@ -2429,12 +4620,12 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "404": + "409": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found + description: Conflict "500": content: application/json: @@ -2446,182 +4637,247 @@ paths: - ADMIN - tokenizer: - ADMIN - summary: Change password + summary: Create bulk invite tags: - users - /api/v1/complete/google: + /api/v1/logs/promote_paths: get: deprecated: false - description: This endpoint creates a session for a user using google callback - operationId: CreateSessionByGoogleCallback + description: This endpoints promotes and indexes paths + operationId: ListPromotedAndIndexedPaths responses: - "303": + "200": content: application/json: schema: properties: data: - $ref: '#/components/schemas/AuthtypesGettableToken' + items: + $ref: '#/components/schemas/PromotetypesPromotePath' + nullable: true + type: array status: type: string required: - status - data type: object - description: See Other + description: OK "400": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Bad Request - "404": + "401": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found + 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 - summary: Create session by google callback + security: + - api_key: + - VIEWER + - tokenizer: + - VIEWER + summary: Promote and index paths tags: - - sessions - /api/v1/complete/oidc: + - logs + post: + deprecated: false + description: This endpoints promotes and indexes paths + operationId: HandlePromoteAndIndexPaths + requestBody: + content: + application/json: + schema: + items: + $ref: '#/components/schemas/PromotetypesPromotePath' + nullable: true + type: array + responses: + "201": + description: Created + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "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: + - EDITOR + - tokenizer: + - EDITOR + summary: Promote and index paths + tags: + - logs + /api/v1/org/preferences: get: deprecated: false - description: This endpoint creates a session for a user using oidc callback - operationId: CreateSessionByOIDCCallback + description: This endpoint lists all org preferences + operationId: ListOrgPreferences responses: - "303": + "200": content: application/json: schema: properties: data: - $ref: '#/components/schemas/AuthtypesGettableToken' + items: + $ref: '#/components/schemas/PreferencetypesPreference' + type: array status: type: string required: - status - data type: object - description: See Other - "400": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request - "404": + description: OK + "401": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found - "451": + description: Unauthorized + "403": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Unavailable For Legal Reasons + description: Forbidden "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error - summary: Create session by oidc callback + security: + - api_key: + - ADMIN + - tokenizer: + - ADMIN + summary: List org preferences tags: - - sessions - /api/v1/complete/saml: - post: + - preferences + /api/v1/org/preferences/{name}: + get: deprecated: false - description: This endpoint creates a session for a user using saml callback - operationId: CreateSessionBySAMLCallback + description: This endpoint returns the org preference by name + operationId: GetOrgPreference parameters: - - in: query - name: RelayState - schema: - type: string - - in: query - name: SAMLResponse + - in: path + name: name + required: true schema: type: string - requestBody: - content: - application/x-www-form-urlencoded: - schema: - properties: - RelayState: - type: string - SAMLResponse: - type: string - type: object responses: - "303": + "200": content: application/json: schema: properties: data: - $ref: '#/components/schemas/AuthtypesGettableToken' + $ref: '#/components/schemas/PreferencetypesPreference' status: type: string required: - status - data type: object - description: See Other + description: OK "400": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Bad Request - "404": + "401": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found - "451": + description: Unauthorized + "403": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Unavailable For Legal Reasons + description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error - summary: Create session by saml callback + security: + - api_key: + - ADMIN + - tokenizer: + - ADMIN + summary: Get org preference tags: - - sessions - /api/v1/dashboards/{id}/public: - delete: + - preferences + put: deprecated: false - description: This endpoint deletes the public sharing config and disables the - public sharing of a dashboard - operationId: DeletePublicDashboard + description: This endpoint updates the org preference by name + operationId: UpdateOrgPreference parameters: - in: path - name: id + name: name required: true schema: type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PreferencetypesUpdatablePreference' responses: "204": + description: No Content + "400": content: application/json: schema: - type: string - description: No Content + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request "401": content: application/json: @@ -2634,6 +4890,12 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found "500": content: application/json: @@ -2645,13 +4907,14 @@ paths: - ADMIN - tokenizer: - ADMIN - summary: Delete public dashboard + summary: Update org preference tags: - - dashboard + - preferences + /api/v1/public/dashboards/{id}: get: deprecated: false - description: This endpoint returns public sharing config for a dashboard - operationId: GetPublicDashboard + description: This endpoint returns the sanitized dashboard data for public access + operationId: GetPublicDashboardData parameters: - in: path name: id @@ -2665,7 +4928,7 @@ paths: schema: properties: data: - $ref: '#/components/schemas/DashboardtypesGettablePublicDasbhboard' + $ref: '#/components/schemas/DashboardtypesGettablePublicDashboardData' status: type: string required: @@ -2692,44 +4955,43 @@ paths: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error security: - - api_key: - - ADMIN - - tokenizer: - - ADMIN - summary: Get public dashboard + - anonymous: + - public-dashboard:read + summary: Get public dashboard data tags: - dashboard - post: + /api/v1/public/dashboards/{id}/widgets/{idx}/query_range: + get: deprecated: false - description: This endpoint creates public sharing config and enables public - sharing of the dashboard - operationId: CreatePublicDashboard + description: This endpoint return query range results for a widget of public + dashboard + operationId: GetPublicDashboardWidgetQueryRange parameters: - in: path name: id required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/DashboardtypesPostablePublicDashboard' + - in: path + name: idx + required: true + schema: + type: string responses: - "201": + "200": content: application/json: schema: properties: data: - $ref: '#/components/schemas/TypesIdentifiable' + $ref: '#/components/schemas/Querybuildertypesv5QueryRangeResponse' status: type: string required: - status - data type: object - description: Created + description: OK "401": content: application/json: @@ -2749,66 +5011,50 @@ paths: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error security: - - api_key: - - ADMIN - - tokenizer: - - ADMIN - summary: Create public dashboard + - anonymous: + - public-dashboard:read + summary: Get query range result tags: - dashboard - put: + /api/v1/resetPassword: + post: deprecated: false - description: This endpoint updates the public sharing config for a dashboard - operationId: UpdatePublicDashboard - parameters: - - in: path - name: id - required: true - schema: - type: string + description: This endpoint resets the password by token + operationId: ResetPassword requestBody: content: application/json: schema: - $ref: '#/components/schemas/DashboardtypesUpdatablePublicDashboard' + $ref: '#/components/schemas/TypesPostableResetPassword' responses: "204": - content: - application/json: - schema: - type: string description: No Content - "401": + "400": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Unauthorized - "403": + description: Bad Request + "409": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Forbidden + description: Conflict "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error - security: - - api_key: - - ADMIN - - tokenizer: - - ADMIN - summary: Update public dashboard + summary: Reset password tags: - - dashboard - /api/v1/domains: + - users + /api/v1/roles: get: deprecated: false - description: This endpoint lists all auth domains - operationId: ListAuthDomains + description: This endpoint lists all roles + operationId: ListRoles responses: "200": content: @@ -2817,7 +5063,7 @@ paths: properties: data: items: - $ref: '#/components/schemas/AuthtypesGettableAuthDomain' + $ref: '#/components/schemas/AuthtypesRole' type: array status: type: string @@ -2849,33 +5095,33 @@ paths: - ADMIN - tokenizer: - ADMIN - summary: List all auth domains + summary: List roles tags: - - authdomains + - role post: deprecated: false - description: This endpoint creates an auth domain - operationId: CreateAuthDomain + description: This endpoint creates a role + operationId: CreateRole requestBody: content: application/json: schema: - $ref: '#/components/schemas/AuthtypesPostableAuthDomain' + $ref: '#/components/schemas/AuthtypesPostableRole' responses: - "200": + "201": content: application/json: schema: properties: data: - $ref: '#/components/schemas/AuthtypesGettableAuthDomain' + $ref: '#/components/schemas/TypesIdentifiable' status: type: string required: - status - data type: object - description: OK + description: Created "400": content: application/json: @@ -2900,25 +5146,37 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Conflict + "451": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unavailable For Legal Reasons "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error + "501": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Implemented security: - api_key: - ADMIN - tokenizer: - ADMIN - summary: Create auth domain + summary: Create role tags: - - authdomains - /api/v1/domains/{id}: + - role + /api/v1/roles/{id}: delete: deprecated: false - description: This endpoint deletes an auth domain - operationId: DeleteAuthDomain + description: This endpoint deletes a role + operationId: DeleteRole parameters: - in: path name: id @@ -2927,13 +5185,11 @@ paths: type: string responses: "204": - description: No Content - "400": content: application/json: schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request + type: string + description: No Content "401": content: application/json: @@ -2946,44 +5202,63 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found + "451": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unavailable For Legal Reasons "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error + "501": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Implemented security: - api_key: - ADMIN - tokenizer: - ADMIN - summary: Delete auth domain + summary: Delete role tags: - - authdomains - put: + - role + get: deprecated: false - description: This endpoint updates an auth domain - operationId: UpdateAuthDomain + description: This endpoint gets a role + operationId: GetRole parameters: - in: path name: id required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/AuthtypesUpdateableAuthDomain' responses: - "204": - description: No Content - "400": + "200": content: application/json: schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request + properties: + data: + $ref: '#/components/schemas/AuthtypesRole' + status: + type: string + required: + - status + - data + type: object + description: OK "401": content: application/json: @@ -2996,12 +5271,6 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "409": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Conflict "500": content: application/json: @@ -3013,68 +5282,31 @@ paths: - ADMIN - tokenizer: - ADMIN - summary: Update auth domain + summary: Get role tags: - - authdomains - /api/v1/fields/keys: - get: - deprecated: false - description: This endpoint returns field keys - operationId: GetFieldsKeys - parameters: - - in: query - name: signal - schema: - $ref: '#/components/schemas/TelemetrytypesSignal' - - in: query - name: source - schema: - $ref: '#/components/schemas/TelemetrytypesSource' - - in: query - name: limit - schema: - type: integer - - in: query - name: startUnixMilli - schema: - format: int64 - type: integer - - in: query - name: endUnixMilli - schema: - format: int64 - type: integer - - in: query - name: fieldContext - schema: - $ref: '#/components/schemas/TelemetrytypesFieldContext' - - in: query - name: fieldDataType - schema: - $ref: '#/components/schemas/TelemetrytypesFieldDataType' - - in: query - name: metricName - schema: - type: string - - in: query - name: searchText + - role + patch: + deprecated: false + description: This endpoint patches a role + operationId: PatchRole + parameters: + - in: path + name: id + required: true schema: type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AuthtypesPatchableRole' responses: - "200": + "204": content: application/json: schema: - properties: - data: - $ref: '#/components/schemas/TelemetrytypesGettableFieldKeys' - status: - type: string - required: - - status - - data - type: object - description: OK + type: string + description: No Content "401": content: application/json: @@ -3087,70 +5319,53 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found + "451": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unavailable For Legal Reasons "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error + "501": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Implemented security: - api_key: - - VIEWER + - ADMIN - tokenizer: - - VIEWER - summary: Get field keys + - ADMIN + summary: Patch role tags: - - fields - /api/v1/fields/values: + - role + /api/v1/roles/{id}/relation/{relation}/objects: get: deprecated: false - description: This endpoint returns field values - operationId: GetFieldsValues + description: Gets all objects connected to the specified role via a given relation + type + operationId: GetObjects parameters: - - in: query - name: signal - schema: - $ref: '#/components/schemas/TelemetrytypesSignal' - - in: query - name: source - schema: - $ref: '#/components/schemas/TelemetrytypesSource' - - in: query - name: limit - schema: - type: integer - - in: query - name: startUnixMilli - schema: - format: int64 - type: integer - - in: query - name: endUnixMilli - schema: - format: int64 - type: integer - - in: query - name: fieldContext - schema: - $ref: '#/components/schemas/TelemetrytypesFieldContext' - - in: query - name: fieldDataType - schema: - $ref: '#/components/schemas/TelemetrytypesFieldDataType' - - in: query - name: metricName - schema: - type: string - - in: query - name: searchText - schema: - type: string - - in: query - name: name + - in: path + name: id + required: true schema: type: string - - in: query - name: existingQuery + - in: path + name: relation + required: true schema: type: string responses: @@ -3160,7 +5375,9 @@ paths: schema: properties: data: - $ref: '#/components/schemas/TelemetrytypesGettableFieldValues' + items: + $ref: '#/components/schemas/AuthtypesGettableObjects' + type: array status: type: string required: @@ -3180,46 +5397,66 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found + "451": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unavailable For Legal Reasons "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error + "501": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Implemented security: - api_key: - - VIEWER + - ADMIN - tokenizer: - - VIEWER - summary: Get field values + - ADMIN + summary: Get objects for a role by relation tags: - - fields - /api/v1/getResetPasswordToken/{id}: - get: + - role + patch: deprecated: false - description: This endpoint returns the reset password token by id - operationId: GetResetPasswordToken + description: Patches the objects connected to the specified role via a given + relation type + operationId: PatchObjects parameters: - in: path name: id required: true schema: type: string + - in: path + name: relation + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AuthtypesPatchableObjects' responses: - "200": + "204": content: application/json: schema: - properties: - data: - $ref: '#/components/schemas/TypesResetPasswordToken' - status: - type: string - required: - - status - - data - type: object - description: OK + type: string + description: No Content "400": content: application/json: @@ -3244,25 +5481,37 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Not Found + "451": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unavailable For Legal Reasons "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error + "501": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Implemented security: - api_key: - ADMIN - tokenizer: - ADMIN - summary: Get reset password token + summary: Patch objects for a role by relation tags: - - users - /api/v1/global/config: + - role + /api/v1/service_accounts: get: deprecated: false - description: This endpoint returns global config - operationId: GetGlobalConfig + description: This endpoint lists the service accounts for an organisation + operationId: ListServiceAccounts responses: "200": content: @@ -3270,7 +5519,9 @@ paths: schema: properties: data: - $ref: '#/components/schemas/GlobaltypesConfig' + items: + $ref: '#/components/schemas/ServiceaccounttypesServiceAccount' + type: array status: type: string required: @@ -3278,25 +5529,41 @@ paths: - 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 - summary: Get global config + security: + - api_key: + - ADMIN + - tokenizer: + - ADMIN + summary: List service accounts tags: - - global - /api/v1/invite: + - serviceaccount post: deprecated: false - description: This endpoint creates an invite for a user - operationId: CreateInvite + description: This endpoint creates a service account + operationId: CreateServiceAccount requestBody: content: application/json: schema: - $ref: '#/components/schemas/TypesPostableInvite' + $ref: '#/components/schemas/ServiceaccounttypesPostableServiceAccount' responses: "201": content: @@ -3304,7 +5571,7 @@ paths: schema: properties: data: - $ref: '#/components/schemas/TypesInvite' + $ref: '#/components/schemas/TypesIdentifiable' status: type: string required: @@ -3347,28 +5614,27 @@ paths: - ADMIN - tokenizer: - ADMIN - summary: Create invite + summary: Create service account tags: - - users - /api/v1/invite/bulk: - post: + - serviceaccount + /api/v1/service_accounts/{id}: + delete: deprecated: false - description: This endpoint creates a bulk invite for a user - operationId: CreateBulkInvite - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/TypesPostableBulkInviteRequest' + description: This endpoint deletes an existing service account + operationId: DeleteServiceAccount + parameters: + - in: path + name: id + required: true + schema: + type: string responses: - "201": - description: Created - "400": + "204": content: application/json: schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request + type: string + description: No Content "401": content: application/json: @@ -3381,12 +5647,12 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "409": + "404": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Conflict + description: Not Found "500": content: application/json: @@ -3398,14 +5664,19 @@ paths: - ADMIN - tokenizer: - ADMIN - summary: Create bulk invite + summary: Deletes a service account tags: - - users - /api/v1/logs/promote_paths: + - serviceaccount get: deprecated: false - description: This endpoints promotes and indexes paths - operationId: ListPromotedAndIndexedPaths + description: This endpoint gets an existing service account + operationId: GetServiceAccount + parameters: + - in: path + name: id + required: true + schema: + type: string responses: "200": content: @@ -3413,10 +5684,7 @@ paths: schema: properties: data: - items: - $ref: '#/components/schemas/PromotetypesPromotePath' - nullable: true - type: array + $ref: '#/components/schemas/ServiceaccounttypesServiceAccountWithRoles' status: type: string required: @@ -3424,12 +5692,6 @@ paths: - data type: object description: OK - "400": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request "401": content: application/json: @@ -3442,6 +5704,12 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found "500": content: application/json: @@ -3450,27 +5718,34 @@ paths: description: Internal Server Error security: - api_key: - - VIEWER + - ADMIN - tokenizer: - - VIEWER - summary: Promote and index paths + - ADMIN + summary: Gets a service account tags: - - logs - post: + - serviceaccount + put: deprecated: false - description: This endpoints promotes and indexes paths - operationId: HandlePromoteAndIndexPaths + description: This endpoint updates an existing service account + operationId: UpdateServiceAccount + parameters: + - in: path + name: id + required: true + schema: + type: string requestBody: content: application/json: schema: - items: - $ref: '#/components/schemas/PromotetypesPromotePath' - nullable: true - type: array + $ref: '#/components/schemas/ServiceaccounttypesPostableServiceAccount' responses: - "201": - description: Created + "204": + content: + application/json: + schema: + type: string + description: No Content "400": content: application/json: @@ -3489,6 +5764,12 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found "500": content: application/json: @@ -3497,17 +5778,23 @@ paths: description: Internal Server Error security: - api_key: - - EDITOR + - ADMIN - tokenizer: - - EDITOR - summary: Promote and index paths + - ADMIN + summary: Updates a service account tags: - - logs - /api/v1/org/preferences: + - serviceaccount + /api/v1/service_accounts/{id}/keys: get: deprecated: false - description: This endpoint lists all org preferences - operationId: ListOrgPreferences + description: This endpoint lists the service account keys + operationId: ListServiceAccountKeys + parameters: + - in: path + name: id + required: true + schema: + type: string responses: "200": content: @@ -3516,7 +5803,7 @@ paths: properties: data: items: - $ref: '#/components/schemas/PreferencetypesPreference' + $ref: '#/components/schemas/ServiceaccounttypesGettableFactorAPIKey' type: array status: type: string @@ -3548,35 +5835,39 @@ paths: - ADMIN - tokenizer: - ADMIN - summary: List org preferences + summary: List service account keys tags: - - preferences - /api/v1/org/preferences/{name}: - get: + - serviceaccount + post: deprecated: false - description: This endpoint returns the org preference by name - operationId: GetOrgPreference + description: This endpoint creates a service account key + operationId: CreateServiceAccountKey parameters: - in: path - name: name + name: id required: true schema: type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceaccounttypesPostableFactorAPIKey' responses: - "200": + "201": content: application/json: schema: properties: data: - $ref: '#/components/schemas/PreferencetypesPreference' + $ref: '#/components/schemas/ServiceaccounttypesGettableFactorAPIKeyWithKey' status: type: string required: - status - data type: object - description: OK + description: Created "400": content: application/json: @@ -3595,12 +5886,12 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "404": + "409": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found + description: Conflict "500": content: application/json: @@ -3612,33 +5903,32 @@ paths: - ADMIN - tokenizer: - ADMIN - summary: Get org preference + summary: Create a service account key tags: - - preferences - put: + - serviceaccount + /api/v1/service_accounts/{id}/keys/{fid}: + delete: deprecated: false - description: This endpoint updates the org preference by name - operationId: UpdateOrgPreference + description: This endpoint revokes an existing service account key + operationId: RevokeServiceAccountKey parameters: - in: path - name: name + name: id + required: true + schema: + type: string + - in: path + name: fid required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PreferencetypesUpdatablePreference' responses: "204": - description: No Content - "400": content: application/json: schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request + type: string + description: No Content "401": content: application/json: @@ -3668,81 +5958,36 @@ paths: - ADMIN - tokenizer: - ADMIN - summary: Update org preference - tags: - - preferences - /api/v1/pats: - get: - deprecated: false - description: This endpoint lists all api keys - operationId: ListAPIKeys - responses: - "200": - content: - application/json: - schema: - properties: - data: - items: - $ref: '#/components/schemas/TypesGettableAPIKey' - type: array - 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: List api keys + summary: Revoke a service account key tags: - - users - post: + - serviceaccount + put: deprecated: false - description: This endpoint creates an api key - operationId: CreateAPIKey + description: This endpoint updates an existing service account key + operationId: UpdateServiceAccountKey + parameters: + - in: path + name: id + required: true + schema: + type: string + - in: path + name: fid + required: true + schema: + type: string requestBody: content: application/json: schema: - $ref: '#/components/schemas/TypesPostableAPIKey' + $ref: '#/components/schemas/ServiceaccounttypesUpdatableFactorAPIKey' responses: - "201": + "204": content: application/json: schema: - properties: - data: - $ref: '#/components/schemas/TypesGettableAPIKey' - status: - type: string - required: - - status - - data - type: object - description: Created + type: string + description: No Content "400": content: application/json: @@ -3761,12 +6006,12 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "409": + "404": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Conflict + description: Not Found "500": content: application/json: @@ -3778,14 +6023,14 @@ paths: - ADMIN - tokenizer: - ADMIN - summary: Create api key + summary: Updates a service account key tags: - - users - /api/v1/pats/{id}: - delete: + - serviceaccount + /api/v1/service_accounts/{id}/roles: + get: deprecated: false - description: This endpoint revokes an api key - operationId: RevokeAPIKey + description: This endpoint gets all the roles for the existing service account + operationId: GetServiceAccountRoles parameters: - in: path name: id @@ -3793,8 +6038,23 @@ paths: schema: type: string responses: - "204": - description: No Content + "200": + content: + application/json: + schema: + properties: + data: + items: + $ref: '#/components/schemas/AuthtypesRole' + nullable: true + type: array + status: + type: string + required: + - status + - data + type: object + description: OK "401": content: application/json: @@ -3824,13 +6084,13 @@ paths: - ADMIN - tokenizer: - ADMIN - summary: Revoke api key + summary: Gets service account roles tags: - - users - put: + - serviceaccount + post: deprecated: false - description: This endpoint updates an api key - operationId: UpdateAPIKey + description: This endpoint assigns a role to a service account + operationId: CreateServiceAccountRole parameters: - in: path name: id @@ -3841,14 +6101,22 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/TypesStorableAPIKey' + $ref: '#/components/schemas/ServiceaccounttypesPostableServiceAccountRole' responses: - "204": + "201": content: application/json: schema: - type: string - description: No Content + properties: + data: + $ref: '#/components/schemas/TypesIdentifiable' + status: + type: string + required: + - status + - data + type: object + description: Created "400": content: application/json: @@ -3867,12 +6135,6 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "404": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found "500": content: application/json: @@ -3884,35 +6146,32 @@ paths: - ADMIN - tokenizer: - ADMIN - summary: Update api key + summary: Create service account role tags: - - users - /api/v1/public/dashboards/{id}: - get: + - serviceaccount + /api/v1/service_accounts/{id}/roles/{rid}: + delete: deprecated: false - description: This endpoint returns the sanitized dashboard data for public access - operationId: GetPublicDashboardData + description: This endpoint revokes a role from service account + operationId: DeleteServiceAccountRole parameters: - in: path name: id required: true schema: type: string + - in: path + name: rid + required: true + schema: + type: string responses: - "200": + "204": content: application/json: schema: - properties: - data: - $ref: '#/components/schemas/DashboardtypesGettablePublicDashboardData' - status: - type: string - required: - - status - - data - type: object - description: OK + type: string + description: No Content "401": content: application/json: @@ -3932,28 +6191,18 @@ paths: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error security: - - anonymous: - - public-dashboard:read - summary: Get public dashboard data + - api_key: + - ADMIN + - tokenizer: + - ADMIN + summary: Delete service account role tags: - - dashboard - /api/v1/public/dashboards/{id}/widgets/{idx}/query_range: + - serviceaccount + /api/v1/service_accounts/me: get: deprecated: false - description: This endpoint return query range results for a widget of public - dashboard - operationId: GetPublicDashboardWidgetQueryRange - parameters: - - in: path - name: id - required: true - schema: - type: string - - in: path - name: idx - required: true - schema: - type: string + description: This endpoint gets my service account + operationId: GetMyServiceAccount responses: "200": content: @@ -3961,7 +6210,7 @@ paths: schema: properties: data: - $ref: '#/components/schemas/Querybuildertypesv5QueryRangeResponse' + $ref: '#/components/schemas/ServiceaccounttypesServiceAccountWithRoles' status: type: string required: @@ -3969,69 +6218,57 @@ paths: - data type: object description: OK - "401": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Unauthorized - "403": + "404": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Forbidden + description: Not Found "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error - security: - - anonymous: - - public-dashboard:read - summary: Get query range result + summary: Gets my service account tags: - - dashboard - /api/v1/resetPassword: - post: + - serviceaccount + put: deprecated: false - description: This endpoint resets the password by token - operationId: ResetPassword + description: This endpoint gets my service account + operationId: UpdateMyServiceAccount requestBody: content: application/json: schema: - $ref: '#/components/schemas/TypesPostableResetPassword' + $ref: '#/components/schemas/ServiceaccounttypesPostableServiceAccount' responses: "204": - description: No Content - "400": content: application/json: schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request - "409": + type: string + description: No Content + "404": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Conflict + description: Not Found "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error - summary: Reset password + summary: Updates my service account tags: - - users - /api/v1/roles: + - serviceaccount + /api/v1/user: get: deprecated: false - description: This endpoint lists all roles - operationId: ListRoles + description: This endpoint lists all users + operationId: ListUsersDeprecated responses: "200": content: @@ -4040,7 +6277,7 @@ paths: properties: data: items: - $ref: '#/components/schemas/AuthtypesRole' + $ref: '#/components/schemas/TypesDeprecatedUser' type: array status: type: string @@ -4072,39 +6309,23 @@ paths: - ADMIN - tokenizer: - ADMIN - summary: List roles + summary: List users tags: - - role - post: + - users + /api/v1/user/{id}: + delete: deprecated: false - description: This endpoint creates a role - operationId: CreateRole - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/AuthtypesPostableRole' + description: This endpoint deletes the user by id + operationId: DeleteUser + parameters: + - in: path + name: id + required: true + schema: + type: string responses: - "201": - content: - application/json: - schema: - properties: - data: - $ref: '#/components/schemas/TypesIdentifiable' - status: - type: string - required: - - status - - data - type: object - description: Created - "400": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request + "204": + description: No Content "401": content: application/json: @@ -4117,43 +6338,30 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "409": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Conflict - "451": + "404": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Unavailable For Legal Reasons + description: Not Found "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error - "501": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Not Implemented security: - api_key: - ADMIN - tokenizer: - ADMIN - summary: Create role + summary: Delete user tags: - - role - /api/v1/roles/{id}: - delete: + - users + get: deprecated: false - description: This endpoint deletes a role - operationId: DeleteRole + description: This endpoint returns the user by id + operationId: GetUserDeprecated parameters: - in: path name: id @@ -4161,12 +6369,20 @@ paths: schema: type: string responses: - "204": + "200": content: application/json: schema: - type: string - description: No Content + properties: + data: + $ref: '#/components/schemas/TypesDeprecatedUser' + status: + type: string + required: + - status + - data + type: object + description: OK "401": content: application/json: @@ -4185,42 +6401,35 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Not Found - "451": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Unavailable For Legal Reasons "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error - "501": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Not Implemented security: - api_key: - ADMIN - tokenizer: - ADMIN - summary: Delete role + summary: Get user tags: - - role - get: + - users + put: deprecated: false - description: This endpoint gets a role - operationId: GetRole + description: This endpoint updates the user by id + operationId: UpdateUserDeprecated parameters: - in: path name: id required: true schema: type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TypesDeprecatedUser' responses: "200": content: @@ -4228,7 +6437,7 @@ paths: schema: properties: data: - $ref: '#/components/schemas/AuthtypesRole' + $ref: '#/components/schemas/TypesDeprecatedUser' status: type: string required: @@ -4236,6 +6445,12 @@ paths: - data type: object description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request "401": content: application/json: @@ -4248,6 +6463,12 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found "500": content: application/json: @@ -4259,31 +6480,29 @@ paths: - ADMIN - tokenizer: - ADMIN - summary: Get role + summary: Update user tags: - - role - patch: - deprecated: false - description: This endpoint patches a role - operationId: PatchRole - parameters: - - in: path - name: id - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/AuthtypesPatchableRole' + - users + /api/v1/user/me: + get: + deprecated: false + description: This endpoint returns the user I belong to + operationId: GetMyUserDeprecated responses: - "204": + "200": content: application/json: schema: - type: string - description: No Content + properties: + data: + $ref: '#/components/schemas/TypesDeprecatedUser' + status: + type: string + required: + - status + - data + type: object + description: OK "401": content: application/json: @@ -4296,52 +6515,73 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "404": + "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found - "451": + description: Internal Server Error + security: + - tokenizer: [] + summary: Get my user + tags: + - users + /api/v1/user/preferences: + get: + deprecated: false + description: This endpoint lists all user preferences + operationId: ListUserPreferences + responses: + "200": + content: + application/json: + schema: + properties: + data: + items: + $ref: '#/components/schemas/PreferencetypesPreference' + type: array + status: + type: string + required: + - status + - data + type: object + description: OK + "401": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Unavailable For Legal Reasons - "500": + description: Unauthorized + "403": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Internal Server Error - "501": + description: Forbidden + "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Not Implemented + description: Internal Server Error security: - api_key: - - ADMIN + - VIEWER - tokenizer: - - ADMIN - summary: Patch role + - VIEWER + summary: List user preferences tags: - - role - /api/v1/roles/{id}/relation/{relation}/objects: + - preferences + /api/v1/user/preferences/{name}: get: deprecated: false - description: Gets all objects connected to the specified role via a given relation - type - operationId: GetObjects + description: This endpoint returns the user preference by name + operationId: GetUserPreference parameters: - in: path - name: id - required: true - schema: - type: string - - in: path - name: relation + name: name required: true schema: type: string @@ -4352,9 +6592,7 @@ paths: schema: properties: data: - items: - $ref: '#/components/schemas/AuthtypesGettableObjects' - type: array + $ref: '#/components/schemas/PreferencetypesPreference' status: type: string required: @@ -4380,45 +6618,27 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Not Found - "451": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Unavailable For Legal Reasons "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error - "501": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Not Implemented security: - api_key: - - ADMIN + - VIEWER - tokenizer: - - ADMIN - summary: Get objects for a role by relation + - VIEWER + summary: Get user preference tags: - - role - patch: + - preferences + put: deprecated: false - description: Patches the objects connected to the specified role via a given - relation type - operationId: PatchObjects + description: This endpoint updates the user preference by name + operationId: UpdateUserPreference parameters: - in: path - name: id - required: true - schema: - type: string - - in: path - name: relation + name: name required: true schema: type: string @@ -4426,13 +6646,9 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/AuthtypesPatchableObjects' + $ref: '#/components/schemas/PreferencetypesUpdatablePreference' responses: "204": - content: - application/json: - schema: - type: string description: No Content "400": content: @@ -4458,110 +6674,77 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Not Found - "451": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Unavailable For Legal Reasons "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error - "501": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Not Implemented security: - api_key: - - ADMIN + - VIEWER - tokenizer: - - ADMIN - summary: Patch objects for a role by relation + - VIEWER + summary: Update user preference tags: - - role - /api/v1/service_accounts: - get: + - preferences + /api/v2/factor_password/forgot: + post: deprecated: false - description: This endpoint lists the service accounts for an organisation - operationId: ListServiceAccounts + description: This endpoint initiates the forgot password flow by sending a reset + password email + operationId: ForgotPassword + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TypesPostableForgotPassword' responses: - "200": - content: - application/json: - schema: - properties: - data: - items: - $ref: '#/components/schemas/ServiceaccounttypesServiceAccount' - type: array - status: - type: string - required: - - status - - data - type: object - description: OK - "401": + "204": + description: No Content + "400": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Unauthorized - "403": + description: Bad Request + "422": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Forbidden + description: Unprocessable Entity "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error - security: - - api_key: - - ADMIN - - tokenizer: - - ADMIN - summary: List service accounts + summary: Forgot password tags: - - serviceaccount - post: + - users + /api/v2/features: + get: deprecated: false - description: This endpoint creates a service account - operationId: CreateServiceAccount - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ServiceaccounttypesPostableServiceAccount' + description: This endpoint returns the supported features and their details + operationId: GetFeatures responses: - "201": + "200": content: application/json: schema: properties: data: - $ref: '#/components/schemas/TypesIdentifiable' + items: + $ref: '#/components/schemas/FeaturetypesGettableFeature' + type: array status: type: string required: - status - data type: object - description: Created - "400": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request + description: OK "401": content: application/json: @@ -4574,12 +6757,6 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "409": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Conflict "500": content: application/json: @@ -4588,30 +6765,41 @@ paths: description: Internal Server Error security: - api_key: - - ADMIN + - VIEWER - tokenizer: - - ADMIN - summary: Create service account + - VIEWER + summary: Get features tags: - - serviceaccount - /api/v1/service_accounts/{id}: - delete: + - features + /api/v2/gateway/ingestion_keys: + get: deprecated: false - description: This endpoint deletes an existing service account - operationId: DeleteServiceAccount + description: This endpoint returns the ingestion keys for a workspace + operationId: GetIngestionKeys parameters: - - in: path - name: id - required: true + - in: query + name: page schema: - type: string + type: integer + - in: query + name: per_page + schema: + type: integer responses: - "204": + "200": content: application/json: schema: - type: string - description: No Content + properties: + data: + $ref: '#/components/schemas/GatewaytypesGettableIngestionKeys' + status: + type: string + required: + - status + - data + type: object + description: OK "401": content: application/json: @@ -4624,12 +6812,6 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "404": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found "500": content: application/json: @@ -4638,37 +6820,36 @@ paths: description: Internal Server Error security: - api_key: - - ADMIN + - EDITOR - tokenizer: - - ADMIN - summary: Deletes a service account + - EDITOR + summary: Get ingestion keys for workspace tags: - - serviceaccount - get: + - gateway + post: deprecated: false - description: This endpoint gets an existing service account - operationId: GetServiceAccount - parameters: - - in: path - name: id - required: true - schema: - type: string + description: This endpoint creates an ingestion key for the workspace + operationId: CreateIngestionKey + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GatewaytypesPostableIngestionKey' responses: - "200": + "201": content: application/json: schema: properties: data: - $ref: '#/components/schemas/ServiceaccounttypesServiceAccount' + $ref: '#/components/schemas/GatewaytypesGettableCreatedIngestionKey' status: type: string required: - status - data type: object - description: OK + description: Created "401": content: application/json: @@ -4681,12 +6862,6 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "404": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found "500": content: application/json: @@ -4695,40 +6870,26 @@ paths: description: Internal Server Error security: - api_key: - - ADMIN + - EDITOR - tokenizer: - - ADMIN - summary: Gets a service account + - EDITOR + summary: Create ingestion key for workspace tags: - - serviceaccount - put: + - gateway + /api/v2/gateway/ingestion_keys/{keyId}: + delete: deprecated: false - description: This endpoint updates an existing service account - operationId: UpdateServiceAccount + description: This endpoint deletes an ingestion key for the workspace + operationId: DeleteIngestionKey parameters: - in: path - name: id + name: keyId required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ServiceaccounttypesUpdatableServiceAccount' responses: "204": - content: - application/json: - schema: - type: string description: No Content - "400": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request "401": content: application/json: @@ -4741,12 +6902,6 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "404": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found "500": content: application/json: @@ -4755,40 +6910,30 @@ paths: description: Internal Server Error security: - api_key: - - ADMIN + - EDITOR - tokenizer: - - ADMIN - summary: Updates a service account + - EDITOR + summary: Delete ingestion key for workspace tags: - - serviceaccount - /api/v1/service_accounts/{id}/keys: - get: + - gateway + patch: deprecated: false - description: This endpoint lists the service account keys - operationId: ListServiceAccountKeys + description: This endpoint updates an ingestion key for the workspace + operationId: UpdateIngestionKey parameters: - in: path - name: id + name: keyId required: true schema: type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GatewaytypesPostableIngestionKey' responses: - "200": - content: - application/json: - schema: - properties: - data: - items: - $ref: '#/components/schemas/ServiceaccounttypesFactorAPIKey' - type: array - status: - type: string - required: - - status - - data - type: object - description: OK + "204": + description: No Content "401": content: application/json: @@ -4809,19 +6954,20 @@ paths: description: Internal Server Error security: - api_key: - - ADMIN + - EDITOR - tokenizer: - - ADMIN - summary: List service account keys + - EDITOR + summary: Update ingestion key for workspace tags: - - serviceaccount + - gateway + /api/v2/gateway/ingestion_keys/{keyId}/limits: post: deprecated: false - description: This endpoint creates a service account key - operationId: CreateServiceAccountKey + description: This endpoint creates an ingestion key limit + operationId: CreateIngestionKeyLimit parameters: - in: path - name: id + name: keyId required: true schema: type: string @@ -4829,7 +6975,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ServiceaccounttypesPostableFactorAPIKey' + $ref: '#/components/schemas/GatewaytypesPostableIngestionKeyLimit' responses: "201": content: @@ -4837,7 +6983,7 @@ paths: schema: properties: data: - $ref: '#/components/schemas/ServiceaccounttypesGettableFactorAPIKeyWithKey' + $ref: '#/components/schemas/GatewaytypesGettableCreatedIngestionKeyLimit' status: type: string required: @@ -4845,12 +6991,6 @@ paths: - data type: object description: Created - "400": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request "401": content: application/json: @@ -4863,12 +7003,6 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "409": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Conflict "500": content: application/json: @@ -4877,34 +7011,25 @@ paths: description: Internal Server Error security: - api_key: - - ADMIN + - EDITOR - tokenizer: - - ADMIN - summary: Create a service account key + - EDITOR + summary: Create limit for the ingestion key tags: - - serviceaccount - /api/v1/service_accounts/{id}/keys/{fid}: + - gateway + /api/v2/gateway/ingestion_keys/limits/{limitId}: delete: deprecated: false - description: This endpoint revokes an existing service account key - operationId: RevokeServiceAccountKey + description: This endpoint deletes an ingestion key limit + operationId: DeleteIngestionKeyLimit parameters: - in: path - name: id - required: true - schema: - type: string - - in: path - name: fid + name: limitId required: true schema: type: string responses: "204": - content: - application/json: - schema: - type: string description: No Content "401": content: @@ -4918,12 +7043,6 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "404": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found "500": content: application/json: @@ -4932,24 +7051,19 @@ paths: description: Internal Server Error security: - api_key: - - ADMIN + - EDITOR - tokenizer: - - ADMIN - summary: Revoke a service account key + - EDITOR + summary: Delete limit for the ingestion key tags: - - serviceaccount - put: + - gateway + patch: deprecated: false - description: This endpoint updates an existing service account key - operationId: UpdateServiceAccountKey + description: This endpoint updates an ingestion key limit + operationId: UpdateIngestionKeyLimit parameters: - in: path - name: id - required: true - schema: - type: string - - in: path - name: fid + name: limitId required: true schema: type: string @@ -4957,20 +7071,10 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ServiceaccounttypesUpdatableFactorAPIKey' + $ref: '#/components/schemas/GatewaytypesUpdatableIngestionKeyLimit' responses: "204": - content: - application/json: - schema: - type: string description: No Content - "400": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request "401": content: application/json: @@ -4983,12 +7087,6 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "404": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found "500": content: application/json: @@ -4997,41 +7095,46 @@ paths: description: Internal Server Error security: - api_key: - - ADMIN + - EDITOR - tokenizer: - - ADMIN - summary: Updates a service account key + - EDITOR + summary: Update limit for the ingestion key tags: - - serviceaccount - /api/v1/service_accounts/{id}/status: - put: + - gateway + /api/v2/gateway/ingestion_keys/search: + get: deprecated: false - description: This endpoint updates an existing service account status - operationId: UpdateServiceAccountStatus + description: This endpoint returns the ingestion keys for a workspace + operationId: SearchIngestionKeys parameters: - - in: path - name: id + - in: query + name: name required: true schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ServiceaccounttypesUpdatableServiceAccountStatus' + type: string + - in: query + name: page + schema: + type: integer + - in: query + name: per_page + schema: + type: integer responses: - "204": - content: - application/json: - schema: - type: string - description: No Content - "400": + "200": content: application/json: schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request + properties: + data: + $ref: '#/components/schemas/GatewaytypesGettableIngestionKeys' + status: + type: string + required: + - status + - data + type: object + description: OK "401": content: application/json: @@ -5044,12 +7147,6 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "404": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found "500": content: application/json: @@ -5058,17 +7155,15 @@ paths: description: Internal Server Error security: - api_key: - - ADMIN + - EDITOR - tokenizer: - - ADMIN - summary: Updates a service account status + - EDITOR + summary: Search ingestion keys for workspace tags: - - serviceaccount - /api/v1/user: + - gateway + /api/v2/healthz: get: - deprecated: false - description: This endpoint lists all users - operationId: ListUsers + operationId: Healthz responses: "200": content: @@ -5076,9 +7171,7 @@ paths: schema: properties: data: - items: - $ref: '#/components/schemas/TypesUser' - type: array + $ref: '#/components/schemas/FactoryResponse' status: type: string required: @@ -5086,64 +7179,114 @@ paths: - data type: object description: OK - "401": + "503": content: application/json: schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Unauthorized - "403": + properties: + data: + $ref: '#/components/schemas/FactoryResponse' + status: + type: string + required: + - status + - data + type: object + description: Service Unavailable + summary: Health check + tags: + - health + /api/v2/livez: + get: + deprecated: false + description: "" + operationId: Livez + responses: + "200": content: application/json: schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Forbidden + properties: + data: + $ref: '#/components/schemas/FactoryResponse' + status: + type: string + required: + - status + - data + type: object + description: OK "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error - security: - - api_key: - - ADMIN - - tokenizer: - - ADMIN - summary: List users + summary: Liveness check tags: - - users - /api/v1/user/{id}: - delete: + - health + /api/v2/metrics: + get: deprecated: false - description: This endpoint deletes the user by id - operationId: DeleteUser + description: This endpoint returns a list of distinct metric names within the + specified time range + operationId: ListMetrics parameters: - - in: path - name: id - required: true + - in: query + name: start + schema: + nullable: true + type: integer + - in: query + name: end + schema: + nullable: true + type: integer + - in: query + name: limit + schema: + type: integer + - in: query + name: searchText + schema: + type: string + - in: query + name: source schema: type: string responses: - "204": - description: No Content - "401": + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/MetricsexplorertypesListMetricsResponse' + status: + type: string + required: + - status + - data + type: object + description: OK + "400": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Unauthorized - "403": + description: Bad Request + "401": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Forbidden - "404": + description: Unauthorized + "403": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found + description: Forbidden "500": content: application/json: @@ -5152,19 +7295,20 @@ paths: description: Internal Server Error security: - api_key: - - ADMIN + - VIEWER - tokenizer: - - ADMIN - summary: Delete user + - VIEWER + summary: List metric names tags: - - users + - metrics + /api/v2/metrics/{metric_name}/alerts: get: deprecated: false - description: This endpoint returns the user by id - operationId: GetUser + description: This endpoint returns associated alerts for a specified metric + operationId: GetMetricAlerts parameters: - in: path - name: id + name: metric_name required: true schema: type: string @@ -5175,7 +7319,7 @@ paths: schema: properties: data: - $ref: '#/components/schemas/TypesUser' + $ref: '#/components/schemas/MetricsexplorertypesMetricAlertsResponse' status: type: string required: @@ -5183,6 +7327,12 @@ paths: - data type: object description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request "401": content: application/json: @@ -5209,27 +7359,34 @@ paths: description: Internal Server Error security: - api_key: - - ADMIN + - VIEWER - tokenizer: - - ADMIN - summary: Get user + - VIEWER + summary: Get metric alerts tags: - - users - put: + - metrics + /api/v2/metrics/{metric_name}/attributes: + get: deprecated: false - description: This endpoint updates the user by id - operationId: UpdateUser + description: This endpoint returns attribute keys and their unique values for + a specified metric + operationId: GetMetricAttributes parameters: + - in: query + name: start + schema: + nullable: true + type: integer + - in: query + name: end + schema: + nullable: true + type: integer - in: path - name: id + name: metric_name required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/TypesUser' responses: "200": content: @@ -5237,7 +7394,7 @@ paths: schema: properties: data: - $ref: '#/components/schemas/TypesUser' + $ref: '#/components/schemas/MetricsexplorertypesMetricAttributesResponse' status: type: string required: @@ -5277,17 +7434,23 @@ paths: description: Internal Server Error security: - api_key: - - ADMIN + - VIEWER - tokenizer: - - ADMIN - summary: Update user + - VIEWER + summary: Get metric attributes tags: - - users - /api/v1/user/me: + - metrics + /api/v2/metrics/{metric_name}/dashboards: get: deprecated: false - description: This endpoint returns the user I belong to - operationId: GetMyUser + description: This endpoint returns associated dashboards for a specified metric + operationId: GetMetricDashboards + parameters: + - in: path + name: metric_name + required: true + schema: + type: string responses: "200": content: @@ -5295,7 +7458,7 @@ paths: schema: properties: data: - $ref: '#/components/schemas/TypesUser' + $ref: '#/components/schemas/MetricsexplorertypesMetricDashboardsResponse' status: type: string required: @@ -5303,6 +7466,12 @@ paths: - data type: object description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request "401": content: application/json: @@ -5315,6 +7484,12 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found "500": content: application/json: @@ -5322,15 +7497,25 @@ paths: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error security: - - tokenizer: [] - summary: Get my user + - api_key: + - VIEWER + - tokenizer: + - VIEWER + summary: Get metric dashboards tags: - - users - /api/v1/user/preferences: + - metrics + /api/v2/metrics/{metric_name}/highlights: get: deprecated: false - description: This endpoint lists all user preferences - operationId: ListUserPreferences + description: This endpoint returns highlights like number of datapoints, totaltimeseries, + active time series, last received time for a specified metric + operationId: GetMetricHighlights + parameters: + - in: path + name: metric_name + required: true + schema: + type: string responses: "200": content: @@ -5338,9 +7523,7 @@ paths: schema: properties: data: - items: - $ref: '#/components/schemas/PreferencetypesPreference' - type: array + $ref: '#/components/schemas/MetricsexplorertypesMetricHighlightsResponse' status: type: string required: @@ -5348,6 +7531,12 @@ paths: - data type: object description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request "401": content: application/json: @@ -5360,6 +7549,12 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found "500": content: application/json: @@ -5371,17 +7566,18 @@ paths: - VIEWER - tokenizer: - VIEWER - summary: List user preferences + summary: Get metric highlights tags: - - preferences - /api/v1/user/preferences/{name}: + - metrics + /api/v2/metrics/{metric_name}/metadata: get: deprecated: false - description: This endpoint returns the user preference by name - operationId: GetUserPreference + description: This endpoint returns metadata information like metric description, + unit, type, temporality, monotonicity for a specified metric + operationId: GetMetricMetadata parameters: - in: path - name: name + name: metric_name required: true schema: type: string @@ -5392,7 +7588,7 @@ paths: schema: properties: data: - $ref: '#/components/schemas/PreferencetypesPreference' + $ref: '#/components/schemas/MetricsexplorertypesMetricMetadata' status: type: string required: @@ -5400,6 +7596,12 @@ paths: - data type: object description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request "401": content: application/json: @@ -5429,16 +7631,17 @@ paths: - VIEWER - tokenizer: - VIEWER - summary: Get user preference + summary: Get metric metadata tags: - - preferences - put: + - metrics + post: deprecated: false - description: This endpoint updates the user preference by name - operationId: UpdateUserPreference + description: This endpoint helps to update metadata information like metric + description, unit, type, temporality, monotonicity for a specified metric + operationId: UpdateMetricMetadata parameters: - in: path - name: name + name: metric_name required: true schema: type: string @@ -5446,10 +7649,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PreferencetypesUpdatablePreference' + $ref: '#/components/schemas/MetricsexplorertypesUpdateMetricMetadataRequest' responses: - "204": - description: No Content + "200": + content: + application/json: + schema: + type: string + description: OK "400": content: application/json: @@ -5468,12 +7675,6 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "404": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found "500": content: application/json: @@ -5482,52 +7683,23 @@ paths: description: Internal Server Error security: - api_key: - - VIEWER + - EDITOR - tokenizer: - - VIEWER - summary: Update user preference + - EDITOR + summary: Update metric metadata tags: - - preferences - /api/v2/factor_password/forgot: + - metrics + /api/v2/metrics/inspect: post: deprecated: false - description: This endpoint initiates the forgot password flow by sending a reset - password email - operationId: ForgotPassword + description: Returns raw time series data points for a metric within a time + range (max 30 minutes). Each series includes labels and timestamp/value pairs. + operationId: InspectMetrics requestBody: content: application/json: schema: - $ref: '#/components/schemas/TypesPostableForgotPassword' - responses: - "204": - description: No Content - "400": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request - "422": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Unprocessable Entity - "500": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Internal Server Error - summary: Forgot password - tags: - - users - /api/v2/features: - get: - deprecated: false - description: This endpoint returns the supported features and their details - operationId: GetFeatures + $ref: '#/components/schemas/MetricsexplorertypesInspectMetricsRequest' responses: "200": content: @@ -5535,9 +7707,7 @@ paths: schema: properties: data: - items: - $ref: '#/components/schemas/FeaturetypesGettableFeature' - type: array + $ref: '#/components/schemas/MetricsexplorertypesInspectMetricsResponse' status: type: string required: @@ -5545,6 +7715,12 @@ paths: - data type: object description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request "401": content: application/json: @@ -5568,23 +7744,15 @@ paths: - VIEWER - tokenizer: - VIEWER - summary: Get features + summary: Inspect raw metric data points tags: - - features - /api/v2/gateway/ingestion_keys: + - metrics + /api/v2/metrics/onboarding: get: deprecated: false - description: This endpoint returns the ingestion keys for a workspace - operationId: GetIngestionKeys - parameters: - - in: query - name: page - schema: - type: integer - - in: query - name: per_page - schema: - type: integer + description: Lightweight endpoint that checks if any non-SigNoz metrics have + been ingested, used for onboarding status detection + operationId: GetMetricsOnboardingStatus responses: "200": content: @@ -5592,7 +7760,7 @@ paths: schema: properties: data: - $ref: '#/components/schemas/GatewaytypesGettableIngestionKeys' + $ref: '#/components/schemas/MetricsexplorertypesMetricsOnboardingResponse' status: type: string required: @@ -5620,76 +7788,44 @@ paths: description: Internal Server Error security: - api_key: - - EDITOR + - VIEWER - tokenizer: - - EDITOR - summary: Get ingestion keys for workspace + - VIEWER + summary: Check if non-SigNoz metrics have been received tags: - - gateway + - metrics + /api/v2/metrics/stats: post: deprecated: false - description: This endpoint creates an ingestion key for the workspace - operationId: CreateIngestionKey + description: This endpoint provides list of metrics with their number of samples + and timeseries for the given time range + operationId: GetMetricsStats requestBody: content: application/json: schema: - $ref: '#/components/schemas/GatewaytypesPostableIngestionKey' + $ref: '#/components/schemas/MetricsexplorertypesStatsRequest' responses: - "201": + "200": content: application/json: schema: properties: data: - $ref: '#/components/schemas/GatewaytypesGettableCreatedIngestionKey' + $ref: '#/components/schemas/MetricsexplorertypesStatsResponse' status: type: string required: - status - data type: object - description: Created - "401": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Unauthorized - "403": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Forbidden - "500": + description: OK + "400": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Internal Server Error - security: - - api_key: - - EDITOR - - tokenizer: - - EDITOR - summary: Create ingestion key for workspace - tags: - - gateway - /api/v2/gateway/ingestion_keys/{keyId}: - delete: - deprecated: false - description: This endpoint deletes an ingestion key for the workspace - operationId: DeleteIngestionKey - parameters: - - in: path - name: keyId - required: true - schema: - type: string - responses: - "204": - description: No Content + description: Bad Request "401": content: application/json: @@ -5710,30 +7846,44 @@ paths: description: Internal Server Error security: - api_key: - - EDITOR + - VIEWER - tokenizer: - - EDITOR - summary: Delete ingestion key for workspace + - VIEWER + summary: Get metrics statistics tags: - - gateway - patch: + - metrics + /api/v2/metrics/treemap: + post: deprecated: false - description: This endpoint updates an ingestion key for the workspace - operationId: UpdateIngestionKey - parameters: - - in: path - name: keyId - required: true - schema: - type: string + description: This endpoint returns a treemap visualization showing the proportional + distribution of metrics by sample count or time series count + operationId: GetMetricsTreemap requestBody: content: application/json: schema: - $ref: '#/components/schemas/GatewaytypesPostableIngestionKey' + $ref: '#/components/schemas/MetricsexplorertypesTreemapRequest' responses: - "204": - description: No Content + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/MetricsexplorertypesTreemapResponse' + status: + type: string + required: + - status + - data + type: object + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request "401": content: application/json: @@ -5754,43 +7904,32 @@ paths: description: Internal Server Error security: - api_key: - - EDITOR + - VIEWER - tokenizer: - - EDITOR - summary: Update ingestion key for workspace - tags: - - gateway - /api/v2/gateway/ingestion_keys/{keyId}/limits: - post: - deprecated: false - description: This endpoint creates an ingestion key limit - operationId: CreateIngestionKeyLimit - parameters: - - in: path - name: keyId - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/GatewaytypesPostableIngestionKeyLimit' + - VIEWER + summary: Get metrics treemap + tags: + - metrics + /api/v2/orgs/me: + get: + deprecated: false + description: This endpoint returns the organization I belong to + operationId: GetMyOrganization responses: - "201": + "200": content: application/json: schema: properties: data: - $ref: '#/components/schemas/GatewaytypesGettableCreatedIngestionKeyLimit' + $ref: '#/components/schemas/TypesOrganization' status: type: string required: - status - data type: object - description: Created + description: OK "401": content: application/json: @@ -5811,26 +7950,30 @@ paths: description: Internal Server Error security: - api_key: - - EDITOR + - ADMIN - tokenizer: - - EDITOR - summary: Create limit for the ingestion key + - ADMIN + summary: Get my organization tags: - - gateway - /api/v2/gateway/ingestion_keys/limits/{limitId}: - delete: + - orgs + put: deprecated: false - description: This endpoint deletes an ingestion key limit - operationId: DeleteIngestionKeyLimit - parameters: - - in: path - name: limitId - required: true - schema: - type: string + description: This endpoint updates the organization I belong to + operationId: UpdateMyOrganization + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TypesOrganization' responses: "204": description: No Content + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request "401": content: application/json: @@ -5843,6 +7986,12 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden + "409": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Conflict "500": content: application/json: @@ -5851,30 +8000,75 @@ paths: description: Internal Server Error security: - api_key: - - EDITOR + - ADMIN - tokenizer: - - EDITOR - summary: Delete limit for the ingestion key + - ADMIN + summary: Update my organization tags: - - gateway - patch: + - orgs + /api/v2/readyz: + get: + operationId: Readyz + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/FactoryResponse' + status: + type: string + required: + - status + - data + type: object + description: OK + "503": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/FactoryResponse' + status: + type: string + required: + - status + - data + type: object + description: Service Unavailable + summary: Readiness check + tags: + - health + /api/v2/roles/{id}/users: + get: deprecated: false - description: This endpoint updates an ingestion key limit - operationId: UpdateIngestionKeyLimit + description: This endpoint returns the users having the role by role id + operationId: GetUsersByRoleID parameters: - in: path - name: limitId + name: id required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/GatewaytypesUpdatableIngestionKeyLimit' responses: - "204": - description: No Content + "200": + content: + application/json: + schema: + properties: + data: + items: + $ref: '#/components/schemas/TypesUser' + type: array + status: + type: string + required: + - status + - data + type: object + description: OK "401": content: application/json: @@ -5887,6 +8081,12 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found "500": content: application/json: @@ -5895,31 +8095,62 @@ paths: description: Internal Server Error security: - api_key: - - EDITOR + - ADMIN - tokenizer: - - EDITOR - summary: Update limit for the ingestion key + - ADMIN + summary: Get users by role id tags: - - gateway - /api/v2/gateway/ingestion_keys/search: + - users + /api/v2/rules/{id}/history/filter_keys: get: deprecated: false - description: This endpoint returns the ingestion keys for a workspace - operationId: SearchIngestionKeys + description: Returns distinct label keys from rule history entries for the selected + range. + operationId: GetRuleHistoryFilterKeys parameters: - in: query - name: name - required: true + name: signal schema: - type: string + $ref: '#/components/schemas/TelemetrytypesSignal' - in: query - name: page + name: source + schema: + $ref: '#/components/schemas/TelemetrytypesSource' + - in: query + name: limit schema: type: integer - in: query - name: per_page + name: startUnixMilli + schema: + format: int64 + type: integer + - in: query + name: endUnixMilli schema: + format: int64 type: integer + - in: query + name: fieldContext + schema: + $ref: '#/components/schemas/TelemetrytypesFieldContext' + - in: query + name: fieldDataType + schema: + $ref: '#/components/schemas/TelemetrytypesFieldDataType' + - in: query + name: metricName + schema: + type: string + - in: query + name: searchText + schema: + type: string + - in: path + name: id + required: true + schema: + type: string responses: "200": content: @@ -5927,7 +8158,7 @@ paths: schema: properties: data: - $ref: '#/components/schemas/GatewaytypesGettableIngestionKeys' + $ref: '#/components/schemas/TelemetrytypesGettableFieldKeys' status: type: string required: @@ -5935,6 +8166,12 @@ paths: - data type: object description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request "401": content: application/json: @@ -5955,39 +8192,68 @@ paths: description: Internal Server Error security: - api_key: - - EDITOR + - VIEWER - tokenizer: - - EDITOR - summary: Search ingestion keys for workspace + - VIEWER + summary: Get rule history filter keys tags: - - gateway - /api/v2/metrics: + - rules + /api/v2/rules/{id}/history/filter_values: get: deprecated: false - description: This endpoint returns a list of distinct metric names within the - specified time range - operationId: ListMetrics + description: Returns distinct label values for a given key from rule history + entries. + operationId: GetRuleHistoryFilterValues parameters: - in: query - name: start + name: signal + schema: + $ref: '#/components/schemas/TelemetrytypesSignal' + - in: query + name: source + schema: + $ref: '#/components/schemas/TelemetrytypesSource' + - in: query + name: limit schema: - nullable: true type: integer - in: query - name: end + name: startUnixMilli schema: - nullable: true + format: int64 type: integer - in: query - name: limit + name: endUnixMilli schema: + format: int64 type: integer + - in: query + name: fieldContext + schema: + $ref: '#/components/schemas/TelemetrytypesFieldContext' + - in: query + name: fieldDataType + schema: + $ref: '#/components/schemas/TelemetrytypesFieldDataType' + - in: query + name: metricName + schema: + type: string - in: query name: searchText schema: type: string - in: query - name: source + name: name + schema: + type: string + - in: query + name: existingQuery + schema: + type: string + - in: path + name: id + required: true schema: type: string responses: @@ -5997,7 +8263,7 @@ paths: schema: properties: data: - $ref: '#/components/schemas/MetricsexplorertypesListMetricsResponse' + $ref: '#/components/schemas/TelemetrytypesGettableFieldValues' status: type: string required: @@ -6034,17 +8300,30 @@ paths: - VIEWER - tokenizer: - VIEWER - summary: List metric names + summary: Get rule history filter values tags: - - metrics - /api/v2/metrics/{metric_name}/alerts: + - rules + /api/v2/rules/{id}/history/overall_status: get: deprecated: false - description: This endpoint returns associated alerts for a specified metric - operationId: GetMetricAlerts + description: Returns overall firing/inactive intervals for a rule in the selected + time range. + operationId: GetRuleHistoryOverallStatus parameters: + - in: query + name: start + required: true + schema: + format: int64 + type: integer + - in: query + name: end + required: true + schema: + format: int64 + type: integer - in: path - name: metric_name + name: id required: true schema: type: string @@ -6055,7 +8334,10 @@ paths: schema: properties: data: - $ref: '#/components/schemas/MetricsexplorertypesMetricAlertsResponse' + items: + $ref: '#/components/schemas/RulestatehistorytypesGettableRuleStateWindow' + nullable: true + type: array status: type: string required: @@ -6081,12 +8363,6 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "404": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found "500": content: application/json: @@ -6098,28 +8374,30 @@ paths: - VIEWER - tokenizer: - VIEWER - summary: Get metric alerts + summary: Get rule overall status timeline tags: - - metrics - /api/v2/metrics/{metric_name}/attributes: + - rules + /api/v2/rules/{id}/history/stats: get: deprecated: false - description: This endpoint returns attribute keys and their unique values for - a specified metric - operationId: GetMetricAttributes + description: Returns trigger and resolution statistics for a rule in the selected + time range. + operationId: GetRuleHistoryStats parameters: - in: query name: start + required: true schema: - nullable: true + format: int64 type: integer - in: query name: end + required: true schema: - nullable: true + format: int64 type: integer - in: path - name: metric_name + name: id required: true schema: type: string @@ -6130,7 +8408,7 @@ paths: schema: properties: data: - $ref: '#/components/schemas/MetricsexplorertypesMetricAttributesResponse' + $ref: '#/components/schemas/RulestatehistorytypesGettableRuleStateHistoryStats' status: type: string required: @@ -6156,12 +8434,6 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "404": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found "500": content: application/json: @@ -6173,17 +8445,50 @@ paths: - VIEWER - tokenizer: - VIEWER - summary: Get metric attributes + summary: Get rule history stats tags: - - metrics - /api/v2/metrics/{metric_name}/dashboards: + - rules + /api/v2/rules/{id}/history/timeline: get: deprecated: false - description: This endpoint returns associated dashboards for a specified metric - operationId: GetMetricDashboards + description: Returns paginated timeline entries for rule state transitions. + operationId: GetRuleHistoryTimeline parameters: + - in: query + name: start + required: true + schema: + format: int64 + type: integer + - in: query + name: end + required: true + schema: + format: int64 + type: integer + - in: query + name: state + schema: + $ref: '#/components/schemas/RuletypesAlertState' + - in: query + name: filterExpression + schema: + type: string + - in: query + name: limit + schema: + format: int64 + type: integer + - in: query + name: order + schema: + $ref: '#/components/schemas/Querybuildertypesv5OrderDirection' + - in: query + name: cursor + schema: + type: string - in: path - name: metric_name + name: id required: true schema: type: string @@ -6194,7 +8499,7 @@ paths: schema: properties: data: - $ref: '#/components/schemas/MetricsexplorertypesMetricDashboardsResponse' + $ref: '#/components/schemas/RulestatehistorytypesGettableRuleStateTimeline' status: type: string required: @@ -6220,12 +8525,6 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "404": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found "500": content: application/json: @@ -6237,18 +8536,30 @@ paths: - VIEWER - tokenizer: - VIEWER - summary: Get metric dashboards + summary: Get rule history timeline tags: - - metrics - /api/v2/metrics/{metric_name}/highlights: + - rules + /api/v2/rules/{id}/history/top_contributors: get: deprecated: false - description: This endpoint returns highlights like number of datapoints, totaltimeseries, - active time series, last received time for a specified metric - operationId: GetMetricHighlights + description: Returns top label combinations contributing to rule firing in the + selected time range. + operationId: GetRuleHistoryTopContributors parameters: + - in: query + name: start + required: true + schema: + format: int64 + type: integer + - in: query + name: end + required: true + schema: + format: int64 + type: integer - in: path - name: metric_name + name: id required: true schema: type: string @@ -6259,7 +8570,10 @@ paths: schema: properties: data: - $ref: '#/components/schemas/MetricsexplorertypesMetricHighlightsResponse' + items: + $ref: '#/components/schemas/RulestatehistorytypesGettableRuleStateHistoryContributor' + nullable: true + type: array status: type: string required: @@ -6285,12 +8599,6 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "404": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found "500": content: application/json: @@ -6302,21 +8610,51 @@ paths: - VIEWER - tokenizer: - VIEWER - summary: Get metric highlights + summary: Get top contributors to rule firing tags: - - metrics - /api/v2/metrics/{metric_name}/metadata: + - rules + /api/v2/sessions: + delete: + deprecated: false + description: This endpoint deletes the session + operationId: DeleteSession + responses: + "204": + description: No Content + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "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: + - tokenizer: [] + summary: Delete session + tags: + - sessions + /api/v2/sessions/context: get: deprecated: false - description: This endpoint returns metadata information like metric description, - unit, type, temporality, monotonicity for a specified metric - operationId: GetMetricMetadata - parameters: - - in: path - name: metric_name - required: true - schema: - type: string + description: This endpoint returns the context for the session + operationId: GetSessionContext responses: "200": content: @@ -6324,7 +8662,7 @@ paths: schema: properties: data: - $ref: '#/components/schemas/MetricsexplorertypesMetricMetadata' + $ref: '#/components/schemas/AuthtypesSessionContext' status: type: string required: @@ -6338,18 +8676,46 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Bad Request - "401": + "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Unauthorized - "403": + description: Internal Server Error + summary: Get session context + tags: + - sessions + /api/v2/sessions/email_password: + post: + deprecated: false + description: This endpoint creates a session for a user using email and password. + operationId: CreateSessionByEmailPassword + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AuthtypesPostableEmailPasswordSession' + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/AuthtypesGettableToken' + status: + type: string + required: + - status + - data + type: object + description: OK + "400": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Forbidden + description: Bad Request "404": content: application/json: @@ -6362,36 +8728,33 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error - security: - - api_key: - - VIEWER - - tokenizer: - - VIEWER - summary: Get metric metadata + summary: Create session by email and password tags: - - metrics + - sessions + /api/v2/sessions/rotate: post: deprecated: false - description: This endpoint helps to update metadata information like metric - description, unit, type, temporality, monotonicity for a specified metric - operationId: UpdateMetricMetadata - parameters: - - in: path - name: metric_name - required: true - schema: - type: string + description: This endpoint rotates the session + operationId: RotateSession requestBody: content: application/json: schema: - $ref: '#/components/schemas/MetricsexplorertypesUpdateMetricMetadataRequest' + $ref: '#/components/schemas/AuthtypesPostableRotateToken' responses: "200": content: application/json: schema: - type: string + properties: + data: + $ref: '#/components/schemas/AuthtypesGettableToken' + status: + type: string + required: + - status + - data + type: object description: OK "400": content: @@ -6399,6 +8762,37 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Bad Request + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + summary: Rotate session + tags: + - sessions + /api/v2/users: + get: + deprecated: false + description: This endpoint lists all users for the organization + operationId: ListUsers + responses: + "200": + content: + application/json: + schema: + properties: + data: + items: + $ref: '#/components/schemas/TypesUser' + type: array + status: + type: string + required: + - status + - data + type: object + description: OK "401": content: application/json: @@ -6419,23 +8813,23 @@ paths: description: Internal Server Error security: - api_key: - - EDITOR - - tokenizer: - - EDITOR - summary: Update metric metadata + - ADMIN + - tokenizer: + - ADMIN + summary: List users v2 tags: - - metrics - /api/v2/metrics/stats: - post: + - users + /api/v2/users/{id}: + get: deprecated: false - description: This endpoint provides list of metrics with their number of samples - and timeseries for the given time range - operationId: GetMetricsStats - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/MetricsexplorertypesStatsRequest' + description: This endpoint returns the user by id + operationId: GetUser + parameters: + - in: path + name: id + required: true + schema: + type: string responses: "200": content: @@ -6443,7 +8837,7 @@ paths: schema: properties: data: - $ref: '#/components/schemas/MetricsexplorertypesStatsResponse' + $ref: '#/components/schemas/AuthtypesUserWithRoles' status: type: string required: @@ -6451,12 +8845,6 @@ paths: - data type: object description: OK - "400": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request "401": content: application/json: @@ -6469,6 +8857,12 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found "500": content: application/json: @@ -6477,38 +8871,30 @@ paths: description: Internal Server Error security: - api_key: - - VIEWER + - ADMIN - tokenizer: - - VIEWER - summary: Get metrics statistics + - ADMIN + summary: Get user by user id tags: - - metrics - /api/v2/metrics/treemap: - post: + - users + put: deprecated: false - description: This endpoint returns a treemap visualization showing the proportional - distribution of metrics by sample count or time series count - operationId: GetMetricsTreemap + description: This endpoint updates the user by id + operationId: UpdateUser + parameters: + - in: path + name: id + required: true + schema: + type: string requestBody: content: application/json: schema: - $ref: '#/components/schemas/MetricsexplorertypesTreemapRequest' + $ref: '#/components/schemas/TypesUpdatableUser' responses: - "200": - content: - application/json: - schema: - properties: - data: - $ref: '#/components/schemas/MetricsexplorertypesTreemapResponse' - status: - type: string - required: - - status - - data - type: object - description: OK + "204": + description: No Content "400": content: application/json: @@ -6527,6 +8913,12 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found "500": content: application/json: @@ -6535,17 +8927,23 @@ paths: description: Internal Server Error security: - api_key: - - VIEWER + - ADMIN - tokenizer: - - VIEWER - summary: Get metrics treemap + - ADMIN + summary: Update user v2 tags: - - metrics - /api/v2/orgs/me: + - users + /api/v2/users/{id}/roles: get: deprecated: false - description: This endpoint returns the organization I belong to - operationId: GetMyOrganization + description: This endpoint returns the user roles by user id + operationId: GetRolesByUserID + parameters: + - in: path + name: id + required: true + schema: + type: string responses: "200": content: @@ -6553,7 +8951,9 @@ paths: schema: properties: data: - $ref: '#/components/schemas/TypesOrganization' + items: + $ref: '#/components/schemas/AuthtypesRole' + type: array status: type: string required: @@ -6573,6 +8973,12 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found "500": content: application/json: @@ -6584,27 +8990,27 @@ paths: - ADMIN - tokenizer: - ADMIN - summary: Get my organization + summary: Get user roles tags: - - orgs - put: + - users + post: deprecated: false - description: This endpoint updates the organization I belong to - operationId: UpdateMyOrganization + description: This endpoint assigns the role to the user roles by user id + operationId: SetRoleByUserID + parameters: + - in: path + name: id + required: true + schema: + type: string requestBody: content: application/json: schema: - $ref: '#/components/schemas/TypesOrganization' + $ref: '#/components/schemas/TypesPostableRole' responses: - "204": - description: No Content - "400": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request + "200": + description: OK "401": content: application/json: @@ -6617,12 +9023,12 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "409": + "404": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Conflict + description: Not Found "500": content: application/json: @@ -6634,23 +9040,29 @@ paths: - ADMIN - tokenizer: - ADMIN - summary: Update my organization + summary: Set user roles tags: - - orgs - /api/v2/sessions: + - users + /api/v2/users/{id}/roles/{roleId}: delete: deprecated: false - description: This endpoint deletes the session - operationId: DeleteSession + description: This endpoint removes a role from the user by user id and role + id + operationId: RemoveUserRoleByUserIDAndRoleID + parameters: + - in: path + name: id + required: true + schema: + type: string + - in: path + name: roleId + required: true + schema: + type: string responses: "204": description: No Content - "400": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request "401": content: application/json: @@ -6663,62 +9075,31 @@ paths: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Forbidden - "500": - content: - application/json: - schema: - $ref: '#/components/schemas/RenderErrorResponse' - description: Internal Server Error - security: - - tokenizer: [] - summary: Delete session - tags: - - sessions - /api/v2/sessions/context: - get: - deprecated: false - description: This endpoint returns the context for the session - operationId: GetSessionContext - responses: - "200": - content: - application/json: - schema: - properties: - data: - $ref: '#/components/schemas/AuthtypesSessionContext' - status: - type: string - required: - - status - - data - type: object - description: OK - "400": + "404": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request + description: Not Found "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error - summary: Get session context + security: + - api_key: + - ADMIN + - tokenizer: + - ADMIN + summary: Remove a role from user tags: - - sessions - /api/v2/sessions/email_password: - post: + - users + /api/v2/users/me: + get: deprecated: false - description: This endpoint creates a session for a user using email and password. - operationId: CreateSessionByEmailPassword - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/AuthtypesPostableEmailPasswordSession' + description: This endpoint returns the user I belong to + operationId: GetMyUser responses: "200": content: @@ -6726,7 +9107,7 @@ paths: schema: properties: data: - $ref: '#/components/schemas/AuthtypesGettableToken' + $ref: '#/components/schemas/AuthtypesUserWithRoles' status: type: string required: @@ -6734,67 +9115,64 @@ paths: - data type: object description: OK - "400": + "401": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request - "404": + description: Unauthorized + "403": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Not Found + description: Forbidden "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error - summary: Create session by email and password + security: + - tokenizer: [] + summary: Get my user v2 tags: - - sessions - /api/v2/sessions/rotate: - post: + - users + put: deprecated: false - description: This endpoint rotates the session - operationId: RotateSession + description: This endpoint updates the user I belong to + operationId: UpdateMyUserV2 requestBody: content: application/json: schema: - $ref: '#/components/schemas/AuthtypesPostableRotateToken' + $ref: '#/components/schemas/TypesUpdatableUser' responses: - "200": + "204": + description: No Content + "401": content: application/json: schema: - properties: - data: - $ref: '#/components/schemas/AuthtypesGettableToken' - status: - type: string - required: - - status - - data - type: object - description: OK - "400": + $ref: '#/components/schemas/RenderErrorResponse' + description: Unauthorized + "403": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' - description: Bad Request + description: Forbidden "500": content: application/json: schema: $ref: '#/components/schemas/RenderErrorResponse' description: Internal Server Error - summary: Rotate session + security: + - tokenizer: [] + summary: Update my user v2 tags: - - sessions + - users /api/v2/zeus/hosts: get: deprecated: false diff --git a/docs/contributing/go/handler.md b/docs/contributing/go/handler.md index 91a2d50431f..679f0971dff 100644 --- a/docs/contributing/go/handler.md +++ b/docs/contributing/go/handler.md @@ -123,6 +123,7 @@ if err := router.Handle("/api/v1/things", handler.New( Description: "This endpoint creates a thing", Request: new(types.PostableThing), RequestContentType: "application/json", + RequestQuery: new(types.QueryableThing), Response: new(types.GettableThing), ResponseContentType: "application/json", SuccessStatusCode: http.StatusCreated, @@ -155,6 +156,8 @@ The `handler.New` function ties the HTTP handler to OpenAPI metadata via `OpenAP - **Request / RequestContentType**: - `Request` is a Go type that describes the request body or form. - `RequestContentType` is usually `"application/json"` or `"application/x-www-form-urlencoded"` (for callbacks like SAML). +- **RequestQuery**: + - `RequestQuery` is a Go type that descirbes query url params. - **RequestExamples**: An array of `handler.OpenAPIExample` that provide concrete request payloads in the generated spec. See [Adding request examples](#adding-request-examples) below. - **Response / ResponseContentType**: - `Response` is the Go type for the successful response payload. diff --git a/docs/contributing/onboarding.md b/docs/contributing/onboarding.md index b2369334396..aff28c35cda 100644 --- a/docs/contributing/onboarding.md +++ b/docs/contributing/onboarding.md @@ -273,6 +273,7 @@ Options can be simple (direct link) or nested (with another question): - Place logo files in `public/Logos/` - Use SVG format - Reference as `"/Logos/your-logo.svg"` +- **Fetching Icons**: New icons can be easily fetched from [OpenBrand](https://openbrand.sh/). Use the pattern `https://openbrand.sh/?url=`, where `` is the URL-encoded link to the service's website. For example, to get Render's logo, use [https://openbrand.sh/?url=https%3A%2F%2Frender.com](https://openbrand.sh/?url=https%3A%2F%2Frender.com). - **Optimize new SVGs**: Run any newly downloaded SVGs through an optimizer like [SVGOMG (svgo)](https://svgomg.net/) or use `npx svgo public/Logos/your-logo.svg` to minimise their size before committing. ### 4. Links diff --git a/ee/anomaly/hourly.go b/ee/anomaly/hourly.go index 95a21bdaa59..d1aaca14d71 100644 --- a/ee/anomaly/hourly.go +++ b/ee/anomaly/hourly.go @@ -16,7 +16,7 @@ func (hp *HourlyProvider) GetBaseSeasonalProvider() *BaseSeasonalProvider { return &hp.BaseSeasonalProvider } -// NewHourlyProvider now uses the generic option type +// NewHourlyProvider now uses the generic option type. func NewHourlyProvider(opts ...GenericProviderOption[*HourlyProvider]) *HourlyProvider { hp := &HourlyProvider{ BaseSeasonalProvider: BaseSeasonalProvider{}, diff --git a/ee/anomaly/params.go b/ee/anomaly/params.go index 9a1aa8a71ce..7824b501a0d 100644 --- a/ee/anomaly/params.go +++ b/ee/anomaly/params.go @@ -32,7 +32,7 @@ func (s Seasonality) IsValid() bool { } type AnomaliesRequest struct { - Params qbtypes.QueryRangeRequest + Params *qbtypes.QueryRangeRequest Seasonality Seasonality } @@ -47,7 +47,7 @@ type AnomaliesResponse struct { // | | // (rounded value for past peiod) + (seasonal growth) // -// score = abs(value - prediction) / stddev (current_season_query) +// score = abs(value - prediction) / stddev (current_season_query). type anomalyQueryParams struct { // CurrentPeriodQuery is the query range params for period user is looking at or eval window // Example: (now-5m, now), (now-30m, now), (now-1h, now) @@ -81,7 +81,7 @@ type anomalyQueryParams struct { Past3SeasonQuery qbtypes.QueryRangeRequest } -func prepareAnomalyQueryParams(req qbtypes.QueryRangeRequest, seasonality Seasonality) *anomalyQueryParams { +func prepareAnomalyQueryParams(req *qbtypes.QueryRangeRequest, seasonality Seasonality) *anomalyQueryParams { start := req.Start end := req.End diff --git a/ee/anomaly/seasonal.go b/ee/anomaly/seasonal.go index ffee2c1d1d4..875a9f2b8c6 100644 --- a/ee/anomaly/seasonal.go +++ b/ee/anomaly/seasonal.go @@ -18,12 +18,12 @@ var ( movingAvgWindowSize = 7 ) -// BaseProvider is an interface that includes common methods for all provider types +// BaseProvider is an interface that includes common methods for all provider types. type BaseProvider interface { GetBaseSeasonalProvider() *BaseSeasonalProvider } -// GenericProviderOption is a generic type for provider options +// GenericProviderOption is a generic type for provider options. type GenericProviderOption[T BaseProvider] func(T) func WithQuerier[T BaseProvider](querier querier.Querier) GenericProviderOption[T] { @@ -74,37 +74,37 @@ func (p *BaseSeasonalProvider) getResults(ctx context.Context, orgID valuer.UUID instrumentationtypes.CodeFunctionName: "getResults", }) // TODO(srikanthccv): parallelize this? - p.logger.InfoContext(ctx, "fetching results for current period", "anomaly_current_period_query", params.CurrentPeriodQuery) + p.logger.InfoContext(ctx, "fetching results for current period", slog.Any("anomaly_current_period_query", params.CurrentPeriodQuery)) currentPeriodResults, err := p.querier.QueryRange(ctx, orgID, ¶ms.CurrentPeriodQuery) if err != nil { return nil, err } - p.logger.InfoContext(ctx, "fetching results for past period", "anomaly_past_period_query", params.PastPeriodQuery) + p.logger.InfoContext(ctx, "fetching results for past period", slog.Any("anomaly_past_period_query", params.PastPeriodQuery)) pastPeriodResults, err := p.querier.QueryRange(ctx, orgID, ¶ms.PastPeriodQuery) if err != nil { return nil, err } - p.logger.InfoContext(ctx, "fetching results for current season", "anomaly_current_season_query", params.CurrentSeasonQuery) + p.logger.InfoContext(ctx, "fetching results for current season", slog.Any("anomaly_current_season_query", params.CurrentSeasonQuery)) currentSeasonResults, err := p.querier.QueryRange(ctx, orgID, ¶ms.CurrentSeasonQuery) if err != nil { return nil, err } - p.logger.InfoContext(ctx, "fetching results for past season", "anomaly_past_season_query", params.PastSeasonQuery) + p.logger.InfoContext(ctx, "fetching results for past season", slog.Any("anomaly_past_season_query", params.PastSeasonQuery)) pastSeasonResults, err := p.querier.QueryRange(ctx, orgID, ¶ms.PastSeasonQuery) if err != nil { return nil, err } - p.logger.InfoContext(ctx, "fetching results for past 2 season", "anomaly_past_2season_query", params.Past2SeasonQuery) + p.logger.InfoContext(ctx, "fetching results for past 2 season", slog.Any("anomaly_past_2season_query", params.Past2SeasonQuery)) past2SeasonResults, err := p.querier.QueryRange(ctx, orgID, ¶ms.Past2SeasonQuery) if err != nil { return nil, err } - p.logger.InfoContext(ctx, "fetching results for past 3 season", "anomaly_past_3season_query", params.Past3SeasonQuery) + p.logger.InfoContext(ctx, "fetching results for past 3 season", slog.Any("anomaly_past_3season_query", params.Past3SeasonQuery)) past3SeasonResults, err := p.querier.QueryRange(ctx, orgID, ¶ms.Past3SeasonQuery) if err != nil { return nil, err @@ -121,7 +121,7 @@ func (p *BaseSeasonalProvider) getResults(ctx context.Context, orgID valuer.UUID } // getMatchingSeries gets the matching series from the query result -// for the given series +// for the given series. func (p *BaseSeasonalProvider) getMatchingSeries(_ context.Context, queryResult *qbtypes.TimeSeriesData, series *qbtypes.TimeSeries) *qbtypes.TimeSeries { if queryResult == nil || len(queryResult.Aggregations) == 0 || len(queryResult.Aggregations[0].Series) == 0 { return nil @@ -155,13 +155,14 @@ func (p *BaseSeasonalProvider) getStdDev(series *qbtypes.TimeSeries) float64 { avg := p.getAvg(series) var sum float64 for _, smpl := range series.Values { - sum += math.Pow(smpl.Value-avg, 2) + d := smpl.Value - avg + sum += d * d } return math.Sqrt(sum / float64(len(series.Values))) } // getMovingAvg gets the moving average for the given series -// for the given window size and start index +// for the given window size and start index. func (p *BaseSeasonalProvider) getMovingAvg(series *qbtypes.TimeSeries, movingAvgWindowSize, startIdx int) float64 { if series == nil || len(series.Values) == 0 { return 0 @@ -212,17 +213,17 @@ func (p *BaseSeasonalProvider) getPredictedSeries( if predictedValue < 0 { // this should not happen (except when the data has extreme outliers) // we will use the moving avg of the previous period series in this case - p.logger.WarnContext(ctx, "predicted value is less than 0 for series", "anomaly_predicted_value", predictedValue, "anomaly_labels", series.Labels) + p.logger.WarnContext(ctx, "predicted value is less than 0 for series", slog.Float64("anomaly_predicted_value", predictedValue), slog.Any("anomaly_labels", series.Labels)) predictedValue = p.getMovingAvg(prevSeries, movingAvgWindowSize, idx) } p.logger.DebugContext(ctx, "predicted value for series", - "anomaly_moving_avg", movingAvg, - "anomaly_avg", avg, - "anomaly_mean", mean, - "anomaly_labels", series.Labels, - "anomaly_predicted_value", predictedValue, - "anomaly_curr", curr.Value, + slog.Float64("anomaly_moving_avg", movingAvg), + slog.Float64("anomaly_avg", avg), + slog.Float64("anomaly_mean", mean), + slog.Any("anomaly_labels", series.Labels), + slog.Float64("anomaly_predicted_value", predictedValue), + slog.Float64("anomaly_curr", curr.Value), ) predictedSeries.Values = append(predictedSeries.Values, &qbtypes.TimeSeriesValue{ Timestamp: curr.Timestamp, @@ -236,7 +237,7 @@ func (p *BaseSeasonalProvider) getPredictedSeries( // getBounds gets the upper and lower bounds for the given series // for the given z score threshold // moving avg of the previous period series + z score threshold * std dev of the series -// moving avg of the previous period series - z score threshold * std dev of the series +// moving avg of the previous period series - z score threshold * std dev of the series. func (p *BaseSeasonalProvider) getBounds( series, predictedSeries, weekSeries *qbtypes.TimeSeries, zScoreThreshold float64, @@ -269,7 +270,7 @@ func (p *BaseSeasonalProvider) getBounds( // getExpectedValue gets the expected value for the given series // for the given index -// prevSeriesAvg + currentSeasonSeriesAvg - mean of past season series, past2 season series and past3 season series +// prevSeriesAvg + currentSeasonSeriesAvg - mean of past season series, past2 season series and past3 season series. func (p *BaseSeasonalProvider) getExpectedValue( _, prevSeries, currentSeasonSeries, pastSeasonSeries, past2SeasonSeries, past3SeasonSeries *qbtypes.TimeSeries, idx int, ) float64 { @@ -283,7 +284,7 @@ func (p *BaseSeasonalProvider) getExpectedValue( // getScore gets the anomaly score for the given series // for the given index -// (value - expectedValue) / std dev of the series +// (value - expectedValue) / std dev of the series. func (p *BaseSeasonalProvider) getScore( series, prevSeries, weekSeries, weekPrevSeries, past2SeasonSeries, past3SeasonSeries *qbtypes.TimeSeries, value float64, idx int, ) float64 { @@ -296,7 +297,7 @@ func (p *BaseSeasonalProvider) getScore( // getAnomalyScores gets the anomaly scores for the given series // for the given index -// (value - expectedValue) / std dev of the series +// (value - expectedValue) / std dev of the series. func (p *BaseSeasonalProvider) getAnomalyScores( series, prevSeries, currentSeasonSeries, pastSeasonSeries, past2SeasonSeries, past3SeasonSeries *qbtypes.TimeSeries, ) *qbtypes.TimeSeries { @@ -412,7 +413,7 @@ func (p *BaseSeasonalProvider) getAnomalies(ctx context.Context, orgID valuer.UU past3SeasonSeries := p.getMatchingSeries(ctx, past3SeasonResult, series) stdDev := p.getStdDev(currentSeasonSeries) - p.logger.InfoContext(ctx, "calculated standard deviation for series", "anomaly_std_dev", stdDev, "anomaly_labels", series.Labels) + p.logger.InfoContext(ctx, "calculated standard deviation for series", slog.Float64("anomaly_std_dev", stdDev), slog.Any("anomaly_labels", series.Labels)) prevSeriesAvg := p.getAvg(pastPeriodSeries) currentSeasonSeriesAvg := p.getAvg(currentSeasonSeries) @@ -420,12 +421,12 @@ func (p *BaseSeasonalProvider) getAnomalies(ctx context.Context, orgID valuer.UU past2SeasonSeriesAvg := p.getAvg(past2SeasonSeries) past3SeasonSeriesAvg := p.getAvg(past3SeasonSeries) p.logger.InfoContext(ctx, "calculated mean for series", - "anomaly_prev_series_avg", prevSeriesAvg, - "anomaly_current_season_series_avg", currentSeasonSeriesAvg, - "anomaly_past_season_series_avg", pastSeasonSeriesAvg, - "anomaly_past_2season_series_avg", past2SeasonSeriesAvg, - "anomaly_past_3season_series_avg", past3SeasonSeriesAvg, - "anomaly_labels", series.Labels, + slog.Float64("anomaly_prev_series_avg", prevSeriesAvg), + slog.Float64("anomaly_current_season_series_avg", currentSeasonSeriesAvg), + slog.Float64("anomaly_past_season_series_avg", pastSeasonSeriesAvg), + slog.Float64("anomaly_past_2season_series_avg", past2SeasonSeriesAvg), + slog.Float64("anomaly_past_3season_series_avg", past3SeasonSeriesAvg), + slog.Any("anomaly_labels", series.Labels), ) predictedSeries := p.getPredictedSeries( diff --git a/ee/auditor/otlphttpauditor/export.go b/ee/auditor/otlphttpauditor/export.go new file mode 100644 index 00000000000..9e4fd68f4ad --- /dev/null +++ b/ee/auditor/otlphttpauditor/export.go @@ -0,0 +1,143 @@ +package otlphttpauditor + +import ( + "bytes" + "context" + "io" + "log/slog" + "net/http" + + "github.com/SigNoz/signoz/pkg/auditor" + "github.com/SigNoz/signoz/pkg/errors" + "github.com/SigNoz/signoz/pkg/types/audittypes" + collogspb "go.opentelemetry.io/proto/otlp/collector/logs/v1" + "google.golang.org/protobuf/proto" + + spb "google.golang.org/genproto/googleapis/rpc/status" +) + +const ( + maxHTTPResponseReadBytes int64 = 64 * 1024 + protobufContentType string = "application/x-protobuf" +) + +func (provider *provider) export(ctx context.Context, events []audittypes.AuditEvent) error { + logs := audittypes.NewPLogsFromAuditEvents(events, "signoz", provider.build.Version(), "signoz.audit") + + request, err := provider.marshaler.MarshalLogs(logs) + if err != nil { + return errors.Wrapf(err, errors.TypeInternal, auditor.ErrCodeAuditExportFailed, "failed to marshal audit logs") + } + + if err := provider.send(ctx, request); err != nil { + provider.settings.Logger().ErrorContext(ctx, "audit export failed", errors.Attr(err), slog.Int("dropped_log_records", len(events))) + return err + } + + return nil +} + +// Posts a protobuf-encoded OTLP request to the configured endpoint. +// Retries are handled by the underlying heimdall HTTP client. +// Ref: https://github.com/open-telemetry/opentelemetry-collector/blob/main/exporter/otlphttpexporter/otlp.go +func (provider *provider) send(ctx context.Context, body []byte) error { + req, err := http.NewRequestWithContext(ctx, http.MethodPost, provider.config.OTLPHTTP.Endpoint.String(), bytes.NewReader(body)) + if err != nil { + return err + } + + req.Header.Set("Content-Type", protobufContentType) + + res, err := provider.httpClient.Do(req) + if err != nil { + return err + } + + defer func() { + _, _ = io.CopyN(io.Discard, res.Body, maxHTTPResponseReadBytes) + res.Body.Close() + }() + + if res.StatusCode >= 200 && res.StatusCode <= 299 { + provider.onSuccess(ctx, res) + return nil + } + + return provider.onErr(res) +} + +// Ref: https://github.com/open-telemetry/opentelemetry-collector/blob/01b07fcbb7a253bd996c290dcae6166e71d13732/exporter/otlphttpexporter/otlp.go#L403. +func (provider *provider) onSuccess(ctx context.Context, res *http.Response) { + resBytes, err := readResponseBody(res) + if err != nil || resBytes == nil { + return + } + + exportResponse := &collogspb.ExportLogsServiceResponse{} + if err := proto.Unmarshal(resBytes, exportResponse); err != nil { + return + } + + ps := exportResponse.GetPartialSuccess() + if ps == nil { + return + } + + if ps.GetErrorMessage() != "" || ps.GetRejectedLogRecords() != 0 { + provider.settings.Logger().WarnContext(ctx, "partial success response", slog.String("message", ps.GetErrorMessage()), slog.Int64("dropped_log_records", ps.GetRejectedLogRecords())) + } +} + +func (provider *provider) onErr(res *http.Response) error { + status := readResponseStatus(res) + + if status != nil { + return errors.Newf(errors.TypeInternal, auditor.ErrCodeAuditExportFailed, "request to %s responded with status code %d, Message=%s, Details=%v", provider.config.OTLPHTTP.Endpoint.String(), res.StatusCode, status.Message, status.Details) + } + + return errors.Newf(errors.TypeInternal, auditor.ErrCodeAuditExportFailed, "request to %s responded with status code %d", provider.config.OTLPHTTP.Endpoint.String(), res.StatusCode) +} + +// Reads at most maxHTTPResponseReadBytes from the response body. +// Ref: https://github.com/open-telemetry/opentelemetry-collector/blob/01b07fcbb7a253bd996c290dcae6166e71d13732/exporter/otlphttpexporter/otlp.go#L275. +func readResponseBody(resp *http.Response) ([]byte, error) { + if resp.ContentLength == 0 { + return nil, nil + } + + maxRead := resp.ContentLength + if maxRead == -1 || maxRead > maxHTTPResponseReadBytes { + maxRead = maxHTTPResponseReadBytes + } + + protoBytes := make([]byte, maxRead) + n, err := io.ReadFull(resp.Body, protoBytes) + if n == 0 && (err == nil || errors.Is(err, io.EOF)) { + return nil, nil + } + if err != nil && !errors.Is(err, io.ErrUnexpectedEOF) { + return nil, err + } + + return protoBytes[:n], nil +} + +// Decodes a protobuf-encoded Status from 4xx/5xx response bodies. Returns nil if the response is empty or cannot be decoded. +// Ref: https://github.com/open-telemetry/opentelemetry-collector/blob/01b07fcbb7a253bd996c290dcae6166e71d13732/exporter/otlphttpexporter/otlp.go#L310. +func readResponseStatus(resp *http.Response) *spb.Status { + if resp.StatusCode < 400 || resp.StatusCode > 599 { + return nil + } + + respBytes, err := readResponseBody(resp) + if err != nil || respBytes == nil { + return nil + } + + respStatus := &spb.Status{} + if err := proto.Unmarshal(respBytes, respStatus); err != nil { + return nil + } + + return respStatus +} diff --git a/ee/auditor/otlphttpauditor/provider.go b/ee/auditor/otlphttpauditor/provider.go new file mode 100644 index 00000000000..c73c2715987 --- /dev/null +++ b/ee/auditor/otlphttpauditor/provider.go @@ -0,0 +1,97 @@ +package otlphttpauditor + +import ( + "context" + + "github.com/SigNoz/signoz/pkg/auditor" + "github.com/SigNoz/signoz/pkg/auditor/auditorserver" + "github.com/SigNoz/signoz/pkg/factory" + client "github.com/SigNoz/signoz/pkg/http/client" + "github.com/SigNoz/signoz/pkg/licensing" + "github.com/SigNoz/signoz/pkg/types/audittypes" + "github.com/SigNoz/signoz/pkg/version" + "go.opentelemetry.io/collector/pdata/plog" +) + +var _ auditor.Auditor = (*provider)(nil) + +type provider struct { + settings factory.ScopedProviderSettings + config auditor.Config + licensing licensing.Licensing + build version.Build + server *auditorserver.Server + marshaler plog.ProtoMarshaler + httpClient *client.Client +} + +func NewFactory(licensing licensing.Licensing, build version.Build) factory.ProviderFactory[auditor.Auditor, auditor.Config] { + return factory.NewProviderFactory(factory.MustNewName("otlphttp"), func(ctx context.Context, providerSettings factory.ProviderSettings, config auditor.Config) (auditor.Auditor, error) { + return newProvider(ctx, providerSettings, config, licensing, build) + }) +} + +func newProvider(_ context.Context, providerSettings factory.ProviderSettings, config auditor.Config, licensing licensing.Licensing, build version.Build) (auditor.Auditor, error) { + settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/ee/auditor/otlphttpauditor") + + httpClient, err := client.New( + settings.Logger(), + providerSettings.TracerProvider, + providerSettings.MeterProvider, + client.WithTimeout(config.OTLPHTTP.Timeout), + client.WithRetryCount(retryCountFromConfig(config.OTLPHTTP.Retry)), + retrierOption(config.OTLPHTTP.Retry), + ) + if err != nil { + return nil, err + } + + provider := &provider{ + settings: settings, + config: config, + licensing: licensing, + build: build, + marshaler: plog.ProtoMarshaler{}, + httpClient: httpClient, + } + + server, err := auditorserver.New(settings, + auditorserver.Config{ + BufferSize: config.BufferSize, + BatchSize: config.BatchSize, + FlushInterval: config.FlushInterval, + }, + provider.export, + ) + if err != nil { + return nil, err + } + + provider.server = server + return provider, nil +} + +func (provider *provider) Start(ctx context.Context) error { + return provider.server.Start(ctx) +} + +func (provider *provider) Audit(ctx context.Context, event audittypes.AuditEvent) { + if event.PrincipalAttributes.PrincipalOrgID.IsZero() { + provider.settings.Logger().WarnContext(ctx, "audit event dropped as org_id is zero") + return + } + + if _, err := provider.licensing.GetActive(ctx, event.PrincipalAttributes.PrincipalOrgID); err != nil { + return + } + + provider.server.Add(ctx, event) +} + +func (provider *provider) Healthy() <-chan struct{} { + return provider.server.Healthy() +} + +func (provider *provider) Stop(ctx context.Context) error { + return provider.server.Stop(ctx) +} diff --git a/ee/auditor/otlphttpauditor/retrier.go b/ee/auditor/otlphttpauditor/retrier.go new file mode 100644 index 00000000000..150d8aca83b --- /dev/null +++ b/ee/auditor/otlphttpauditor/retrier.go @@ -0,0 +1,52 @@ +package otlphttpauditor + +import ( + "time" + + "github.com/SigNoz/signoz/pkg/auditor" + client "github.com/SigNoz/signoz/pkg/http/client" +) + +// retrier implements client.Retriable with exponential backoff +// derived from auditor.RetryConfig. +type retrier struct { + initialInterval time.Duration + maxInterval time.Duration +} + +func newRetrier(cfg auditor.RetryConfig) *retrier { + return &retrier{ + initialInterval: cfg.InitialInterval, + maxInterval: cfg.MaxInterval, + } +} + +// NextInterval returns the backoff duration for the given retry attempt. +// Uses exponential backoff: initialInterval * 2^retry, capped at maxInterval. +func (r *retrier) NextInterval(retry int) time.Duration { + interval := r.initialInterval + for range retry { + interval *= 2 + } + return min(interval, r.maxInterval) +} + +func retrierOption(cfg auditor.RetryConfig) client.Option { + return client.WithRetriable(newRetrier(cfg)) +} + +func retryCountFromConfig(cfg auditor.RetryConfig) int { + if !cfg.Enabled || cfg.MaxElapsedTime <= 0 { + return 0 + } + + count := 0 + elapsed := time.Duration(0) + interval := cfg.InitialInterval + for elapsed < cfg.MaxElapsedTime { + elapsed += interval + interval = min(interval*2, cfg.MaxInterval) + count++ + } + return count +} diff --git a/ee/authn/callbackauthn/oidccallbackauthn/authn.go b/ee/authn/callbackauthn/oidccallbackauthn/authn.go index 5efabbda741..b46690001a9 100644 --- a/ee/authn/callbackauthn/oidccallbackauthn/authn.go +++ b/ee/authn/callbackauthn/oidccallbackauthn/authn.go @@ -3,6 +3,7 @@ package oidccallbackauthn import ( "context" "fmt" + "log/slog" "net/url" "github.com/SigNoz/signoz/pkg/authn" @@ -150,7 +151,7 @@ func (a *AuthN) HandleCallback(ctx context.Context, query url.Values) (*authtype // Some IDPs return a single group as a string instead of an array groups = append(groups, g) default: - a.settings.Logger().WarnContext(ctx, "oidc: unsupported groups type", "type", fmt.Sprintf("%T", claimValue)) + a.settings.Logger().WarnContext(ctx, "oidc: unsupported groups type", slog.String("type", fmt.Sprintf("%T", claimValue))) } } } diff --git a/ee/authz/openfgaauthz/provider.go b/ee/authz/openfgaauthz/provider.go index deaa8e8973e..603e3251cf4 100644 --- a/ee/authz/openfgaauthz/provider.go +++ b/ee/authz/openfgaauthz/provider.go @@ -16,6 +16,7 @@ import ( "github.com/SigNoz/signoz/pkg/valuer" openfgav1 "github.com/openfga/api/proto/openfga/v1" openfgapkgtransformer "github.com/openfga/language/pkg/go/transformer" + "github.com/openfga/openfga/pkg/storage" ) type provider struct { @@ -26,14 +27,14 @@ type provider struct { registry []authz.RegisterTypeable } -func NewProviderFactory(sqlstore sqlstore.SQLStore, openfgaSchema []openfgapkgtransformer.ModuleFile, licensing licensing.Licensing, registry ...authz.RegisterTypeable) factory.ProviderFactory[authz.AuthZ, authz.Config] { +func NewProviderFactory(sqlstore sqlstore.SQLStore, openfgaSchema []openfgapkgtransformer.ModuleFile, openfgaDataStore storage.OpenFGADatastore, licensing licensing.Licensing, registry ...authz.RegisterTypeable) factory.ProviderFactory[authz.AuthZ, authz.Config] { return factory.NewProviderFactory(factory.MustNewName("openfga"), func(ctx context.Context, ps factory.ProviderSettings, config authz.Config) (authz.AuthZ, error) { - return newOpenfgaProvider(ctx, ps, config, sqlstore, openfgaSchema, licensing, registry) + return newOpenfgaProvider(ctx, ps, config, sqlstore, openfgaSchema, openfgaDataStore, licensing, registry) }) } -func newOpenfgaProvider(ctx context.Context, settings factory.ProviderSettings, config authz.Config, sqlstore sqlstore.SQLStore, openfgaSchema []openfgapkgtransformer.ModuleFile, licensing licensing.Licensing, registry []authz.RegisterTypeable) (authz.AuthZ, error) { - pkgOpenfgaAuthzProvider := pkgopenfgaauthz.NewProviderFactory(sqlstore, openfgaSchema) +func newOpenfgaProvider(ctx context.Context, settings factory.ProviderSettings, config authz.Config, sqlstore sqlstore.SQLStore, openfgaSchema []openfgapkgtransformer.ModuleFile, openfgaDataStore storage.OpenFGADatastore, licensing licensing.Licensing, registry []authz.RegisterTypeable) (authz.AuthZ, error) { + pkgOpenfgaAuthzProvider := pkgopenfgaauthz.NewProviderFactory(sqlstore, openfgaSchema, openfgaDataStore) pkgAuthzService, err := pkgOpenfgaAuthzProvider.New(ctx, settings, config) if err != nil { return nil, err @@ -57,6 +58,10 @@ func (provider *provider) Start(ctx context.Context) error { return provider.openfgaServer.Start(ctx) } +func (provider *provider) Healthy() <-chan struct{} { + return provider.openfgaServer.Healthy() +} + func (provider *provider) Stop(ctx context.Context) error { return provider.openfgaServer.Stop(ctx) } diff --git a/ee/authz/openfgaserver/server.go b/ee/authz/openfgaserver/server.go index b3d8da22d46..ff3ed39c4dd 100644 --- a/ee/authz/openfgaserver/server.go +++ b/ee/authz/openfgaserver/server.go @@ -16,7 +16,6 @@ type Server struct { } func NewOpenfgaServer(ctx context.Context, pkgAuthzService authz.AuthZ) (*Server, error) { - return &Server{ pkgAuthzService: pkgAuthzService, }, nil @@ -26,14 +25,31 @@ func (server *Server) Start(ctx context.Context) error { return server.pkgAuthzService.Start(ctx) } +func (server *Server) Healthy() <-chan struct{} { + return server.pkgAuthzService.Healthy() +} + func (server *Server) Stop(ctx context.Context) error { return server.pkgAuthzService.Stop(ctx) } func (server *Server) CheckWithTupleCreation(ctx context.Context, claims authtypes.Claims, orgID valuer.UUID, relation authtypes.Relation, typeable authtypes.Typeable, selectors []authtypes.Selector, _ []authtypes.Selector) error { - subject, err := authtypes.NewSubject(authtypes.TypeableUser, claims.UserID, orgID, nil) - if err != nil { - return err + subject := "" + switch claims.Principal { + case authtypes.PrincipalUser: + user, err := authtypes.NewSubject(authtypes.TypeableUser, claims.UserID, orgID, nil) + if err != nil { + return err + } + + subject = user + case authtypes.PrincipalServiceAccount: + serviceAccount, err := authtypes.NewSubject(authtypes.TypeableServiceAccount, claims.ServiceAccountID, orgID, nil) + if err != nil { + return err + } + + subject = serviceAccount } tupleSlice, err := typeable.Tuples(subject, relation, selectors, orgID) diff --git a/ee/authz/openfgaserver/sqlstore.go b/ee/authz/openfgaserver/sqlstore.go new file mode 100644 index 00000000000..44032c6f97c --- /dev/null +++ b/ee/authz/openfgaserver/sqlstore.go @@ -0,0 +1,32 @@ +package openfgaserver + +import ( + "github.com/SigNoz/signoz/ee/sqlstore/postgressqlstore" + "github.com/SigNoz/signoz/pkg/errors" + "github.com/SigNoz/signoz/pkg/sqlstore" + "github.com/openfga/openfga/pkg/storage" + "github.com/openfga/openfga/pkg/storage/postgres" + "github.com/openfga/openfga/pkg/storage/sqlcommon" + "github.com/openfga/openfga/pkg/storage/sqlite" +) + +func NewSQLStore(store sqlstore.SQLStore) (storage.OpenFGADatastore, error) { + switch store.BunDB().Dialect().Name().String() { + case "sqlite": + return sqlite.NewWithDB(store.SQLDB(), &sqlcommon.Config{ + MaxTuplesPerWriteField: 100, + MaxTypesPerModelField: 100, + }) + case "pg": + pgStore, ok := store.(postgressqlstore.Pooler) + if !ok { + panic(errors.New(errors.TypeInternal, errors.CodeInternal, "postgressqlstore should implement Pooler")) + } + + return postgres.NewWithDB(pgStore.Pool(), nil, &sqlcommon.Config{ + MaxTuplesPerWriteField: 100, + MaxTypesPerModelField: 100, + }) + } + return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "invalid store type: %s", store.BunDB().Dialect().Name().String()) +} diff --git a/ee/licensing/config.go b/ee/licensing/config.go index 8fc87c0ba13..8359ef2b12a 100644 --- a/ee/licensing/config.go +++ b/ee/licensing/config.go @@ -13,7 +13,7 @@ var ( once sync.Once ) -// initializes the licensing configuration +// Config initializes the licensing configuration. func Config(pollInterval time.Duration, failureThreshold int) licensing.Config { once.Do(func() { config = licensing.Config{PollInterval: pollInterval, FailureThreshold: failureThreshold} diff --git a/ee/licensing/httplicensing/provider.go b/ee/licensing/httplicensing/provider.go index 1258a29bf0c..edbe3e12460 100644 --- a/ee/licensing/httplicensing/provider.go +++ b/ee/licensing/httplicensing/provider.go @@ -3,8 +3,11 @@ package httplicensing import ( "context" "encoding/json" + "log/slog" "time" + "github.com/tidwall/gjson" + "github.com/SigNoz/signoz/ee/licensing/licensingstore/sqllicensingstore" "github.com/SigNoz/signoz/pkg/analytics" "github.com/SigNoz/signoz/pkg/errors" @@ -16,7 +19,6 @@ import ( "github.com/SigNoz/signoz/pkg/types/licensetypes" "github.com/SigNoz/signoz/pkg/valuer" "github.com/SigNoz/signoz/pkg/zeus" - "github.com/tidwall/gjson" ) type provider struct { @@ -55,7 +57,7 @@ func (provider *provider) Start(ctx context.Context) error { err := provider.Validate(ctx) if err != nil { - provider.settings.Logger().ErrorContext(ctx, "failed to validate license from upstream server", "error", err) + provider.settings.Logger().ErrorContext(ctx, "failed to validate license from upstream server", errors.Attr(err)) } for { @@ -65,7 +67,7 @@ func (provider *provider) Start(ctx context.Context) error { case <-tick.C: err := provider.Validate(ctx) if err != nil { - provider.settings.Logger().ErrorContext(ctx, "failed to validate license from upstream server", "error", err) + provider.settings.Logger().ErrorContext(ctx, "failed to validate license from upstream server", errors.Attr(err)) } } } @@ -133,7 +135,7 @@ func (provider *provider) Refresh(ctx context.Context, organizationID valuer.UUI if errors.Ast(err, errors.TypeNotFound) { return nil } - provider.settings.Logger().ErrorContext(ctx, "license validation failed", "org_id", organizationID.StringValue()) + provider.settings.Logger().ErrorContext(ctx, "license validation failed", slog.String("org_id", organizationID.StringValue())) return err } diff --git a/ee/modules/dashboard/impldashboard/module.go b/ee/modules/dashboard/impldashboard/module.go index 74aba8eec98..dfd96616ebd 100644 --- a/ee/modules/dashboard/impldashboard/module.go +++ b/ee/modules/dashboard/impldashboard/module.go @@ -213,8 +213,8 @@ func (module *module) Update(ctx context.Context, orgID valuer.UUID, id valuer.U return module.pkgDashboardModule.Update(ctx, orgID, id, updatedBy, data, diff) } -func (module *module) LockUnlock(ctx context.Context, orgID valuer.UUID, id valuer.UUID, updatedBy string, role types.Role, lock bool) error { - return module.pkgDashboardModule.LockUnlock(ctx, orgID, id, updatedBy, role, lock) +func (module *module) LockUnlock(ctx context.Context, orgID valuer.UUID, id valuer.UUID, updatedBy string, isAdmin bool, lock bool) error { + return module.pkgDashboardModule.LockUnlock(ctx, orgID, id, updatedBy, isAdmin, lock) } func (module *module) MustGetTypeables() []authtypes.Typeable { diff --git a/ee/querier/handler.go b/ee/querier/handler.go index c6f5dc4abfb..28938c4a2e8 100644 --- a/ee/querier/handler.go +++ b/ee/querier/handler.go @@ -6,7 +6,6 @@ import ( "encoding/json" "io" "net/http" - "runtime/debug" anomalyV2 "github.com/SigNoz/signoz/ee/anomaly" "github.com/SigNoz/signoz/pkg/errors" @@ -54,26 +53,6 @@ func (h *handler) QueryRange(rw http.ResponseWriter, req *http.Request) { return } - defer func() { - if r := recover(); r != nil { - stackTrace := string(debug.Stack()) - - queryJSON, _ := json.Marshal(queryRangeRequest) - - h.set.Logger.ErrorContext(ctx, "panic in QueryRange", - "error", r, - "user", claims.UserID, - "payload", string(queryJSON), - "stacktrace", stackTrace, - ) - - render.Error(rw, errors.NewInternalf( - errors.CodeInternal, - "Something went wrong on our end. It's not you, it's us. Our team is notified about it. Reach out to support if issue persists.", - )) - } - }() - if err := queryRangeRequest.Validate(); err != nil { render.Error(rw, err) return @@ -86,7 +65,7 @@ func (h *handler) QueryRange(rw http.ResponseWriter, req *http.Request) { } if anomalyQuery, ok := queryRangeRequest.IsAnomalyRequest(); ok { - anomalies, err := h.handleAnomalyQuery(ctx, orgID, anomalyQuery, queryRangeRequest) + anomalies, err := h.handleAnomalyQuery(ctx, orgID, anomalyQuery, &queryRangeRequest) if err != nil { render.Error(rw, errors.NewInternalf(errors.CodeInternal, "failed to get anomalies: %v", err)) return @@ -100,7 +79,7 @@ func (h *handler) QueryRange(rw http.ResponseWriter, req *http.Request) { // Build step intervals from the anomaly query stepIntervals := make(map[string]uint64) if anomalyQuery.StepInterval.Duration > 0 { - stepIntervals[anomalyQuery.Name] = uint64(anomalyQuery.StepInterval.Duration.Seconds()) + stepIntervals[anomalyQuery.Name] = uint64(anomalyQuery.StepInterval.Seconds()) } finalResp := &qbtypes.QueryRangeResponse{ @@ -170,7 +149,7 @@ func (h *handler) createAnomalyProvider(seasonality anomalyV2.Seasonality) anoma } } -func (h *handler) handleAnomalyQuery(ctx context.Context, orgID valuer.UUID, anomalyQuery *qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation], queryRangeRequest qbtypes.QueryRangeRequest) (*anomalyV2.AnomaliesResponse, error) { +func (h *handler) handleAnomalyQuery(ctx context.Context, orgID valuer.UUID, anomalyQuery *qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation], queryRangeRequest *qbtypes.QueryRangeRequest) (*anomalyV2.AnomaliesResponse, error) { seasonality := extractSeasonality(anomalyQuery) provider := h.createAnomalyProvider(seasonality) diff --git a/ee/query-service/app/api/cloudIntegrations.go b/ee/query-service/app/api/cloudIntegrations.go index d773841d765..c0df93298ac 100644 --- a/ee/query-service/app/api/cloudIntegrations.go +++ b/ee/query-service/app/api/cloudIntegrations.go @@ -10,15 +10,15 @@ import ( "strings" "time" + "log/slog" + "github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/http/render" - "github.com/SigNoz/signoz/pkg/modules/user" basemodel "github.com/SigNoz/signoz/pkg/query-service/model" - "github.com/SigNoz/signoz/pkg/types" "github.com/SigNoz/signoz/pkg/types/authtypes" + "github.com/SigNoz/signoz/pkg/types/serviceaccounttypes" "github.com/SigNoz/signoz/pkg/valuer" "github.com/gorilla/mux" - "log/slog" ) type CloudIntegrationConnectionParamsResponse struct { @@ -49,7 +49,7 @@ func (ah *APIHandler) CloudIntegrationsGenerateConnectionParams(w http.ResponseW return } - apiKey, apiErr := ah.getOrCreateCloudIntegrationPAT(r.Context(), claims.OrgID, cloudProvider) + apiKey, apiErr := ah.getOrCreateCloudIntegrationFactorAPIKey(r.Context(), valuer.MustNewUUID(claims.OrgID), cloudProvider) if apiErr != nil { RespondError(w, basemodel.WrapApiError( apiErr, "couldn't provision PAT for cloud integration:", @@ -109,79 +109,44 @@ func (ah *APIHandler) CloudIntegrationsGenerateConnectionParams(w http.ResponseW ah.Respond(w, result) } -func (ah *APIHandler) getOrCreateCloudIntegrationPAT(ctx context.Context, orgId string, cloudProvider string) ( +func (ah *APIHandler) getOrCreateCloudIntegrationFactorAPIKey(ctx context.Context, orgID valuer.UUID, cloudProvider string) ( string, *basemodel.ApiError, ) { - integrationPATName := fmt.Sprintf("%s integration", cloudProvider) - - integrationUser, apiErr := ah.getOrCreateCloudIntegrationUser(ctx, orgId, cloudProvider) + integrationPATName := fmt.Sprintf("%s", cloudProvider) + serviceAccount, apiErr := ah.getOrCreateCloudIntegrationServiceAccount(ctx, orgID) if apiErr != nil { return "", apiErr } - orgIdUUID, err := valuer.NewUUID(orgId) - if err != nil { - return "", basemodel.InternalError(fmt.Errorf( - "couldn't parse orgId: %w", err, - )) - } - - allPats, err := ah.Signoz.Modules.User.ListAPIKeys(ctx, orgIdUUID) - if err != nil { - return "", basemodel.InternalError(fmt.Errorf( - "couldn't list PATs: %w", err, - )) - } - for _, p := range allPats { - if p.UserID == integrationUser.ID && p.Name == integrationPATName { - return p.Token, nil - } - } - - slog.InfoContext(ctx, "no PAT found for cloud integration, creating a new one", - "cloud_provider", cloudProvider, - ) - - newPAT, err := types.NewStorableAPIKey( - integrationPATName, - integrationUser.ID, - types.RoleViewer, - 0, - ) + factorAPIKey, err := serviceAccount.NewFactorAPIKey(integrationPATName, 0) if err != nil { return "", basemodel.InternalError(fmt.Errorf( "couldn't create cloud integration PAT: %w", err, )) } - err = ah.Signoz.Modules.User.CreateAPIKey(ctx, newPAT) + factorAPIKey, err = ah.Signoz.Modules.ServiceAccount.GetOrCreateFactorAPIKey(ctx, factorAPIKey) if err != nil { return "", basemodel.InternalError(fmt.Errorf( "couldn't create cloud integration PAT: %w", err, )) } - return newPAT.Token, nil + return factorAPIKey.Key, nil } -func (ah *APIHandler) getOrCreateCloudIntegrationUser( - ctx context.Context, orgId string, cloudProvider string, -) (*types.User, *basemodel.ApiError) { - cloudIntegrationUserName := fmt.Sprintf("%s-integration", cloudProvider) - email := valuer.MustNewEmail(fmt.Sprintf("%s@signoz.io", cloudIntegrationUserName)) - - cloudIntegrationUser, err := types.NewUser(cloudIntegrationUserName, email, types.RoleViewer, valuer.MustNewUUID(orgId), types.UserStatusActive) +func (ah *APIHandler) getOrCreateCloudIntegrationServiceAccount(ctx context.Context, orgId valuer.UUID) (*serviceaccounttypes.ServiceAccount, *basemodel.ApiError) { + domain := ah.Signoz.Modules.ServiceAccount.Config().Email.Domain + cloudIntegrationServiceAccount := serviceaccounttypes.NewServiceAccount("integration", domain, serviceaccounttypes.ServiceAccountStatusActive, orgId) + cloudIntegrationServiceAccount, err := ah.Signoz.Modules.ServiceAccount.GetOrCreate(ctx, orgId, cloudIntegrationServiceAccount) if err != nil { - return nil, basemodel.InternalError(fmt.Errorf("couldn't create cloud integration user: %w", err)) + return nil, basemodel.InternalError(fmt.Errorf("couldn't create cloud integration service account: %w", err)) } - - password := types.MustGenerateFactorPassword(cloudIntegrationUser.ID.StringValue()) - - cloudIntegrationUser, err = ah.Signoz.Modules.User.GetOrCreateUser(ctx, cloudIntegrationUser, user.WithFactorPassword(password)) + err = ah.Signoz.Modules.ServiceAccount.SetRoleByName(ctx, orgId, cloudIntegrationServiceAccount.ID, authtypes.SigNozViewerRoleName) if err != nil { - return nil, basemodel.InternalError(fmt.Errorf("couldn't look for integration user: %w", err)) + return nil, basemodel.InternalError(fmt.Errorf("couldn't create cloud integration service account: %w", err)) } - return cloudIntegrationUser, nil + return cloudIntegrationServiceAccount, nil } func (ah *APIHandler) getIngestionUrlAndSigNozAPIUrl(ctx context.Context, licenseKey string) ( diff --git a/ee/query-service/app/api/featureFlags.go b/ee/query-service/app/api/featureFlags.go index dc2118f4219..4c5ebe3bb11 100644 --- a/ee/query-service/app/api/featureFlags.go +++ b/ee/query-service/app/api/featureFlags.go @@ -4,10 +4,15 @@ import ( "encoding/json" "errors" "fmt" + "io" "net/http" "time" + signozerrors "github.com/SigNoz/signoz/pkg/errors" + + "log/slog" + "github.com/SigNoz/signoz/ee/query-service/constants" "github.com/SigNoz/signoz/pkg/flagger" "github.com/SigNoz/signoz/pkg/http/render" @@ -15,7 +20,6 @@ import ( "github.com/SigNoz/signoz/pkg/types/featuretypes" "github.com/SigNoz/signoz/pkg/types/licensetypes" "github.com/SigNoz/signoz/pkg/valuer" - "log/slog" ) func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) { @@ -38,7 +42,7 @@ func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) { slog.DebugContext(ctx, "fetching license") license, err := ah.Signoz.Licensing.GetActive(ctx, orgID) if err != nil { - slog.ErrorContext(ctx, "failed to fetch license", "error", err) + slog.ErrorContext(ctx, "failed to fetch license", signozerrors.Attr(err)) } else if license == nil { slog.DebugContext(ctx, "no active license found") } else { @@ -51,7 +55,7 @@ func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) { // merge featureSet and zeusFeatures in featureSet with higher priority to zeusFeatures featureSet = MergeFeatureSets(zeusFeatures, featureSet) } else { - slog.ErrorContext(ctx, "failed to fetch zeus features", "error", err) + slog.ErrorContext(ctx, "failed to fetch zeus features", signozerrors.Attr(err)) } } } diff --git a/ee/query-service/app/api/queryrange.go b/ee/query-service/app/api/queryrange.go index 07b951dbb03..5fbee0f5bd8 100644 --- a/ee/query-service/app/api/queryrange.go +++ b/ee/query-service/app/api/queryrange.go @@ -7,6 +7,7 @@ import ( "net/http" "github.com/SigNoz/signoz/ee/query-service/anomaly" + "github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/http/render" baseapp "github.com/SigNoz/signoz/pkg/query-service/app" "github.com/SigNoz/signoz/pkg/query-service/app/queryBuilder" @@ -35,7 +36,7 @@ func (aH *APIHandler) queryRangeV4(w http.ResponseWriter, r *http.Request) { queryRangeParams, apiErrorObj := baseapp.ParseQueryRangeParams(r) if apiErrorObj != nil { - slog.ErrorContext(r.Context(), "error parsing metric query range params", "error", apiErrorObj.Err) + slog.ErrorContext(r.Context(), "error parsing metric query range params", errors.Attr(apiErrorObj.Err)) RespondError(w, apiErrorObj, nil) return } @@ -44,7 +45,7 @@ func (aH *APIHandler) queryRangeV4(w http.ResponseWriter, r *http.Request) { // add temporality for each metric temporalityErr := aH.PopulateTemporality(r.Context(), orgID, queryRangeParams) if temporalityErr != nil { - slog.ErrorContext(r.Context(), "error while adding temporality for metrics", "error", temporalityErr) + slog.ErrorContext(r.Context(), "error while adding temporality for metrics", errors.Attr(temporalityErr)) RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: temporalityErr}, nil) return } diff --git a/ee/query-service/app/server.go b/ee/query-service/app/server.go index f63784cfe78..538215676ff 100644 --- a/ee/query-service/app/server.go +++ b/ee/query-service/app/server.go @@ -5,19 +5,23 @@ import ( "fmt" "net" "net/http" - _ "net/http/pprof" // http profiler "slices" + "go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux" + "go.opentelemetry.io/otel/propagation" + "github.com/SigNoz/signoz/pkg/cache/memorycache" + "github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/factory" "github.com/SigNoz/signoz/pkg/queryparser" "github.com/SigNoz/signoz/pkg/ruler/rulestore/sqlrulestore" "github.com/SigNoz/signoz/pkg/types/telemetrytypes" - "go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux" - "go.opentelemetry.io/otel/propagation" "github.com/gorilla/handlers" + "github.com/rs/cors" + "github.com/soheilhy/cmux" + "github.com/SigNoz/signoz/ee/query-service/app/api" "github.com/SigNoz/signoz/ee/query-service/rules" "github.com/SigNoz/signoz/ee/query-service/usage" @@ -25,14 +29,15 @@ import ( "github.com/SigNoz/signoz/pkg/cache" "github.com/SigNoz/signoz/pkg/http/middleware" "github.com/SigNoz/signoz/pkg/modules/organization" + "github.com/SigNoz/signoz/pkg/modules/rulestatehistory" "github.com/SigNoz/signoz/pkg/prometheus" "github.com/SigNoz/signoz/pkg/querier" "github.com/SigNoz/signoz/pkg/signoz" "github.com/SigNoz/signoz/pkg/sqlstore" "github.com/SigNoz/signoz/pkg/telemetrystore" "github.com/SigNoz/signoz/pkg/web" - "github.com/rs/cors" - "github.com/soheilhy/cmux" + + "log/slog" "github.com/SigNoz/signoz/pkg/query-service/agentConf" baseapp "github.com/SigNoz/signoz/pkg/query-service/app" @@ -44,10 +49,8 @@ import ( opAmpModel "github.com/SigNoz/signoz/pkg/query-service/app/opamp/model" baseconst "github.com/SigNoz/signoz/pkg/query-service/constants" "github.com/SigNoz/signoz/pkg/query-service/healthcheck" - baseint "github.com/SigNoz/signoz/pkg/query-service/interfaces" baserules "github.com/SigNoz/signoz/pkg/query-service/rules" "github.com/SigNoz/signoz/pkg/query-service/utils" - "log/slog" ) // Server runs HTTP, Mux and a grpc server @@ -95,7 +98,6 @@ func NewServer(config signoz.Config, signoz *signoz.SigNoz) (*Server, error) { ) rm, err := makeRulesManager( - reader, signoz.Cache, signoz.Alertmanager, signoz.SQLStore, @@ -103,6 +105,7 @@ func NewServer(config signoz.Config, signoz *signoz.SigNoz) (*Server, error) { signoz.TelemetryMetadataStore, signoz.Prometheus, signoz.Modules.OrgGetter, + signoz.Modules.RuleStateHistory, signoz.Querier, signoz.Instrumentation.ToProviderSettings(), signoz.QueryParser, @@ -133,6 +136,7 @@ func NewServer(config signoz.Config, signoz *signoz.SigNoz) (*Server, error) { logParsingPipelineController, err := logparsingpipeline.NewLogParsingPipelinesController( signoz.SQLStore, integrationsController.GetPipelinesForInstalledIntegrations, + reader, ) if err != nil { return nil, err @@ -207,6 +211,7 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*h r := baseapp.NewRouter() am := middleware.NewAuthZ(s.signoz.Instrumentation.Logger(), s.signoz.Modules.OrgGetter, s.signoz.Authz) + r.Use(middleware.NewRecovery(s.signoz.Instrumentation.Logger()).Wrap) r.Use(otelmux.Middleware( "apiserver", otelmux.WithMeterProvider(s.signoz.Instrumentation.MeterProvider()), @@ -215,7 +220,6 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*h otelmux.WithFilter(func(r *http.Request) bool { return !slices.Contains([]string{"/api/v1/health"}, r.URL.Path) }), - otelmux.WithPublicEndpoint(), )) r.Use(middleware.NewIdentN(s.signoz.IdentNResolver, s.signoz.Sharder, s.signoz.Instrumentation.Logger()).Wrap) r.Use(middleware.NewTimeout(s.signoz.Instrumentation.Logger(), @@ -223,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.NewLogging(s.signoz.Instrumentation.Logger(), s.config.APIServer.Logging.ExcludedRoutes).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) @@ -236,7 +240,6 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*h apiHandler.RegisterWebSocketPaths(r, am) apiHandler.RegisterMessagingQueuesRoutes(r, am) apiHandler.RegisterThirdPartyApiRoutes(r, am) - apiHandler.MetricExplorerRoutes(r, am) apiHandler.RegisterTraceFunnelsRoutes(r, am) err := s.signoz.APIServer.AddToRouter(r) @@ -304,25 +307,16 @@ func (s *Server) Start(ctx context.Context) error { case nil, http.ErrServerClosed, cmux.ErrListenerClosed: // normal exit, nothing to do default: - slog.Error("Could not start HTTP server", "error", err) + slog.Error("Could not start HTTP server", errors.Attr(err)) } s.unavailableChannel <- healthcheck.Unavailable }() - go func() { - slog.Info("Starting pprof server", "addr", baseconst.DebugHttpPort) - - err = http.ListenAndServe(baseconst.DebugHttpPort, nil) - if err != nil { - slog.Error("Could not start pprof server", "error", err) - } - }() - go func() { slog.Info("Starting OpAmp Websocket server", "addr", baseconst.OpAmpWsEndpoint) err := s.opampServer.Start(baseconst.OpAmpWsEndpoint) if err != nil { - slog.Error("opamp ws server failed to start", "error", err) + slog.Error("opamp ws server failed to start", errors.Attr(err)) s.unavailableChannel <- healthcheck.Unavailable } }() @@ -349,28 +343,28 @@ func (s *Server) Stop(ctx context.Context) error { return nil } -func makeRulesManager(ch baseint.Reader, cache cache.Cache, alertmanager alertmanager.Alertmanager, sqlstore sqlstore.SQLStore, telemetryStore telemetrystore.TelemetryStore, metadataStore telemetrytypes.MetadataStore, prometheus prometheus.Prometheus, orgGetter organization.Getter, querier querier.Querier, providerSettings factory.ProviderSettings, queryParser queryparser.QueryParser) (*baserules.Manager, error) { +func makeRulesManager(cache cache.Cache, alertmanager alertmanager.Alertmanager, sqlstore sqlstore.SQLStore, telemetryStore telemetrystore.TelemetryStore, metadataStore telemetrytypes.MetadataStore, prometheus prometheus.Prometheus, orgGetter organization.Getter, ruleStateHistoryModule rulestatehistory.Module, querier querier.Querier, providerSettings factory.ProviderSettings, queryParser queryparser.QueryParser) (*baserules.Manager, error) { ruleStore := sqlrulestore.NewRuleStore(sqlstore, queryParser, providerSettings) maintenanceStore := sqlrulestore.NewMaintenanceStore(sqlstore) // create manager opts managerOpts := &baserules.ManagerOptions{ - TelemetryStore: telemetryStore, - MetadataStore: metadataStore, - Prometheus: prometheus, - Context: context.Background(), - Reader: ch, - Querier: querier, - Logger: providerSettings.Logger, - Cache: cache, - EvalDelay: baseconst.GetEvalDelay(), - PrepareTaskFunc: rules.PrepareTaskFunc, - PrepareTestRuleFunc: rules.TestNotification, - Alertmanager: alertmanager, - OrgGetter: orgGetter, - RuleStore: ruleStore, - MaintenanceStore: maintenanceStore, - SqlStore: sqlstore, - QueryParser: queryParser, + TelemetryStore: telemetryStore, + MetadataStore: metadataStore, + Prometheus: prometheus, + Context: context.Background(), + Querier: querier, + Logger: providerSettings.Logger, + Cache: cache, + EvalDelay: baseconst.GetEvalDelay(), + PrepareTaskFunc: rules.PrepareTaskFunc, + PrepareTestRuleFunc: rules.TestNotification, + Alertmanager: alertmanager, + OrgGetter: orgGetter, + RuleStore: ruleStore, + MaintenanceStore: maintenanceStore, + SQLStore: sqlstore, + QueryParser: queryParser, + RuleStateHistoryModule: ruleStateHistoryModule, } // create Manager diff --git a/ee/query-service/rules/anomaly.go b/ee/query-service/rules/anomaly.go index 8fd4432c38d..46fc89c1e50 100644 --- a/ee/query-service/rules/anomaly.go +++ b/ee/query-service/rules/anomaly.go @@ -5,57 +5,34 @@ import ( "encoding/json" "fmt" "log/slog" - "math" "strings" "sync" "time" - "github.com/SigNoz/signoz/ee/query-service/anomaly" - "github.com/SigNoz/signoz/pkg/cache" - "github.com/SigNoz/signoz/pkg/query-service/common" - "github.com/SigNoz/signoz/pkg/query-service/model" - "github.com/SigNoz/signoz/pkg/transition" + "github.com/SigNoz/signoz/pkg/errors" + "github.com/SigNoz/signoz/pkg/querier" + "github.com/SigNoz/signoz/pkg/types/rulestatehistorytypes" "github.com/SigNoz/signoz/pkg/types/ruletypes" "github.com/SigNoz/signoz/pkg/valuer" - querierV2 "github.com/SigNoz/signoz/pkg/query-service/app/querier/v2" - "github.com/SigNoz/signoz/pkg/query-service/app/queryBuilder" - "github.com/SigNoz/signoz/pkg/query-service/interfaces" - v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3" - "github.com/SigNoz/signoz/pkg/query-service/utils/labels" - "github.com/SigNoz/signoz/pkg/query-service/utils/times" - "github.com/SigNoz/signoz/pkg/query-service/utils/timestamp" - "github.com/SigNoz/signoz/pkg/units" baserules "github.com/SigNoz/signoz/pkg/query-service/rules" - querierV5 "github.com/SigNoz/signoz/pkg/querier" - - anomalyV2 "github.com/SigNoz/signoz/ee/anomaly" + "github.com/SigNoz/signoz/ee/anomaly" qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5" ) -const ( - RuleTypeAnomaly = "anomaly_rule" -) - type AnomalyRule struct { *baserules.BaseRule mtx sync.Mutex - reader interfaces.Reader - - // querierV2 is used for alerts created after the introduction of new metrics query builder - querierV2 interfaces.Querier + // querier is used for alerts migrated after the introduction of new query builder + querier querier.Querier - // querierV5 is used for alerts migrated after the introduction of new query builder - querierV5 querierV5.Querier - - provider anomaly.Provider - providerV2 anomalyV2.Provider + provider anomaly.Provider version string logger *slog.Logger @@ -69,18 +46,16 @@ func NewAnomalyRule( id string, orgID valuer.UUID, p *ruletypes.PostableRule, - reader interfaces.Reader, - querierV5 querierV5.Querier, + querier querier.Querier, logger *slog.Logger, - cache cache.Cache, opts ...baserules.RuleOption, ) (*AnomalyRule, error) { - logger.Info("creating new AnomalyRule", "rule_id", id) + logger.Info("creating new AnomalyRule", slog.String("rule.id", id)) opts = append(opts, baserules.WithLogger(logger)) - baseRule, err := baserules.NewBaseRule(id, orgID, p, reader, opts...) + baseRule, err := baserules.NewBaseRule(id, orgID, p, opts...) if err != nil { return nil, err } @@ -100,93 +75,38 @@ func NewAnomalyRule( t.seasonality = anomaly.SeasonalityDaily } - logger.Info("using seasonality", "seasonality", t.seasonality.String()) - - querierOptsV2 := querierV2.QuerierOptions{ - Reader: reader, - Cache: cache, - KeyGenerator: queryBuilder.NewKeyGenerator(), - } + logger.Info("using seasonality", slog.String("rule.id", id), slog.String("rule.seasonality", t.seasonality.StringValue())) - t.querierV2 = querierV2.NewQuerier(querierOptsV2) - t.reader = reader if t.seasonality == anomaly.SeasonalityHourly { t.provider = anomaly.NewHourlyProvider( - anomaly.WithCache[*anomaly.HourlyProvider](cache), - anomaly.WithKeyGenerator[*anomaly.HourlyProvider](queryBuilder.NewKeyGenerator()), - anomaly.WithReader[*anomaly.HourlyProvider](reader), + anomaly.WithQuerier[*anomaly.HourlyProvider](querier), + anomaly.WithLogger[*anomaly.HourlyProvider](logger), ) } else if t.seasonality == anomaly.SeasonalityDaily { t.provider = anomaly.NewDailyProvider( - anomaly.WithCache[*anomaly.DailyProvider](cache), - anomaly.WithKeyGenerator[*anomaly.DailyProvider](queryBuilder.NewKeyGenerator()), - anomaly.WithReader[*anomaly.DailyProvider](reader), + anomaly.WithQuerier[*anomaly.DailyProvider](querier), + anomaly.WithLogger[*anomaly.DailyProvider](logger), ) } else if t.seasonality == anomaly.SeasonalityWeekly { t.provider = anomaly.NewWeeklyProvider( - anomaly.WithCache[*anomaly.WeeklyProvider](cache), - anomaly.WithKeyGenerator[*anomaly.WeeklyProvider](queryBuilder.NewKeyGenerator()), - anomaly.WithReader[*anomaly.WeeklyProvider](reader), - ) - } - - if t.seasonality == anomaly.SeasonalityHourly { - t.providerV2 = anomalyV2.NewHourlyProvider( - anomalyV2.WithQuerier[*anomalyV2.HourlyProvider](querierV5), - anomalyV2.WithLogger[*anomalyV2.HourlyProvider](logger), - ) - } else if t.seasonality == anomaly.SeasonalityDaily { - t.providerV2 = anomalyV2.NewDailyProvider( - anomalyV2.WithQuerier[*anomalyV2.DailyProvider](querierV5), - anomalyV2.WithLogger[*anomalyV2.DailyProvider](logger), - ) - } else if t.seasonality == anomaly.SeasonalityWeekly { - t.providerV2 = anomalyV2.NewWeeklyProvider( - anomalyV2.WithQuerier[*anomalyV2.WeeklyProvider](querierV5), - anomalyV2.WithLogger[*anomalyV2.WeeklyProvider](logger), + anomaly.WithQuerier[*anomaly.WeeklyProvider](querier), + anomaly.WithLogger[*anomaly.WeeklyProvider](logger), ) } - t.querierV5 = querierV5 + t.querier = querier t.version = p.Version t.logger = logger return &t, nil } func (r *AnomalyRule) Type() ruletypes.RuleType { - return RuleTypeAnomaly + return ruletypes.RuleTypeAnomaly } -func (r *AnomalyRule) prepareQueryRange(ctx context.Context, ts time.Time) (*v3.QueryRangeParamsV3, error) { - - r.logger.InfoContext( - ctx, "prepare query range request v4", "ts", ts.UnixMilli(), "eval_window", r.EvalWindow().Milliseconds(), "eval_delay", r.EvalDelay().Milliseconds(), - ) - - st, en := r.Timestamps(ts) - start := st.UnixMilli() - end := en.UnixMilli() +func (r *AnomalyRule) prepareQueryRange(ctx context.Context, ts time.Time) *qbtypes.QueryRangeRequest { - compositeQuery := r.Condition().CompositeQuery - - if compositeQuery.PanelType != v3.PanelTypeGraph { - compositeQuery.PanelType = v3.PanelTypeGraph - } - - // default mode - return &v3.QueryRangeParamsV3{ - Start: start, - End: end, - Step: int64(math.Max(float64(common.MinAllowedStepInterval(start, end)), 60)), - CompositeQuery: compositeQuery, - Variables: make(map[string]interface{}, 0), - NoCache: false, - }, nil -} - -func (r *AnomalyRule) prepareQueryRangeV5(ctx context.Context, ts time.Time) (*qbtypes.QueryRangeRequest, error) { - - r.logger.InfoContext(ctx, "prepare query range request v5", "ts", ts.UnixMilli(), "eval_window", r.EvalWindow().Milliseconds(), "eval_delay", r.EvalDelay().Milliseconds()) + r.logger.InfoContext(ctx, "prepare query range request", slog.String("rule.id", r.ID()), slog.Int64("ts", ts.UnixMilli()), slog.Int64("eval.window_ms", r.EvalWindow().Milliseconds()), slog.Int64("eval.delay_ms", r.EvalDelay().Milliseconds())) startTs, endTs := r.Timestamps(ts) start, end := startTs.UnixMilli(), endTs.UnixMilli() @@ -202,25 +122,14 @@ func (r *AnomalyRule) prepareQueryRangeV5(ctx context.Context, ts time.Time) (*q } req.CompositeQuery.Queries = make([]qbtypes.QueryEnvelope, len(r.Condition().CompositeQuery.Queries)) copy(req.CompositeQuery.Queries, r.Condition().CompositeQuery.Queries) - return req, nil -} - -func (r *AnomalyRule) GetSelectedQuery() string { - return r.Condition().GetSelectedQueryName() + return req } func (r *AnomalyRule) buildAndRunQuery(ctx context.Context, orgID valuer.UUID, ts time.Time) (ruletypes.Vector, error) { - params, err := r.prepareQueryRange(ctx, ts) - if err != nil { - return nil, err - } - err = r.PopulateTemporality(ctx, orgID, params) - if err != nil { - return nil, fmt.Errorf("internal error while setting temporality") - } + params := r.prepareQueryRange(ctx, ts) - anomalies, err := r.provider.GetAnomalies(ctx, orgID, &anomaly.GetAnomaliesRequest{ + anomalies, err := r.provider.GetAnomalies(ctx, orgID, &anomaly.AnomaliesRequest{ Params: params, Seasonality: r.seasonality, }) @@ -228,87 +137,43 @@ func (r *AnomalyRule) buildAndRunQuery(ctx context.Context, orgID valuer.UUID, t return nil, err } - var queryResult *v3.Result + var queryResult *qbtypes.TimeSeriesData for _, result := range anomalies.Results { - if result.QueryName == r.GetSelectedQuery() { + if result.QueryName == r.SelectedQuery(ctx) { queryResult = result break } } - hasData := len(queryResult.AnomalyScores) > 0 - if missingDataAlert := r.HandleMissingDataAlert(ctx, ts, hasData); missingDataAlert != nil { - return ruletypes.Vector{*missingDataAlert}, nil + if queryResult == nil { + r.logger.WarnContext(ctx, "nil qb result", slog.String("rule.id", r.ID()), slog.Int64("ts", ts.UnixMilli())) + return ruletypes.Vector{}, nil } - var resultVector ruletypes.Vector - - scoresJSON, _ := json.Marshal(queryResult.AnomalyScores) - r.logger.InfoContext(ctx, "anomaly scores", "scores", string(scoresJSON)) + hasData := len(queryResult.Aggregations) > 0 && + queryResult.Aggregations[0] != nil && + len(queryResult.Aggregations[0].AnomalyScores) > 0 - for _, series := range queryResult.AnomalyScores { - if !r.Condition().ShouldEval(series) { - r.logger.InfoContext(ctx, "not enough data points to evaluate series, skipping", "ruleid", r.ID(), "numPoints", len(series.Points), "requiredPoints", r.Condition().RequiredNumPoints) - continue - } - results, err := r.Threshold.Eval(*series, r.Unit(), ruletypes.EvalData{ - ActiveAlerts: r.ActiveAlertsLabelFP(), - SendUnmatched: r.ShouldSendUnmatched(), - }) - if err != nil { - return nil, err - } - resultVector = append(resultVector, results...) - } - return resultVector, nil -} - -func (r *AnomalyRule) buildAndRunQueryV5(ctx context.Context, orgID valuer.UUID, ts time.Time) (ruletypes.Vector, error) { - - params, err := r.prepareQueryRangeV5(ctx, ts) - if err != nil { - return nil, err - } - - anomalies, err := r.providerV2.GetAnomalies(ctx, orgID, &anomalyV2.AnomaliesRequest{ - Params: *params, - Seasonality: anomalyV2.Seasonality{String: valuer.NewString(r.seasonality.String())}, - }) - if err != nil { - return nil, err - } - - var qbResult *qbtypes.TimeSeriesData - for _, result := range anomalies.Results { - if result.QueryName == r.GetSelectedQuery() { - qbResult = result - break - } - } - - if qbResult == nil { - r.logger.WarnContext(ctx, "nil qb result", "ts", ts.UnixMilli()) - } - - queryResult := transition.ConvertV5TimeSeriesDataToV4Result(qbResult) - - hasData := len(queryResult.AnomalyScores) > 0 if missingDataAlert := r.HandleMissingDataAlert(ctx, ts, hasData); missingDataAlert != nil { return ruletypes.Vector{*missingDataAlert}, nil + } else if !hasData { + r.logger.WarnContext(ctx, "no anomaly result", slog.String("rule.id", r.ID())) + return ruletypes.Vector{}, nil } var resultVector ruletypes.Vector - scoresJSON, _ := json.Marshal(queryResult.AnomalyScores) - r.logger.InfoContext(ctx, "anomaly scores", "scores", string(scoresJSON)) + scoresJSON, _ := json.Marshal(queryResult.Aggregations[0].AnomalyScores) + // TODO(srikanthccv): this could be noisy but we do this to answer false alert requests + r.logger.InfoContext(ctx, "anomaly scores", slog.String("rule.id", r.ID()), slog.String("anomaly.scores", string(scoresJSON))) // Filter out new series if newGroupEvalDelay is configured - seriesToProcess := queryResult.AnomalyScores + seriesToProcess := queryResult.Aggregations[0].AnomalyScores if r.ShouldSkipNewGroups() { filteredSeries, filterErr := r.BaseRule.FilterNewSeries(ctx, ts, seriesToProcess) // In case of error we log the error and continue with the original series if filterErr != nil { - r.logger.ErrorContext(ctx, "Error filtering new series, ", "error", filterErr, "rule_name", r.Name()) + r.logger.ErrorContext(ctx, "error filtering new series", slog.String("rule.id", r.ID()), errors.Attr(filterErr)) } else { seriesToProcess = filteredSeries } @@ -316,10 +181,10 @@ func (r *AnomalyRule) buildAndRunQueryV5(ctx context.Context, orgID valuer.UUID, for _, series := range seriesToProcess { if !r.Condition().ShouldEval(series) { - r.logger.InfoContext(ctx, "not enough data points to evaluate series, skipping", "ruleid", r.ID(), "numPoints", len(series.Points), "requiredPoints", r.Condition().RequiredNumPoints) + r.logger.InfoContext(ctx, "not enough data points to evaluate series, skipping", slog.String("rule.id", r.ID()), slog.Int("series.num_points", len(series.Values)), slog.Int("series.required_points", r.Condition().RequiredNumPoints)) continue } - results, err := r.Threshold.Eval(*series, r.Unit(), ruletypes.EvalData{ + results, err := r.Threshold.Eval(series, r.Unit(), ruletypes.EvalData{ ActiveAlerts: r.ActiveAlertsLabelFP(), SendUnmatched: r.ShouldSendUnmatched(), }) @@ -340,13 +205,9 @@ func (r *AnomalyRule) Eval(ctx context.Context, ts time.Time) (int, error) { var res ruletypes.Vector var err error - if r.version == "v5" { - r.logger.InfoContext(ctx, "running v5 query") - res, err = r.buildAndRunQueryV5(ctx, r.OrgID(), ts) - } else { - r.logger.InfoContext(ctx, "running v4 query") - res, err = r.buildAndRunQuery(ctx, r.OrgID(), ts) - } + r.logger.InfoContext(ctx, "running query", slog.String("rule.id", r.ID())) + res, err = r.buildAndRunQuery(ctx, r.OrgID(), ts) + if err != nil { return 0, err } @@ -370,7 +231,7 @@ func (r *AnomalyRule) Eval(ctx context.Context, ts time.Time) (int, error) { } value := valueFormatter.Format(smpl.V, r.Unit()) threshold := valueFormatter.Format(smpl.Target, smpl.TargetUnit) - r.logger.DebugContext(ctx, "Alert template data for rule", "rule_name", r.Name(), "formatter", valueFormatter.Name(), "value", value, "threshold", threshold) + r.logger.DebugContext(ctx, "alert template data for rule", slog.String("rule.id", r.ID()), slog.String("formatter.name", valueFormatter.Name()), slog.String("alert.value", value), slog.String("alert.threshold", threshold)) tmplData := ruletypes.AlertTemplateData(l, value, threshold) // Inject some convenience variables that are easier to remember for users @@ -385,35 +246,34 @@ func (r *AnomalyRule) Eval(ctx context.Context, ts time.Time) (int, error) { defs+text, "__alert_"+r.Name(), tmplData, - times.Time(timestamp.FromTime(ts)), nil, ) result, err := tmpl.Expand() if err != nil { result = fmt.Sprintf("", err) - r.logger.ErrorContext(ctx, "Expanding alert template failed", "error", err, "data", tmplData, "rule_name", r.Name()) + r.logger.ErrorContext(ctx, "expanding alert template failed", slog.String("rule.id", r.ID()), errors.Attr(err), slog.Any("alert.template_data", tmplData)) } return result } - lb := labels.NewBuilder(smpl.Metric).Del(labels.MetricNameLabel).Del(labels.TemporalityLabel) - resultLabels := labels.NewBuilder(smpl.Metric).Del(labels.MetricNameLabel).Del(labels.TemporalityLabel).Labels() + lb := ruletypes.NewBuilder(smpl.Metric...).Del(ruletypes.MetricNameLabel).Del(ruletypes.TemporalityLabel) + resultLabels := ruletypes.NewBuilder(smpl.Metric...).Del(ruletypes.MetricNameLabel).Del(ruletypes.TemporalityLabel).Labels() for name, value := range r.Labels().Map() { lb.Set(name, expand(value)) } - lb.Set(labels.AlertNameLabel, r.Name()) - lb.Set(labels.AlertRuleIdLabel, r.ID()) - lb.Set(labels.RuleSourceLabel, r.GeneratorURL()) + lb.Set(ruletypes.AlertNameLabel, r.Name()) + lb.Set(ruletypes.AlertRuleIDLabel, r.ID()) + lb.Set(ruletypes.RuleSourceLabel, r.GeneratorURL()) - annotations := make(labels.Labels, 0, len(r.Annotations().Map())) + annotations := make(ruletypes.Labels, 0, len(r.Annotations().Map())) for name, value := range r.Annotations().Map() { - annotations = append(annotations, labels.Label{Name: name, Value: expand(value)}) + annotations = append(annotations, ruletypes.Label{Name: name, Value: expand(value)}) } if smpl.IsMissing { - lb.Set(labels.AlertNameLabel, "[No data] "+r.Name()) - lb.Set(labels.NoDataLabel, "true") + lb.Set(ruletypes.AlertNameLabel, "[No data] "+r.Name()) + lb.Set(ruletypes.NoDataLabel, "true") } lbs := lb.Labels() @@ -421,17 +281,17 @@ func (r *AnomalyRule) Eval(ctx context.Context, ts time.Time) (int, error) { resultFPs[h] = struct{}{} if _, ok := alerts[h]; ok { - r.logger.ErrorContext(ctx, "the alert query returns duplicate records", "rule_id", r.ID(), "alert", alerts[h]) - err = fmt.Errorf("duplicate alert found, vector contains metrics with the same labelset after applying alert labels") + r.logger.ErrorContext(ctx, "the alert query returns duplicate records", slog.String("rule.id", r.ID()), slog.Any("alert", alerts[h])) + err = errors.NewInternalf(errors.CodeInternal, "duplicate alert found, vector contains metrics with the same labelset after applying alert labels") return 0, err } alerts[h] = &ruletypes.Alert{ Labels: lbs, - QueryResultLables: resultLabels, + QueryResultLabels: resultLabels, Annotations: annotations, ActiveAt: ts, - State: model.StatePending, + State: ruletypes.StatePending, Value: smpl.V, GeneratorURL: r.GeneratorURL(), Receivers: ruleReceiverMap[lbs.Map()[ruletypes.LabelThresholdName]], @@ -440,12 +300,12 @@ func (r *AnomalyRule) Eval(ctx context.Context, ts time.Time) (int, error) { } } - r.logger.InfoContext(ctx, "number of alerts found", "rule_name", r.Name(), "alerts_count", len(alerts)) + r.logger.InfoContext(ctx, "number of alerts found", slog.String("rule.id", r.ID()), slog.Int("alert.count", len(alerts))) // alerts[h] is ready, add or update active list now for h, a := range alerts { // Check whether we already have alerting state for the identifying label set. // Update the last value and annotations if so, create a new alert entry otherwise. - if alert, ok := r.Active[h]; ok && alert.State != model.StateInactive { + if alert, ok := r.Active[h]; ok && alert.State != ruletypes.StateInactive { alert.Value = a.Value alert.Annotations = a.Annotations @@ -461,76 +321,76 @@ func (r *AnomalyRule) Eval(ctx context.Context, ts time.Time) (int, error) { r.Active[h] = a } - itemsToAdd := []model.RuleStateHistory{} + itemsToAdd := []rulestatehistorytypes.RuleStateHistory{} // Check if any pending alerts should be removed or fire now. Write out alert timeseries. for fp, a := range r.Active { - labelsJSON, err := json.Marshal(a.QueryResultLables) + labelsJSON, err := json.Marshal(a.QueryResultLabels) if err != nil { - r.logger.ErrorContext(ctx, "error marshaling labels", "error", err, "labels", a.Labels) + r.logger.ErrorContext(ctx, "error marshaling labels", slog.String("rule.id", r.ID()), errors.Attr(err), slog.Any("alert.labels", a.Labels)) } if _, ok := resultFPs[fp]; !ok { // If the alert was previously firing, keep it around for a given // retention time so it is reported as resolved to the AlertManager. - if a.State == model.StatePending || (!a.ResolvedAt.IsZero() && ts.Sub(a.ResolvedAt) > ruletypes.ResolvedRetention) { + if a.State == ruletypes.StatePending || (!a.ResolvedAt.IsZero() && ts.Sub(a.ResolvedAt) > ruletypes.ResolvedRetention) { delete(r.Active, fp) } - if a.State != model.StateInactive { - a.State = model.StateInactive + if a.State != ruletypes.StateInactive { + a.State = ruletypes.StateInactive a.ResolvedAt = ts - itemsToAdd = append(itemsToAdd, model.RuleStateHistory{ + itemsToAdd = append(itemsToAdd, rulestatehistorytypes.RuleStateHistory{ RuleID: r.ID(), RuleName: r.Name(), - State: model.StateInactive, + State: ruletypes.StateInactive, StateChanged: true, UnixMilli: ts.UnixMilli(), - Labels: model.LabelsString(labelsJSON), - Fingerprint: a.QueryResultLables.Hash(), + Labels: rulestatehistorytypes.LabelsString(labelsJSON), + Fingerprint: a.QueryResultLabels.Hash(), Value: a.Value, }) } continue } - if a.State == model.StatePending && ts.Sub(a.ActiveAt) >= r.HoldDuration().Duration() { - a.State = model.StateFiring + if a.State == ruletypes.StatePending && ts.Sub(a.ActiveAt) >= r.HoldDuration().Duration() { + a.State = ruletypes.StateFiring a.FiredAt = ts - state := model.StateFiring + state := ruletypes.StateFiring if a.Missing { - state = model.StateNoData + state = ruletypes.StateNoData } - itemsToAdd = append(itemsToAdd, model.RuleStateHistory{ + itemsToAdd = append(itemsToAdd, rulestatehistorytypes.RuleStateHistory{ RuleID: r.ID(), RuleName: r.Name(), State: state, StateChanged: true, UnixMilli: ts.UnixMilli(), - Labels: model.LabelsString(labelsJSON), - Fingerprint: a.QueryResultLables.Hash(), + Labels: rulestatehistorytypes.LabelsString(labelsJSON), + Fingerprint: a.QueryResultLabels.Hash(), Value: a.Value, }) } // We need to change firing alert to recovering if the returned sample meets recovery threshold - changeFiringToRecovering := a.State == model.StateFiring && a.IsRecovering + changeFiringToRecovering := a.State == ruletypes.StateFiring && a.IsRecovering // We need to change recovering alerts to firing if the returned sample meets target threshold - changeRecoveringToFiring := a.State == model.StateRecovering && !a.IsRecovering && !a.Missing + changeRecoveringToFiring := a.State == ruletypes.StateRecovering && !a.IsRecovering && !a.Missing // in any of the above case we need to update the status of alert if changeFiringToRecovering || changeRecoveringToFiring { - state := model.StateRecovering + state := ruletypes.StateRecovering if changeRecoveringToFiring { - state = model.StateFiring + state = ruletypes.StateFiring } a.State = state - r.logger.DebugContext(ctx, "converting alert state", "name", r.Name(), "state", state) - itemsToAdd = append(itemsToAdd, model.RuleStateHistory{ + r.logger.DebugContext(ctx, "converting alert state", slog.String("rule.id", r.ID()), slog.Any("alert.state", state)) + itemsToAdd = append(itemsToAdd, rulestatehistorytypes.RuleStateHistory{ RuleID: r.ID(), RuleName: r.Name(), State: state, StateChanged: true, UnixMilli: ts.UnixMilli(), - Labels: model.LabelsString(labelsJSON), - Fingerprint: a.QueryResultLables.Hash(), + Labels: rulestatehistorytypes.LabelsString(labelsJSON), + Fingerprint: a.QueryResultLabels.Hash(), Value: a.Value, }) } diff --git a/ee/query-service/rules/anomaly_test.go b/ee/query-service/rules/anomaly_test.go index cc338af153d..876509be807 100644 --- a/ee/query-service/rules/anomaly_test.go +++ b/ee/query-service/rules/anomaly_test.go @@ -2,21 +2,19 @@ package rules import ( "context" - "log/slog" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/SigNoz/signoz/ee/query-service/anomaly" "github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest" - "github.com/SigNoz/signoz/pkg/query-service/app/clickhouseReader" - v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3" - "github.com/SigNoz/signoz/pkg/telemetrystore" - "github.com/SigNoz/signoz/pkg/telemetrystore/telemetrystoretest" + qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5" "github.com/SigNoz/signoz/pkg/types/ruletypes" + "github.com/SigNoz/signoz/pkg/types/telemetrytypes" "github.com/SigNoz/signoz/pkg/valuer" + + "github.com/SigNoz/signoz/ee/anomaly" ) // mockAnomalyProvider is a mock implementation of anomaly.Provider for testing. @@ -24,13 +22,13 @@ import ( // time periods (current, past period, current season, past season, past 2 seasons, // past 3 seasons), making it cumbersome to create mock data. type mockAnomalyProvider struct { - responses []*anomaly.GetAnomaliesResponse + responses []*anomaly.AnomaliesResponse callCount int } -func (m *mockAnomalyProvider) GetAnomalies(ctx context.Context, orgID valuer.UUID, req *anomaly.GetAnomaliesRequest) (*anomaly.GetAnomaliesResponse, error) { +func (m *mockAnomalyProvider) GetAnomalies(ctx context.Context, orgID valuer.UUID, req *anomaly.AnomaliesRequest) (*anomaly.AnomaliesResponse, error) { if m.callCount >= len(m.responses) { - return &anomaly.GetAnomaliesResponse{Results: []*v3.Result{}}, nil + return &anomaly.AnomaliesResponse{Results: []*qbtypes.TimeSeriesData{}}, nil } resp := m.responses[m.callCount] m.callCount++ @@ -49,45 +47,46 @@ func TestAnomalyRule_NoData_AlertOnAbsent(t *testing.T) { postableRule := ruletypes.PostableRule{ AlertName: "Test anomaly no data", AlertType: ruletypes.AlertTypeMetric, - RuleType: RuleTypeAnomaly, + RuleType: ruletypes.RuleTypeAnomaly, Evaluation: &ruletypes.EvaluationEnvelope{Kind: ruletypes.RollingEvaluation, Spec: ruletypes.RollingWindow{ EvalWindow: evalWindow, Frequency: valuer.MustParseTextDuration("1m"), }}, RuleCondition: &ruletypes.RuleCondition{ - CompareOp: ruletypes.ValueIsAbove, - MatchType: ruletypes.AtleastOnce, - Target: &target, - CompositeQuery: &v3.CompositeQuery{ - QueryType: v3.QueryTypeBuilder, - BuilderQueries: map[string]*v3.BuilderQuery{ - "A": { - QueryName: "A", - Expression: "A", - DataSource: v3.DataSourceMetrics, - Temporality: v3.Unspecified, + CompareOperator: ruletypes.ValueIsAbove, + MatchType: ruletypes.AtleastOnce, + Target: &target, + CompositeQuery: &ruletypes.AlertCompositeQuery{ + QueryType: ruletypes.QueryTypeBuilder, + Queries: []qbtypes.QueryEnvelope{{ + Type: qbtypes.QueryTypeBuilder, + Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{ + Name: "A", + Signal: telemetrytypes.SignalMetrics, }, - }, + }}, }, SelectedQuery: "A", Seasonality: "daily", Thresholds: &ruletypes.RuleThresholdData{ Kind: ruletypes.BasicThresholdKind, Spec: ruletypes.BasicRuleThresholds{{ - Name: "Test anomaly no data", - TargetValue: &target, - MatchType: ruletypes.AtleastOnce, - CompareOp: ruletypes.ValueIsAbove, + Name: "Test anomaly no data", + TargetValue: &target, + MatchType: ruletypes.AtleastOnce, + CompareOperator: ruletypes.ValueIsAbove, }}, }, }, } - responseNoData := &anomaly.GetAnomaliesResponse{ - Results: []*v3.Result{ + responseNoData := &anomaly.AnomaliesResponse{ + Results: []*qbtypes.TimeSeriesData{ { - QueryName: "A", - AnomalyScores: []*v3.Series{}, + QueryName: "A", + Aggregations: []*qbtypes.AggregationBucket{{ + AnomalyScores: []*qbtypes.TimeSeries{}, + }}, }, }, } @@ -115,23 +114,17 @@ func TestAnomalyRule_NoData_AlertOnAbsent(t *testing.T) { t.Run(c.description, func(t *testing.T) { postableRule.RuleCondition.AlertOnAbsent = c.alertOnAbsent - telemetryStore := telemetrystoretest.New(telemetrystore.Config{}, nil) - options := clickhouseReader.NewOptions("primaryNamespace") - reader := clickhouseReader.NewReader(slog.Default(), nil, telemetryStore, nil, "", time.Second, nil, nil, options) - rule, err := NewAnomalyRule( "test-anomaly-rule", valuer.GenerateUUID(), &postableRule, - reader, nil, logger, - nil, ) require.NoError(t, err) rule.provider = &mockAnomalyProvider{ - responses: []*anomaly.GetAnomaliesResponse{responseNoData}, + responses: []*anomaly.AnomaliesResponse{responseNoData}, } alertsFound, err := rule.Eval(context.Background(), evalTime) @@ -156,46 +149,47 @@ func TestAnomalyRule_NoData_AbsentFor(t *testing.T) { postableRule := ruletypes.PostableRule{ AlertName: "Test anomaly no data with AbsentFor", AlertType: ruletypes.AlertTypeMetric, - RuleType: RuleTypeAnomaly, + RuleType: ruletypes.RuleTypeAnomaly, Evaluation: &ruletypes.EvaluationEnvelope{Kind: ruletypes.RollingEvaluation, Spec: ruletypes.RollingWindow{ EvalWindow: evalWindow, Frequency: valuer.MustParseTextDuration("1m"), }}, RuleCondition: &ruletypes.RuleCondition{ - CompareOp: ruletypes.ValueIsAbove, - MatchType: ruletypes.AtleastOnce, - AlertOnAbsent: true, - Target: &target, - CompositeQuery: &v3.CompositeQuery{ - QueryType: v3.QueryTypeBuilder, - BuilderQueries: map[string]*v3.BuilderQuery{ - "A": { - QueryName: "A", - Expression: "A", - DataSource: v3.DataSourceMetrics, - Temporality: v3.Unspecified, + CompareOperator: ruletypes.ValueIsAbove, + MatchType: ruletypes.AtleastOnce, + AlertOnAbsent: true, + Target: &target, + CompositeQuery: &ruletypes.AlertCompositeQuery{ + QueryType: ruletypes.QueryTypeBuilder, + Queries: []qbtypes.QueryEnvelope{{ + Type: qbtypes.QueryTypeBuilder, + Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{ + Name: "A", + Signal: telemetrytypes.SignalMetrics, }, - }, + }}, }, SelectedQuery: "A", Seasonality: "daily", Thresholds: &ruletypes.RuleThresholdData{ Kind: ruletypes.BasicThresholdKind, Spec: ruletypes.BasicRuleThresholds{{ - Name: "Test anomaly no data with AbsentFor", - TargetValue: &target, - MatchType: ruletypes.AtleastOnce, - CompareOp: ruletypes.ValueIsAbove, + Name: "Test anomaly no data with AbsentFor", + TargetValue: &target, + MatchType: ruletypes.AtleastOnce, + CompareOperator: ruletypes.ValueIsAbove, }}, }, }, } - responseNoData := &anomaly.GetAnomaliesResponse{ - Results: []*v3.Result{ + responseNoData := &anomaly.AnomaliesResponse{ + Results: []*qbtypes.TimeSeriesData{ { - QueryName: "A", - AnomalyScores: []*v3.Series{}, + QueryName: "A", + Aggregations: []*qbtypes.AggregationBucket{{ + AnomalyScores: []*qbtypes.TimeSeries{}, + }}, }, }, } @@ -229,32 +223,35 @@ func TestAnomalyRule_NoData_AbsentFor(t *testing.T) { t1 := baseTime.Add(5 * time.Minute) t2 := t1.Add(c.timeBetweenEvals) - responseWithData := &anomaly.GetAnomaliesResponse{ - Results: []*v3.Result{ + responseWithData := &anomaly.AnomaliesResponse{ + Results: []*qbtypes.TimeSeriesData{ { QueryName: "A", - AnomalyScores: []*v3.Series{ - { - Labels: map[string]string{"test": "label"}, - Points: []v3.Point{ - {Timestamp: baseTime.UnixMilli(), Value: 1.0}, - {Timestamp: baseTime.Add(time.Minute).UnixMilli(), Value: 1.5}, + Aggregations: []*qbtypes.AggregationBucket{{ + AnomalyScores: []*qbtypes.TimeSeries{ + { + Labels: []*qbtypes.Label{ + { + Key: telemetrytypes.TelemetryFieldKey{Name: "Test"}, + Value: "labels", + }, + }, + Values: []*qbtypes.TimeSeriesValue{ + {Timestamp: baseTime.UnixMilli(), Value: 1.0}, + {Timestamp: baseTime.Add(time.Minute).UnixMilli(), Value: 1.5}, + }, }, }, - }, + }}, }, }, } - telemetryStore := telemetrystoretest.New(telemetrystore.Config{}, nil) - options := clickhouseReader.NewOptions("primaryNamespace") - reader := clickhouseReader.NewReader(slog.Default(), nil, telemetryStore, nil, "", time.Second, nil, nil, options) - - rule, err := NewAnomalyRule("test-anomaly-rule", valuer.GenerateUUID(), &postableRule, reader, nil, logger, nil) + rule, err := NewAnomalyRule("test-anomaly-rule", valuer.GenerateUUID(), &postableRule, nil, logger) require.NoError(t, err) rule.provider = &mockAnomalyProvider{ - responses: []*anomaly.GetAnomaliesResponse{responseWithData, responseNoData}, + responses: []*anomaly.AnomaliesResponse{responseWithData, responseNoData}, } alertsFound1, err := rule.Eval(context.Background(), t1) diff --git a/ee/query-service/rules/manager.go b/ee/query-service/rules/manager.go index 1f9b19e763a..64a778f1171 100644 --- a/ee/query-service/rules/manager.go +++ b/ee/query-service/rules/manager.go @@ -6,14 +6,14 @@ import ( "time" + "log/slog" + + "github.com/google/uuid" + "github.com/SigNoz/signoz/pkg/errors" - basemodel "github.com/SigNoz/signoz/pkg/query-service/model" baserules "github.com/SigNoz/signoz/pkg/query-service/rules" - "github.com/SigNoz/signoz/pkg/query-service/utils/labels" "github.com/SigNoz/signoz/pkg/types/ruletypes" "github.com/SigNoz/signoz/pkg/valuer" - "github.com/google/uuid" - "log/slog" ) func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error) { @@ -21,24 +21,25 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error) rules := make([]baserules.Rule, 0) var task baserules.Task - ruleId := baserules.RuleIdFromTaskName(opts.TaskName) + ruleID := baserules.RuleIDFromTaskName(opts.TaskName) evaluation, err := opts.Rule.Evaluation.GetEvaluation() if err != nil { return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "evaluation is invalid: %v", err) } + if opts.Rule.RuleType == ruletypes.RuleTypeThreshold { // create a threshold rule tr, err := baserules.NewThresholdRule( - ruleId, + ruleID, opts.OrgID, opts.Rule, - opts.Reader, opts.Querier, opts.Logger, baserules.WithEvalDelay(opts.ManagerOpts.EvalDelay), baserules.WithSQLStore(opts.SQLStore), baserules.WithQueryParser(opts.ManagerOpts.QueryParser), baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore), + baserules.WithRuleStateHistoryModule(opts.ManagerOpts.RuleStateHistoryModule), ) if err != nil { @@ -54,15 +55,15 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error) // create promql rule pr, err := baserules.NewPromRule( - ruleId, + ruleID, opts.OrgID, opts.Rule, opts.Logger, - opts.Reader, opts.ManagerOpts.Prometheus, baserules.WithSQLStore(opts.SQLStore), baserules.WithQueryParser(opts.ManagerOpts.QueryParser), baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore), + baserules.WithRuleStateHistoryModule(opts.ManagerOpts.RuleStateHistoryModule), ) if err != nil { @@ -77,17 +78,16 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error) } else if opts.Rule.RuleType == ruletypes.RuleTypeAnomaly { // create anomaly rule ar, err := NewAnomalyRule( - ruleId, + ruleID, opts.OrgID, opts.Rule, - opts.Reader, opts.Querier, opts.Logger, - opts.Cache, baserules.WithEvalDelay(opts.ManagerOpts.EvalDelay), baserules.WithSQLStore(opts.SQLStore), baserules.WithQueryParser(opts.ManagerOpts.QueryParser), baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore), + baserules.WithRuleStateHistoryModule(opts.ManagerOpts.RuleStateHistoryModule), ) if err != nil { return task, err @@ -99,7 +99,7 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error) task = newTask(baserules.TaskTypeCh, opts.TaskName, evaluation.GetFrequency().Duration(), rules, opts.ManagerOpts, opts.NotifyFunc, opts.MaintenanceStore, opts.OrgID) } else { - return nil, fmt.Errorf("unsupported rule type %s. Supported types: %s, %s", opts.Rule.RuleType, ruletypes.RuleTypeProm, ruletypes.RuleTypeThreshold) + return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "unsupported rule type %s. Supported types: %s, %s", opts.Rule.RuleType, ruletypes.RuleTypeProm, ruletypes.RuleTypeThreshold) } return task, nil @@ -107,12 +107,12 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error) // TestNotification prepares a dummy rule for given rule parameters and // sends a test notification. returns alert count and error (if any) -func TestNotification(opts baserules.PrepareTestRuleOptions) (int, *basemodel.ApiError) { +func TestNotification(opts baserules.PrepareTestRuleOptions) (int, error) { ctx := context.Background() if opts.Rule == nil { - return 0, basemodel.BadRequest(fmt.Errorf("rule is required")) + return 0, errors.NewInvalidInputf(errors.CodeInvalidInput, "rule is required") } parsedRule := opts.Rule @@ -132,15 +132,14 @@ func TestNotification(opts baserules.PrepareTestRuleOptions) (int, *basemodel.Ap if parsedRule.RuleType == ruletypes.RuleTypeThreshold { // add special labels for test alerts - parsedRule.Labels[labels.RuleSourceLabel] = "" - parsedRule.Labels[labels.AlertRuleIdLabel] = "" + parsedRule.Labels[ruletypes.RuleSourceLabel] = "" + parsedRule.Labels[ruletypes.AlertRuleIDLabel] = "" // create a threshold rule rule, err = baserules.NewThresholdRule( alertname, opts.OrgID, parsedRule, - opts.Reader, opts.Querier, opts.Logger, baserules.WithSendAlways(), @@ -151,8 +150,8 @@ func TestNotification(opts baserules.PrepareTestRuleOptions) (int, *basemodel.Ap ) if err != nil { - slog.Error("failed to prepare a new threshold rule for test", "name", alertname, "error", err) - return 0, basemodel.BadRequest(err) + slog.Error("failed to prepare a new threshold rule for test", "name", alertname, errors.Attr(err)) + return 0, err } } else if parsedRule.RuleType == ruletypes.RuleTypeProm { @@ -163,7 +162,6 @@ func TestNotification(opts baserules.PrepareTestRuleOptions) (int, *basemodel.Ap opts.OrgID, parsedRule, opts.Logger, - opts.Reader, opts.ManagerOpts.Prometheus, baserules.WithSendAlways(), baserules.WithSendUnmatched(), @@ -173,8 +171,8 @@ func TestNotification(opts baserules.PrepareTestRuleOptions) (int, *basemodel.Ap ) if err != nil { - slog.Error("failed to prepare a new promql rule for test", "name", alertname, "error", err) - return 0, basemodel.BadRequest(err) + slog.Error("failed to prepare a new promql rule for test", "name", alertname, errors.Attr(err)) + return 0, err } } else if parsedRule.RuleType == ruletypes.RuleTypeAnomaly { // create anomaly rule @@ -182,10 +180,8 @@ func TestNotification(opts baserules.PrepareTestRuleOptions) (int, *basemodel.Ap alertname, opts.OrgID, parsedRule, - opts.Reader, opts.Querier, opts.Logger, - opts.Cache, baserules.WithSendAlways(), baserules.WithSendUnmatched(), baserules.WithSQLStore(opts.SQLStore), @@ -193,11 +189,11 @@ func TestNotification(opts baserules.PrepareTestRuleOptions) (int, *basemodel.Ap baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore), ) if err != nil { - slog.Error("failed to prepare a new anomaly rule for test", "name", alertname, "error", err) - return 0, basemodel.BadRequest(err) + slog.Error("failed to prepare a new anomaly rule for test", "name", alertname, errors.Attr(err)) + return 0, err } } else { - return 0, basemodel.BadRequest(fmt.Errorf("failed to derive ruletype with given information")) + return 0, errors.NewInvalidInputf(errors.CodeInvalidInput, "failed to derive ruletype with given information") } // set timestamp to current utc time @@ -205,8 +201,8 @@ func TestNotification(opts baserules.PrepareTestRuleOptions) (int, *basemodel.Ap alertsFound, err := rule.Eval(ctx, ts) if err != nil { - slog.Error("evaluating rule failed", "rule", rule.Name(), "error", err) - return 0, basemodel.InternalError(fmt.Errorf("rule evaluation failed")) + slog.Error("evaluating rule failed", "rule", rule.Name(), errors.Attr(err)) + return 0, err } rule.SendAlerts(ctx, ts, 0, time.Minute, opts.NotifyFunc) diff --git a/ee/query-service/rules/manager_test.go b/ee/query-service/rules/manager_test.go index e66dd12e65f..60c9496098b 100644 --- a/ee/query-service/rules/manager_test.go +++ b/ee/query-service/rules/manager_test.go @@ -114,11 +114,8 @@ func TestManager_TestNotification_SendUnmatched_ThresholdRule(t *testing.T) { }, }) - count, apiErr := mgr.TestNotification(context.Background(), orgID, string(ruleBytes)) - if apiErr != nil { - t.Logf("TestNotification error: %v, type: %s", apiErr.Err, apiErr.Typ) - } - require.Nil(t, apiErr) + count, err := mgr.TestNotification(context.Background(), orgID, string(ruleBytes)) + require.Nil(t, err) assert.Equal(t, tc.ExpectAlerts, count) if tc.ExpectAlerts > 0 { @@ -257,7 +254,7 @@ func TestManager_TestNotification_SendUnmatched_PromRule(t *testing.T) { WillReturnRows(samplesRows) // Create Prometheus provider for this test - promProvider = prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, store) + promProvider = prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{Timeout: 2 * time.Minute}, store) }, ManagerOptionsHook: func(opts *rules.ManagerOptions) { // Set Prometheus provider for PromQL queries @@ -268,11 +265,8 @@ func TestManager_TestNotification_SendUnmatched_PromRule(t *testing.T) { }, }) - count, apiErr := mgr.TestNotification(context.Background(), orgID, string(ruleBytes)) - if apiErr != nil { - t.Logf("TestNotification error: %v, type: %s", apiErr.Err, apiErr.Typ) - } - require.Nil(t, apiErr) + count, err := mgr.TestNotification(context.Background(), orgID, string(ruleBytes)) + require.Nil(t, err) assert.Equal(t, tc.ExpectAlerts, count) if tc.ExpectAlerts > 0 { diff --git a/ee/query-service/usage/manager.go b/ee/query-service/usage/manager.go index ec71b84378c..a10e7fee173 100644 --- a/ee/query-service/usage/manager.go +++ b/ee/query-service/usage/manager.go @@ -15,6 +15,7 @@ import ( "github.com/google/uuid" "github.com/SigNoz/signoz/ee/query-service/model" + "github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/licensing" "github.com/SigNoz/signoz/pkg/modules/organization" "github.com/SigNoz/signoz/pkg/query-service/utils/encryption" @@ -76,14 +77,14 @@ func (lm *Manager) Start(ctx context.Context) error { func (lm *Manager) UploadUsage(ctx context.Context) { organizations, err := lm.orgGetter.ListByOwnedKeyRange(ctx) if err != nil { - slog.ErrorContext(ctx, "failed to get organizations", "error", err) + slog.ErrorContext(ctx, "failed to get organizations", errors.Attr(err)) return } for _, organization := range organizations { // check if license is present or not license, err := lm.licenseService.GetActive(ctx, organization.ID) if err != nil { - slog.ErrorContext(ctx, "failed to get active license", "error", err) + slog.ErrorContext(ctx, "failed to get active license", errors.Attr(err)) return } if license == nil { @@ -115,7 +116,7 @@ func (lm *Manager) UploadUsage(ctx context.Context) { dbusages := []model.UsageDB{} err := lm.clickhouseConn.Select(ctx, &dbusages, fmt.Sprintf(query, db, db), time.Now().Add(-(24 * time.Hour))) if err != nil && !strings.Contains(err.Error(), "doesn't exist") { - slog.ErrorContext(ctx, "failed to get usage from clickhouse", "error", err) + slog.ErrorContext(ctx, "failed to get usage from clickhouse", errors.Attr(err)) return } for _, u := range dbusages { @@ -135,14 +136,14 @@ func (lm *Manager) UploadUsage(ctx context.Context) { for _, usage := range usages { usageDataBytes, err := encryption.Decrypt([]byte(usage.ExporterID[:32]), []byte(usage.Data)) if err != nil { - slog.ErrorContext(ctx, "error while decrypting usage data", "error", err) + slog.ErrorContext(ctx, "error while decrypting usage data", errors.Attr(err)) return } usageData := model.Usage{} err = json.Unmarshal(usageDataBytes, &usageData) if err != nil { - slog.ErrorContext(ctx, "error while unmarshalling usage data", "error", err) + slog.ErrorContext(ctx, "error while unmarshalling usage data", errors.Attr(err)) return } @@ -163,13 +164,13 @@ func (lm *Manager) UploadUsage(ctx context.Context) { body, errv2 := json.Marshal(payload) if errv2 != nil { - slog.ErrorContext(ctx, "error while marshalling usage payload", "error", errv2) + slog.ErrorContext(ctx, "error while marshalling usage payload", errors.Attr(errv2)) return } errv2 = lm.zeus.PutMeters(ctx, payload.LicenseKey.String(), body) if errv2 != nil { - slog.ErrorContext(ctx, "failed to upload usage", "error", errv2) + slog.ErrorContext(ctx, "failed to upload usage", errors.Attr(errv2)) // not returning error here since it is captured in the failed count return } diff --git a/ee/sqlschema/postgressqlschema/provider.go b/ee/sqlschema/postgressqlschema/provider.go index c063983ef86..f39c02fac38 100644 --- a/ee/sqlschema/postgressqlschema/provider.go +++ b/ee/sqlschema/postgressqlschema/provider.go @@ -4,11 +4,12 @@ import ( "context" "database/sql" + "github.com/uptrace/bun" + "github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/factory" "github.com/SigNoz/signoz/pkg/sqlschema" "github.com/SigNoz/signoz/pkg/sqlstore" - "github.com/uptrace/bun" ) type provider struct { @@ -113,7 +114,7 @@ WHERE defer func() { if err := constraintsRows.Close(); err != nil { - provider.settings.Logger().ErrorContext(ctx, "error closing rows", "error", err) + provider.settings.Logger().ErrorContext(ctx, "error closing rows", errors.Attr(err)) } }() @@ -174,7 +175,7 @@ WHERE defer func() { if err := foreignKeyConstraintsRows.Close(); err != nil { - provider.settings.Logger().ErrorContext(ctx, "error closing rows", "error", err) + provider.settings.Logger().ErrorContext(ctx, "error closing rows", errors.Attr(err)) } }() @@ -243,7 +244,7 @@ ORDER BY index_name, column_position`, string(name)) defer func() { if err := rows.Close(); err != nil { - provider.settings.Logger().ErrorContext(ctx, "error closing rows", "error", err) + provider.settings.Logger().ErrorContext(ctx, "error closing rows", errors.Attr(err)) } }() diff --git a/ee/sqlstore/postgressqlstore/dialect.go b/ee/sqlstore/postgressqlstore/dialect.go index 4f6987bda77..04f5ba53161 100644 --- a/ee/sqlstore/postgressqlstore/dialect.go +++ b/ee/sqlstore/postgressqlstore/dialect.go @@ -336,9 +336,10 @@ func (dialect *dialect) UpdatePrimaryKey(ctx context.Context, bun bun.IDB, oldMo } fkReference := "" - if reference == Org { + switch reference { + case Org: fkReference = OrgReference - } else if reference == User { + case User: fkReference = UserReference } @@ -392,9 +393,10 @@ func (dialect *dialect) AddPrimaryKey(ctx context.Context, bun bun.IDB, oldModel } fkReference := "" - if reference == Org { + switch reference { + case Org: fkReference = OrgReference - } else if reference == User { + case User: fkReference = UserReference } diff --git a/ee/sqlstore/postgressqlstore/provider.go b/ee/sqlstore/postgressqlstore/provider.go index a635e851c95..3400d217018 100644 --- a/ee/sqlstore/postgressqlstore/provider.go +++ b/ee/sqlstore/postgressqlstore/provider.go @@ -14,14 +14,21 @@ import ( "github.com/uptrace/bun/dialect/pgdialect" ) +var _ Pooler = new(provider) + type provider struct { settings factory.ScopedProviderSettings sqldb *sql.DB bundb *sqlstore.BunDB + pgxPool *pgxpool.Pool dialect *dialect formatter sqlstore.SQLFormatter } +type Pooler interface { + Pool() *pgxpool.Pool +} + func NewFactory(hookFactories ...factory.ProviderFactory[sqlstore.SQLStoreHook, sqlstore.Config]) factory.ProviderFactory[sqlstore.SQLStore, sqlstore.Config] { return factory.NewProviderFactory(factory.MustNewName("postgres"), func(ctx context.Context, providerSettings factory.ProviderSettings, config sqlstore.Config) (sqlstore.SQLStore, error) { hooks := make([]sqlstore.SQLStoreHook, len(hookFactories)) @@ -62,6 +69,7 @@ func New(ctx context.Context, providerSettings factory.ProviderSettings, config settings: settings, sqldb: sqldb, bundb: bunDB, + pgxPool: pool, dialect: new(dialect), formatter: newFormatter(bunDB.Dialect()), }, nil @@ -75,6 +83,10 @@ func (provider *provider) SQLDB() *sql.DB { return provider.sqldb } +func (provider *provider) Pool() *pgxpool.Pool { + return provider.pgxPool +} + func (provider *provider) Dialect() sqlstore.SQLDialect { return provider.dialect } diff --git a/ee/zeus/config.go b/ee/zeus/config.go index 2a81e556080..3a4775d2d4e 100644 --- a/ee/zeus/config.go +++ b/ee/zeus/config.go @@ -19,7 +19,7 @@ var ( once sync.Once ) -// initializes the Zeus configuration +// initializes the Zeus configuration. func Config() zeus.Config { once.Do(func() { parsedURL, err := neturl.Parse(url) diff --git a/ee/zeus/httpzeus/provider.go b/ee/zeus/httpzeus/provider.go index 83abdc013d4..76248edfe14 100644 --- a/ee/zeus/httpzeus/provider.go +++ b/ee/zeus/httpzeus/provider.go @@ -189,7 +189,7 @@ func (provider *Provider) do(ctx context.Context, url *url.URL, method string, k return nil, provider.errFromStatusCode(response.StatusCode, errorMessage) } -// This can be taken down to the client package +// This can be taken down to the client package. func (provider *Provider) errFromStatusCode(statusCode int, errorMessage string) error { switch statusCode { case http.StatusBadRequest: diff --git a/frontend/.cursor/rules/state-management.mdc b/frontend/.cursor/rules/state-management.mdc new file mode 100644 index 00000000000..fb5c02ae1d0 --- /dev/null +++ b/frontend/.cursor/rules/state-management.mdc @@ -0,0 +1,97 @@ +--- +globs: **/*.store.ts +alwaysApply: false +--- +# State Management: React Query, nuqs, Zustand + +Use the following stack. Do **not** introduce or recommend Redux or React Context for shared/global state. + +## Server state → React Query + +- **Use for:** API responses, time-series data, caching, background refetch, retries, stale/refresh. +- **Do not use Redux/Context** to store or mirror data that comes from React Query (e.g. do not dispatch API results into Redux). +- Prefer generated React Query hooks from `frontend/src/api/generated` when available. +- Keep server state in React Query; expose it via hooks that return the query result (and optionally memoized derived values). Do not duplicate it in Redux or Context. + +```tsx +// ✅ GOOD: single source of truth from React Query +export function useAppStateHook() { + const { data, isError } = useQuery(...) + const memoizedConfigs = useMemo(() => ({ ... }), [data?.configs]) + return { configs: memoizedConfigs, isError, ... } +} + +// ❌ BAD: copying React Query result into Redux +dispatch({ type: UPDATE_LATEST_VERSION, payload: queryResponse.data }) +``` + +## URL state → nuqs + +- **Use for:** shareable state, filters, time range, selected values, pagination, view state that belongs in the URL. +- **Do not use Redux/Context** for state that should be shareable or reflected in the URL. +- Use [nuqs](https://nuqs.dev/docs/basic-usage) for typed, type-safe URL search params. Avoid ad-hoc `useSearchParams` encoding/decoding. +- Keep URL payload small; respect browser URL length limits (e.g. Chrome ~2k chars). Do not put large datasets or sensitive data in query params. + +```tsx +// ✅ GOOD: nuqs for filters / time range / selection +const [timeRange, setTimeRange] = useQueryState('timeRange', parseAsString.withDefault('1h')) +const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1)) + +// ❌ BAD: Redux/Context for shareable or URL-synced state +const { timeRange } = useContext(SomeContext) +``` + +## Client state → Zustand + +- **Use for:** global/client state, cross-component state, feature flags, complex or large client objects (e.g. dashboard state, query builder state). +- **Do not use Redux or React Context** for global or feature-level client state. +- Prefer small, domain-scoped stores (e.g. DashboardStore, QueryBuilderStore). + +### Zustand best practices (align with eslint-plugin-zustand-rules) + +- **One store per module.** Do not define multiple `create()` calls in the same file; use one store per module (or compose slices into one store). +- **Always use selectors.** Call the store hook with a selector so only the used slice triggers re-renders. Never use `useStore()` with no selector. + +```tsx +// ✅ GOOD: selector — re-renders only when isDashboardLocked changes +const isLocked = useDashboardStore(state => state.isDashboardLocked) + +// ❌ BAD: no selector — re-renders on any store change +const state = useDashboardStore() +``` + +- **Never mutate state directly.** Update only via `set` or `setState` (or `getState()` + `set` for reads). No `state.foo = x` or `state.bears += 1` inside actions. + +```tsx +// ✅ GOOD: use set +increment: () => set(state => ({ bears: state.bears + 1 })) + +// ❌ BAD: direct mutation +increment: () => { state.bears += 1 } +``` + +- **State properties before actions.** In the store object, list all state fields first, then action functions. +- **Split into slices when state is large.** If a store has many top-level properties (e.g. more than 5–10), split into slice factories and combine with one `create()`. + +```tsx +// ✅ GOOD: slices for large state +const createBearSlice = set => ({ bears: 0, addBear: () => set(s => ({ bears: s.bears + 1 })) }) +const createFishSlice = set => ({ fish: 0, addFish: () => set(s => ({ fish: s.fish + 1 })) }) +const useStore = create(set => ({ ...createBearSlice(set), ...createFishSlice(set) })) +``` + +- **In projects using Zustand:** add `eslint-plugin-zustand-rules` and extend `plugin:zustand-rules/recommended` to enforce these rules automatically. + +## Local state → React state only + +- **Use useState/useReducer** for: component-local UI state, form inputs, toggles, hover state, data that never leaves the component. +- Do not use Zustand, Redux, or Context for state that is purely local to one component or a small subtree. + +## Summary + +| State type | Use | Avoid | +|-------------------|------------------|--------------------| +| Server / API | React Query | Redux, Context | +| URL / shareable | nuqs | Redux, Context | +| Global client | Zustand | Redux, Context | +| Local UI | useState/useReducer | Zustand, Redux, Context | diff --git a/frontend/.cursor/skills/migrate-state-management/SKILL.md b/frontend/.cursor/skills/migrate-state-management/SKILL.md new file mode 100644 index 00000000000..e086689aa0d --- /dev/null +++ b/frontend/.cursor/skills/migrate-state-management/SKILL.md @@ -0,0 +1,150 @@ +--- +name: migrate-state-management +description: Migrate Redux or React Context to the correct state option (React Query for server state, nuqs for URL/shareable state, Zustand for global client state). Use when refactoring away from Redux/Context, moving state to the right store, or when the user asks to migrate state management. +--- + +# Migrate State: Redux/Context → React Query, nuqs, Zustand + +Do **not** introduce or recommend Redux or React Context. Migrate existing usage to the stack below. + +## 1. Classify the state + +Before changing code, classify what the state represents: + +| If the state is… | Migrate to | Do not use | +|------------------|------------|------------| +| From API / server (versions, configs, fetched lists, time-series) | **React Query** | Redux, Context | +| Shareable via URL (filters, time range, page, selected ids) | **nuqs** | Redux, Context | +| Global/client UI (dashboard lock, query builder, feature flags, large client objects) | **Zustand** | Redux, Context | +| Local to one component (inputs, toggles, hover) | **useState / useReducer** | Zustand, Redux, Context | + +If one slice mixes concerns (e.g. Redux has both API data and pagination), split: API → React Query, pagination → nuqs, rest → Zustand or local state. + +## 2. Migrate to React Query (server state) + +**When:** State comes from or mirrors an API response (e.g. `currentVersion`, `latestVersion`, `configs`, lists). + +**Steps:** + +1. Find where the data is fetched (existing `useQuery`/API call) and where it is dispatched or set in Context/Redux. +2. Remove the dispatch/set that writes API results into Redux/Context. +3. Expose a single hook that uses the query and returns the same shape consumers expect (use `useMemo` for derived objects like `configs` to avoid unnecessary re-renders). +4. Replace Redux/Context consumption with the new hook. Prefer generated React Query hooks from `frontend/src/api/generated` when available. +5. Configure cache/refetch (e.g. `refetchOnMount: false`, `staleTime`) so behavior matches previous “single source” expectations. + +**Before (Redux mirroring React Query):** + +```tsx +if (getUserLatestVersionResponse.isFetched && getUserLatestVersionResponse.isSuccess && getUserLatestVersionResponse.data?.payload) { + dispatch({ type: UPDATE_LATEST_VERSION, payload: { latestVersion: getUserLatestVersionResponse.data.payload.tag_name } }) +} +``` + +**After (single source in React Query):** + +```tsx +export function useAppStateHook() { + const { data, isError } = useQuery(...) + const memoizedConfigs = useMemo(() => ({ ... }), [data?.configs]) + return { + latestVersion: data?.payload?.tag_name, + configs: memoizedConfigs, + isError, + } +} +``` + +Consumers use `useAppStateHook()` instead of `useSelector` or Context. Do not copy React Query result into Redux or Context. + +## 3. Migrate to nuqs (URL / shareable state) + +**When:** State should be in the URL: filters, time range, pagination, selected values, view state. Keep payload small (e.g. Chrome ~2k chars); no large datasets or sensitive data. + +**Steps:** + +1. Identify which Redux/Context fields are shareable or already reflected in the URL (e.g. `currentPage`, `timeRange`, `selectedFilter`). +2. Add nuqs (or use existing): `useQueryState('param', parseAsString.withDefault('…'))` (or `parseAsInteger`, etc.). +3. Replace reads/writes of those fields with nuqs hooks. Use typed parsers; avoid ad-hoc `useSearchParams` encoding/decoding. +4. Remove the same fields from Redux/Context and their reducers/providers. + +**Before (Context/Redux):** + +```tsx +const { timeRange } = useContext(SomeContext) +const [page, setPage] = useDispatch(...) +``` + +**After (nuqs):** + +```tsx +const [timeRange, setTimeRange] = useQueryState('timeRange', parseAsString.withDefault('1h')) +const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1)) +``` + +## 4. Migrate to Zustand (global client state) + +**When:** State is global or cross-component client state: feature flags, dashboard state, query builder state, complex/large client objects (e.g. up to ~1.5–2MB). Not for server cache or local-only UI. + +**Steps:** + +1. Create one store per domain (e.g. `DashboardStore`, `QueryBuilderStore`). One `create()` per module; for large state use slice factories and combine. +2. Put state properties first, then actions. Use `set` (or `setState` / `getState()` + `set`) for updates; never mutate state directly. +3. Replace Context/Redux consumption with the store hook **and a selector** so only the used slice triggers re-renders. +4. Remove the old Context provider / Redux slice and related dispatches. + +**Selector (required):** + +```tsx +const isLocked = useDashboardStore(state => state.isDashboardLocked) +``` + +Never use `useStore()` with no selector. Never do `state.foo = x` inside actions; use `set(state => ({ ... }))`. + +**Before (Context/Redux):** + +```tsx +const { isDashboardLocked, setLocked } = useContext(DashboardContext) +``` + +**After (Zustand):** + +```tsx +const isLocked = useDashboardStore(state => state.isDashboardLocked) +const setLocked = useDashboardStore(state => state.setLocked) +``` + +For large stores (many top-level fields), split into slices and combine: + +```tsx +const createBearSlice = set => ({ bears: 0, addBear: () => set(s => ({ bears: s.bears + 1 })) }) +const useStore = create(set => ({ ...createBearSlice(set), ...createFishSlice(set) })) +``` + +Add `eslint-plugin-zustand-rules` with `plugin:zustand-rules/recommended` to enforce selectors and no direct mutation. + +## 5. Migrate to local state (useState / useReducer) + +**When:** State is used only inside one component or a small subtree (form inputs, toggles, hover, panel selection). No URL sync, no cross-feature sharing. + +**Steps:** + +1. Move the state into the component that owns it (or the smallest common parent). +2. Use `useState` or `useReducer` (useReducer when multiple related fields change together). +3. Remove from Redux/Context and any provider/slice. + +Do not use Zustand, Redux, or Context for purely local UI state. + +## 6. Migration checklist + +- [ ] Classify each piece of state (server / URL / global client / local). +- [ ] Server state: move to React Query; expose via hook; remove Redux/Context mirroring. +- [ ] URL state: move to nuqs; remove from Redux/Context; keep URL payload small. +- [ ] Global client state: move to Zustand with selectors and immutable updates; one store per domain. +- [ ] Local state: move to useState/useReducer in the owning component. +- [ ] Remove old Redux slices / Context providers and all dispatches/consumers for migrated state. +- [ ] Do not duplicate the same data in multiple places (e.g. React Query + Redux). + +## Additional resources + +- Project rule: [.cursor/rules/state-management.mdc](../../rules/state-management.mdc) +- Detailed patterns and rationale: [reference.md](reference.md) diff --git a/frontend/.cursor/skills/migrate-state-management/reference.md b/frontend/.cursor/skills/migrate-state-management/reference.md new file mode 100644 index 00000000000..0ca9687f0a2 --- /dev/null +++ b/frontend/.cursor/skills/migrate-state-management/reference.md @@ -0,0 +1,50 @@ +# State migration reference + +## Why migrate + +- **Context:** Re-renders all consumers on any change; no granular subscriptions; becomes brittle at scale. +- **Redux:** Heavy boilerplate (actions, reducers, selectors, Provider); slower onboarding; often used to mirror React Query or URL state. +- **Goal:** Fewer mechanisms, domain isolation, granular subscriptions, single source of truth per state type. + +## React Query migration (server state) + +Typical anti-pattern: API is called via React Query, then result is dispatched to Redux. Flow becomes: Component → useQueries → API → dispatch → Reducer → Redux state → useSelector. + +Correct flow: Component → useQuery (or custom hook wrapping it) → same component reads from hook. No Redux/Context in between. + +- Prefer generated hooks from `frontend/src/api/generated`. +- For “app state” that is just API data (versions, configs), one hook that returns `{ ...data, configs: useMemo(...) }` is enough. No selectors needed for plain data; useMemo only where the value is used as dependency (e.g. in useState). +- Set `staleTime` / `refetchOnMount` etc. so refetch behavior matches previous expectations. + +## nuqs migration (URL state) + +Redux/Context often hold pagination, filters, time range, selected values that are shareable. Those belong in the URL. + +- Use [nuqs](https://nuqs.dev/docs/basic-usage) for typed search params. Avoid ad-hoc `useSearchParams` + manual encoding. +- Browser limits: Chrome ~2k chars practical; keep payload small; no large datasets or secrets in query params. +- If the app uses TanStack Router, search params can be handled there; otherwise nuqs is the standard. + +## Zustand migration (client state) + +- One store per domain (e.g. DashboardStore, QueryBuilderStore). Multiple `create()` in one file is disallowed; use one store or composed slices. +- Always use a selector: `useStore(s => s.field)` so only that field drives re-renders. +- Never mutate: update only via `set(state => ({ ... }))` or `setState` / `getState()` + `set`. +- State properties first, then actions. For 5–10+ top-level fields, split into slice factories and combine with one `create()`. +- Large client objects: Zustand is for “large” in the ~1.5–2MB range; above that, optimize at API/store design. +- Testing: no Provider; stores are plain functions; easy to reset and mock. + +## What not to use + +- **Redux / Context** for new or migrated shared/global state. +- **Redux / Context** to store or mirror React Query results. +- **Redux / Context** for state that should live in the URL (use nuqs). +- **Zustand / Redux / Context** for component-local UI (use useState/useReducer). + +## Summary table + +| State type | Use | Avoid | +|-------------|--------------------|-----------------| +| Server/API | React Query | Redux, Context | +| URL/shareable | nuqs | Redux, Context | +| Global client | Zustand | Redux, Context | +| Local UI | useState/useReducer | Zustand, Redux, Context | diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs index 2c8d0fab18c..bc14de049fd 100644 --- a/frontend/.eslintrc.cjs +++ b/frontend/.eslintrc.cjs @@ -205,6 +205,25 @@ module.exports = { ], }, overrides: [ + { + files: ['src/**/*.{jsx,tsx,ts}'], + excludedFiles: [ + '**/*.test.{js,jsx,ts,tsx}', + '**/*.spec.{js,jsx,ts,tsx}', + '**/__tests__/**/*.{js,jsx,ts,tsx}', + ], + rules: { + 'no-restricted-properties': [ + 'error', + { + object: 'navigator', + property: 'clipboard', + message: + 'Do not use navigator.clipboard directly since it does not work well with specific browsers. Use hook useCopyToClipboard from react-use library. https://streamich.github.io/react-use/?path=/story/side-effects-usecopytoclipboard--docs', + }, + ], + }, + }, { files: [ '**/*.test.{js,jsx,ts,tsx}', diff --git a/frontend/README.md b/frontend/README.md index ff6edd44884..1ac304587bf 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -12,7 +12,7 @@ or `docker build . -t tagname` -**Tag to remote url- Introduce versinoing later on** +**Tag to remote url- Introduce versioning later on** ``` docker tag signoz/frontend:latest 7296823551/signoz:latest diff --git a/frontend/__mocks__/useSafeNavigate.ts b/frontend/__mocks__/useSafeNavigate.ts index a1044da052c..5e1e62878dc 100644 --- a/frontend/__mocks__/useSafeNavigate.ts +++ b/frontend/__mocks__/useSafeNavigate.ts @@ -2,6 +2,7 @@ interface SafeNavigateOptions { replace?: boolean; state?: unknown; + newTab?: boolean; } interface SafeNavigateTo { @@ -20,9 +21,7 @@ interface UseSafeNavigateReturn { export const useSafeNavigate = (): UseSafeNavigateReturn => ({ safeNavigate: jest.fn( - (to: SafeNavigateToType, options?: SafeNavigateOptions) => { - console.log(`Mock safeNavigate called with:`, to, options); - }, + (_to: SafeNavigateToType, _options?: SafeNavigateOptions) => {}, ) as jest.MockedFunction< (to: SafeNavigateToType, options?: SafeNavigateOptions) => void >, diff --git a/frontend/package.json b/frontend/package.json index 10a561bd280..dbf21494bcb 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -68,8 +68,8 @@ "@signozhq/toggle-group": "0.0.1", "@signozhq/tooltip": "0.0.2", "@signozhq/ui": "0.0.5", - "@tanstack/react-table": "8.20.6", - "@tanstack/react-virtual": "3.11.2", + "@tanstack/react-table": "8.21.3", + "@tanstack/react-virtual": "3.13.22", "@uiw/codemirror-theme-copilot": "4.23.11", "@uiw/codemirror-theme-github": "4.24.1", "@uiw/react-codemirror": "4.23.10", @@ -164,6 +164,7 @@ "vite-plugin-html": "3.2.2", "web-vitals": "^0.2.4", "xstate": "^4.31.0", + "zod": "4.3.6", "zustand": "5.0.11" }, "browserslist": { @@ -286,4 +287,4 @@ "tmp": "0.2.4", "vite": "npm:rolldown-vite@7.3.1" } -} \ No newline at end of file +} diff --git a/frontend/public/Logos/mistral.svg b/frontend/public/Logos/mistral.svg new file mode 100644 index 00000000000..2c725f52c5b --- /dev/null +++ b/frontend/public/Logos/mistral.svg @@ -0,0 +1 @@ +Mistral AI \ No newline at end of file diff --git a/frontend/public/Logos/openclaw.svg b/frontend/public/Logos/openclaw.svg new file mode 100644 index 00000000000..0212dd0ba98 --- /dev/null +++ b/frontend/public/Logos/openclaw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/Logos/render.svg b/frontend/public/Logos/render.svg new file mode 100644 index 00000000000..993bd0a253a --- /dev/null +++ b/frontend/public/Logos/render.svg @@ -0,0 +1 @@ +Render \ No newline at end of file diff --git a/frontend/src/AppRoutes/Private.tsx b/frontend/src/AppRoutes/Private.tsx index cbcca1f6491..0e9c2dc9348 100644 --- a/frontend/src/AppRoutes/Private.tsx +++ b/frontend/src/AppRoutes/Private.tsx @@ -101,6 +101,22 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element { preference.name === ORG_PREFERENCES.ORG_ONBOARDING, )?.value; + // Don't redirect to onboarding if workspace has issues (blocked, suspended, or restricted) + // User needs access to settings/billing to fix payment issues + const isWorkspaceBlocked = trialInfo?.workSpaceBlock; + const isWorkspaceSuspended = activeLicense?.state === LicenseState.DEFAULTED; + const isWorkspaceAccessRestricted = + activeLicense?.state === LicenseState.TERMINATED || + activeLicense?.state === LicenseState.EXPIRED || + activeLicense?.state === LicenseState.CANCELLED; + + const hasWorkspaceIssue = + isWorkspaceBlocked || isWorkspaceSuspended || isWorkspaceAccessRestricted; + + if (hasWorkspaceIssue) { + return; + } + const isFirstUser = checkFirstTimeUser(); if ( isFirstUser && @@ -119,40 +135,36 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element { orgPreferences, usersData, pathname, + trialInfo?.workSpaceBlock, + activeLicense?.state, ]); - const navigateToWorkSpaceBlocked = (route: any): void => { - const { path } = route; - + const navigateToWorkSpaceBlocked = useCallback((): void => { const isRouteEnabledForWorkspaceBlockedState = isAdmin && - (path === ROUTES.SETTINGS || - path === ROUTES.ORG_SETTINGS || - path === ROUTES.MEMBERS_SETTINGS || - path === ROUTES.BILLING || - path === ROUTES.MY_SETTINGS); + (pathname === ROUTES.SETTINGS || + pathname === ROUTES.ORG_SETTINGS || + pathname === ROUTES.MEMBERS_SETTINGS || + pathname === ROUTES.BILLING || + pathname === ROUTES.MY_SETTINGS); if ( - path && - path !== ROUTES.WORKSPACE_LOCKED && + pathname && + pathname !== ROUTES.WORKSPACE_LOCKED && !isRouteEnabledForWorkspaceBlockedState ) { history.push(ROUTES.WORKSPACE_LOCKED); } - }; + }, [isAdmin, pathname]); - const navigateToWorkSpaceAccessRestricted = (route: any): void => { - const { path } = route; - - if (path && path !== ROUTES.WORKSPACE_ACCESS_RESTRICTED) { + const navigateToWorkSpaceAccessRestricted = useCallback((): void => { + if (pathname && pathname !== ROUTES.WORKSPACE_ACCESS_RESTRICTED) { history.push(ROUTES.WORKSPACE_ACCESS_RESTRICTED); } - }; + }, [pathname]); useEffect(() => { if (!isFetchingActiveLicense && activeLicense) { - const currentRoute = mapRoutes.get('current'); - const isTerminated = activeLicense.state === LicenseState.TERMINATED; const isExpired = activeLicense.state === LicenseState.EXPIRED; const isCancelled = activeLicense.state === LicenseState.CANCELLED; @@ -161,61 +173,53 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element { const { platform } = activeLicense; - if ( - isWorkspaceAccessRestricted && - platform === LicensePlatform.CLOUD && - currentRoute - ) { - navigateToWorkSpaceAccessRestricted(currentRoute); + if (isWorkspaceAccessRestricted && platform === LicensePlatform.CLOUD) { + navigateToWorkSpaceAccessRestricted(); } } - }, [isFetchingActiveLicense, activeLicense, mapRoutes, pathname]); + }, [ + isFetchingActiveLicense, + activeLicense, + navigateToWorkSpaceAccessRestricted, + ]); useEffect(() => { if (!isFetchingActiveLicense) { - const currentRoute = mapRoutes.get('current'); const shouldBlockWorkspace = trialInfo?.workSpaceBlock; if ( shouldBlockWorkspace && - currentRoute && activeLicense?.platform === LicensePlatform.CLOUD ) { - navigateToWorkSpaceBlocked(currentRoute); + navigateToWorkSpaceBlocked(); } } - // eslint-disable-next-line react-hooks/exhaustive-deps }, [ isFetchingActiveLicense, trialInfo?.workSpaceBlock, activeLicense?.platform, - mapRoutes, - pathname, + navigateToWorkSpaceBlocked, ]); - const navigateToWorkSpaceSuspended = (route: any): void => { - const { path } = route; - - if (path && path !== ROUTES.WORKSPACE_SUSPENDED) { + const navigateToWorkSpaceSuspended = useCallback((): void => { + if (pathname && pathname !== ROUTES.WORKSPACE_SUSPENDED) { history.push(ROUTES.WORKSPACE_SUSPENDED); } - }; + }, [pathname]); useEffect(() => { if (!isFetchingActiveLicense && activeLicense) { - const currentRoute = mapRoutes.get('current'); const shouldSuspendWorkspace = activeLicense.state === LicenseState.DEFAULTED; if ( shouldSuspendWorkspace && - currentRoute && activeLicense.platform === LicensePlatform.CLOUD ) { - navigateToWorkSpaceSuspended(currentRoute); + navigateToWorkSpaceSuspended(); } } - }, [isFetchingActiveLicense, activeLicense, mapRoutes, pathname]); + }, [isFetchingActiveLicense, activeLicense, navigateToWorkSpaceSuspended]); useEffect(() => { if (org && org.length > 0 && org[0].id !== undefined) { diff --git a/frontend/src/AppRoutes/__tests__/Private.test.tsx b/frontend/src/AppRoutes/__tests__/Private.test.tsx new file mode 100644 index 00000000000..9ec68b3fbba --- /dev/null +++ b/frontend/src/AppRoutes/__tests__/Private.test.tsx @@ -0,0 +1,1539 @@ +import { ReactElement } from 'react'; +import { QueryClient, QueryClientProvider } from 'react-query'; +import { MemoryRouter, Route, Switch, useLocation } from 'react-router-dom'; +import { act, render, screen, waitFor } from '@testing-library/react'; +import { FeatureKeys } from 'constants/features'; +import { LOCALSTORAGE } from 'constants/localStorage'; +import { ORG_PREFERENCES } from 'constants/orgPreferences'; +import ROUTES from 'constants/routes'; +import history from 'lib/history'; +import { AppContext } from 'providers/App/App'; +import { IAppContext, IUser } from 'providers/App/types'; +import { + LicenseEvent, + LicensePlatform, + LicenseResModel, + LicenseState, + LicenseStatus, + TrialInfo, +} from 'types/api/licensesV3/getActive'; +import { OrgPreference } from 'types/api/preferences/preference'; +import { ROLES, USER_ROLES } from 'types/roles'; + +import PrivateRoute from '../Private'; + +// Mock history module +jest.mock('lib/history', () => ({ + __esModule: true, + default: { + push: jest.fn(), + location: { pathname: '/', search: '', hash: '' }, + listen: jest.fn(), + createHref: jest.fn(), + }, +})); + +const mockHistoryPush = history.push as jest.Mock; + +// Mock localStorage APIs +const mockLocalStorage: Record = {}; +jest.mock('api/browser/localstorage/get', () => ({ + __esModule: true, + default: (key: string): string | null => mockLocalStorage[key] || null, +})); + +jest.mock('api/browser/localstorage/set', () => ({ + __esModule: true, + default: (key: string, value: string): void => { + mockLocalStorage[key] = value; + }, +})); + +// Mock useGetTenantLicense hook +let mockIsCloudUser = true; +jest.mock('hooks/useGetTenantLicense', () => ({ + useGetTenantLicense: (): { + isCloudUser: boolean; + isEnterpriseSelfHostedUser: boolean; + isCommunityUser: boolean; + isCommunityEnterpriseUser: boolean; + } => ({ + isCloudUser: mockIsCloudUser, + isEnterpriseSelfHostedUser: !mockIsCloudUser, + isCommunityUser: false, + isCommunityEnterpriseUser: false, + }), +})); + +// Mock react-query for users fetch +let mockUsersData: { email: string }[] = []; +jest.mock('api/v1/user/get', () => ({ + __esModule: true, + default: jest.fn(() => Promise.resolve({ data: mockUsersData })), +})); + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + retry: false, + }, + }, +}); + +// Component to capture current location for assertions +function LocationDisplay(): ReactElement { + const location = useLocation(); + return
{location.pathname}
; +} + +// Helper to create mock user +function createMockUser(overrides: Partial = {}): IUser { + return { + accessJwt: 'test-token', + refreshJwt: 'test-refresh-token', + id: 'user-id', + email: 'test@signoz.io', + displayName: 'Test User', + createdAt: 1732544623, + organization: 'Test Org', + orgId: 'org-id', + role: USER_ROLES.ADMIN as ROLES, + ...overrides, + }; +} + +// Helper to create mock license +function createMockLicense( + overrides: Partial = {}, +): LicenseResModel { + return { + key: 'test-key', + event_queue: { + created_at: '0', + event: LicenseEvent.NO_EVENT, + scheduled_at: '0', + status: '', + updated_at: '0', + }, + state: LicenseState.ACTIVE, + status: LicenseStatus.VALID, + platform: LicensePlatform.CLOUD, + created_at: '0', + plan: { + created_at: '0', + description: '', + is_active: true, + name: '', + updated_at: '0', + }, + plan_id: '0', + free_until: '0', + updated_at: '0', + valid_from: 0, + valid_until: 0, + ...overrides, + }; +} + +// Helper to create mock trial info +function createMockTrialInfo(overrides: Partial = {}): TrialInfo { + return { + trialStart: -1, + trialEnd: -1, + onTrial: false, + workSpaceBlock: false, + trialConvertedToSubscription: false, + gracePeriodEnd: -1, + ...overrides, + }; +} + +// Helper to create mock org preferences +function createMockOrgPreferences(onboardingComplete = true): OrgPreference[] { + return [ + { + name: ORG_PREFERENCES.ORG_ONBOARDING, + description: 'Organisation Onboarding', + valueType: 'boolean', + defaultValue: false, + allowedValues: ['true', 'false'], + allowedScopes: ['org'], + value: onboardingComplete, + }, + ]; +} + +// Helper to create mock app context +function createMockAppContext( + overrides: Partial = {}, +): IAppContext { + return { + user: createMockUser(), + activeLicense: createMockLicense(), + trialInfo: createMockTrialInfo(), + featureFlags: [], + orgPreferences: createMockOrgPreferences(), + userPreferences: [], + isLoggedIn: true, + org: [{ createdAt: 0, id: 'org-id', displayName: 'Test Org' }], + isFetchingUser: false, + isFetchingActiveLicense: false, + isFetchingFeatureFlags: false, + isFetchingOrgPreferences: false, + userFetchError: null, + activeLicenseFetchError: null, + featureFlagsFetchError: null, + orgPreferencesFetchError: null, + changelog: null, + showChangelogModal: false, + activeLicenseRefetch: jest.fn(), + updateUser: jest.fn(), + updateOrgPreferences: jest.fn(), + updateUserPreferenceInContext: jest.fn(), + updateOrg: jest.fn(), + updateChangelog: jest.fn(), + toggleChangelogModal: jest.fn(), + versionData: { version: '1.0.0', ee: 'Y', setupCompleted: true }, + hasEditPermission: true, + ...overrides, + }; +} + +interface RenderPrivateRouteOptions { + initialRoute?: string; + appContext?: Partial; + isCloudUser?: boolean; +} + +function renderPrivateRoute(options: RenderPrivateRouteOptions = {}): void { + const { + initialRoute = ROUTES.HOME, + appContext = {}, + isCloudUser = true, + } = options; + + mockIsCloudUser = isCloudUser; + + const contextValue = createMockAppContext(appContext); + + render( + + + + + + +
Content
+ +
+
+
+
+
+
, + ); +} + +// Generic assertion helpers for navigation behavior +// Using these allows easier refactoring when switching from history.push to Redirect component + +async function assertRedirectsTo(targetRoute: string): Promise { + await waitFor(() => { + expect(mockHistoryPush).toHaveBeenCalledWith(targetRoute); + }); +} + +function assertNoRedirect(): void { + expect(mockHistoryPush).not.toHaveBeenCalled(); +} + +function assertDoesNotRedirectTo(targetRoute: string): void { + expect(mockHistoryPush).not.toHaveBeenCalledWith(targetRoute); +} + +function assertRendersChildren(): void { + expect(screen.getByTestId('children-rendered')).toBeInTheDocument(); +} + +describe('PrivateRoute', () => { + beforeEach(() => { + jest.clearAllMocks(); + queryClient.clear(); + Object.keys(mockLocalStorage).forEach((key) => delete mockLocalStorage[key]); + mockIsCloudUser = true; + mockUsersData = []; + }); + + describe('Old Routes Handling', () => { + it('should redirect /pipelines to /logs/pipelines preserving search params', () => { + renderPrivateRoute({ initialRoute: '/pipelines?foo=bar' }); + + expect(screen.getByTestId('location-display')).toHaveTextContent( + '/logs/pipelines', + ); + }); + + it('should redirect /logs-explorer to /logs/logs-explorer', () => { + renderPrivateRoute({ initialRoute: '/logs-explorer' }); + + expect(screen.getByTestId('location-display')).toHaveTextContent( + '/logs/logs-explorer', + ); + }); + + it('should redirect /logs-explorer/live to /logs/logs-explorer/live', () => { + renderPrivateRoute({ initialRoute: '/logs-explorer/live' }); + + expect(screen.getByTestId('location-display')).toHaveTextContent( + '/logs/logs-explorer/live', + ); + }); + + it('should redirect /logs-save-views to /logs/saved-views', () => { + renderPrivateRoute({ initialRoute: '/logs-save-views' }); + + expect(screen.getByTestId('location-display')).toHaveTextContent( + '/logs/saved-views', + ); + }); + + it('should redirect /traces-save-views to /traces/saved-views', () => { + renderPrivateRoute({ initialRoute: '/traces-save-views' }); + + expect(screen.getByTestId('location-display')).toHaveTextContent( + '/traces/saved-views', + ); + }); + + it('should redirect /settings/access-tokens to /settings/service-accounts', () => { + renderPrivateRoute({ initialRoute: '/settings/access-tokens' }); + + expect(screen.getByTestId('location-display')).toHaveTextContent( + '/settings/service-accounts', + ); + }); + + it('should redirect /settings/api-keys to /settings/service-accounts', () => { + renderPrivateRoute({ initialRoute: '/settings/api-keys' }); + + expect(screen.getByTestId('location-display')).toHaveTextContent( + '/settings/service-accounts', + ); + }); + + it('should redirect /messaging-queues to /messaging-queues/overview', () => { + renderPrivateRoute({ initialRoute: '/messaging-queues' }); + + expect(screen.getByTestId('location-display')).toHaveTextContent( + '/messaging-queues/overview', + ); + }); + + it('should redirect /alerts/edit to /alerts/overview', () => { + renderPrivateRoute({ initialRoute: '/alerts/edit' }); + + expect(screen.getByTestId('location-display')).toHaveTextContent( + '/alerts/overview', + ); + }); + }); + + describe('Public Dashboard Route', () => { + it('should render children for public dashboard route without redirecting when not logged in', () => { + renderPrivateRoute({ + initialRoute: '/public/dashboard/abc123', + appContext: { isLoggedIn: false }, + }); + + assertRendersChildren(); + assertNoRedirect(); + }); + + it('should render children for public dashboard route when logged in without redirecting', () => { + renderPrivateRoute({ + initialRoute: '/public/dashboard/abc123', + appContext: { isLoggedIn: true }, + }); + + assertRendersChildren(); + // Critical: without the isPublicDashboard early return, logged-in users + // would be redirected to HOME due to the non-private route handling + assertNoRedirect(); + }); + }); + + describe('Private Routes - User Not Logged In', () => { + it('should redirect to login when accessing private route without authentication', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { isLoggedIn: false }, + }); + + await assertRedirectsTo(ROUTES.LOGIN); + }); + + it('should save current pathname to localStorage before redirecting to login', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.ALL_DASHBOARD, + appContext: { isLoggedIn: false }, + }); + + await waitFor(() => { + expect(mockLocalStorage[LOCALSTORAGE.UNAUTHENTICATED_ROUTE_HIT]).toBe( + ROUTES.ALL_DASHBOARD, + ); + }); + await assertRedirectsTo(ROUTES.LOGIN); + }); + + it('should redirect to login for /services route when not logged in', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.APPLICATION, + appContext: { isLoggedIn: false }, + }); + + await assertRedirectsTo(ROUTES.LOGIN); + }); + + it('should redirect to login for /alerts route when not logged in', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.LIST_ALL_ALERT, + appContext: { isLoggedIn: false }, + }); + + await assertRedirectsTo(ROUTES.LOGIN); + }); + }); + + describe('Private Routes - User Logged In', () => { + it('should render children when logged in user has correct permissions for route', () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + }); + + assertRendersChildren(); + assertNoRedirect(); + }); + + it('should redirect to unauthorized when VIEWER tries to access admin-only route /alerts/new', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.ALERTS_NEW, + appContext: { + isLoggedIn: true, + user: createMockUser({ role: USER_ROLES.VIEWER as ROLES }), + }, + }); + + await assertRedirectsTo(ROUTES.UN_AUTHORIZED); + }); + + it('should not redirect VIEWER from /settings/org-settings since it matches SETTINGS route which allows all roles', () => { + // Note: ORG_SETTINGS is not a standalone route in routes.ts + // It's handled by the SETTINGS route (exact: false) which allows all roles + // The actual permission check happens inside SettingsPage component + renderPrivateRoute({ + initialRoute: ROUTES.ORG_SETTINGS, + appContext: { + isLoggedIn: true, + user: createMockUser({ role: USER_ROLES.VIEWER as ROLES }), + }, + }); + + // VIEWER can access since SETTINGS route allows all roles + assertRendersChildren(); + }); + + it('should allow ADMIN to access /settings/org-settings', () => { + renderPrivateRoute({ + initialRoute: ROUTES.ORG_SETTINGS, + appContext: { + isLoggedIn: true, + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + }); + + assertRendersChildren(); + }); + + it('should allow EDITOR to access /alerts/new route', () => { + renderPrivateRoute({ + initialRoute: ROUTES.ALERTS_NEW, + appContext: { + isLoggedIn: true, + user: createMockUser({ role: USER_ROLES.EDITOR as ROLES }), + }, + }); + + assertRendersChildren(); + }); + + it('should allow VIEWER to access /dashboard route', () => { + renderPrivateRoute({ + initialRoute: '/dashboard/test-id', + appContext: { + isLoggedIn: true, + user: createMockUser({ role: USER_ROLES.VIEWER as ROLES }), + }, + }); + + assertRendersChildren(); + }); + }); + + describe('Non-Private Routes - User Logged In', () => { + it('should redirect to saved route from localStorage when logging in', async () => { + mockLocalStorage[LOCALSTORAGE.UNAUTHENTICATED_ROUTE_HIT] = + ROUTES.ALL_DASHBOARD; + + renderPrivateRoute({ + initialRoute: ROUTES.LOGIN, + appContext: { isLoggedIn: true }, + }); + + await assertRedirectsTo(ROUTES.ALL_DASHBOARD); + }); + + it('should clear localStorage after redirecting to saved route', async () => { + mockLocalStorage[LOCALSTORAGE.UNAUTHENTICATED_ROUTE_HIT] = ROUTES.HOME; + + renderPrivateRoute({ + initialRoute: ROUTES.LOGIN, + appContext: { isLoggedIn: true }, + }); + + await waitFor(() => { + expect(mockLocalStorage[LOCALSTORAGE.UNAUTHENTICATED_ROUTE_HIT]).toBe(''); + }); + }); + + it('should redirect to home when logged in user visits login page with no saved route', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.LOGIN, + appContext: { isLoggedIn: true }, + }); + + await assertRedirectsTo(ROUTES.HOME); + }); + + it('should not redirect when on /something-went-wrong page even when logged in', () => { + renderPrivateRoute({ + initialRoute: ROUTES.SOMETHING_WENT_WRONG, + appContext: { isLoggedIn: true }, + }); + + assertDoesNotRedirectTo(ROUTES.HOME); + }); + }); + + describe('Non-Private Routes - User Not Logged In', () => { + it('should not redirect when not logged in user visits login page', () => { + renderPrivateRoute({ + initialRoute: ROUTES.LOGIN, + appContext: { isLoggedIn: false }, + }); + + // Should not redirect - login page handles its own routing + assertNoRedirect(); + }); + + it('should not redirect when not logged in user visits signup page', () => { + renderPrivateRoute({ + initialRoute: ROUTES.SIGN_UP, + appContext: { isLoggedIn: false }, + }); + + assertNoRedirect(); + }); + + it('should not redirect when not logged in user visits password reset page', () => { + renderPrivateRoute({ + initialRoute: ROUTES.PASSWORD_RESET, + appContext: { isLoggedIn: false }, + }); + + assertNoRedirect(); + }); + + it('should not redirect when not logged in user visits forgot password page', () => { + renderPrivateRoute({ + initialRoute: ROUTES.FORGOT_PASSWORD, + appContext: { isLoggedIn: false }, + }); + + assertNoRedirect(); + }); + }); + + describe('Unknown Routes', () => { + it('should redirect to home when logged in and visiting unknown route', async () => { + renderPrivateRoute({ + initialRoute: '/unknown-route-that-does-not-exist', + appContext: { isLoggedIn: true }, + }); + + await assertRedirectsTo(ROUTES.HOME); + }); + + it('should redirect to login when not logged in and visiting unknown route', async () => { + renderPrivateRoute({ + initialRoute: '/unknown-route-that-does-not-exist', + appContext: { isLoggedIn: false }, + }); + + await assertRedirectsTo(ROUTES.LOGIN); + }); + + it('should save unknown route to localStorage before redirecting to login', async () => { + const unknownRoute = '/some-unknown-page'; + + renderPrivateRoute({ + initialRoute: unknownRoute, + appContext: { isLoggedIn: false }, + }); + + await waitFor(() => { + expect(mockLocalStorage[LOCALSTORAGE.UNAUTHENTICATED_ROUTE_HIT]).toBe( + unknownRoute, + ); + }); + }); + + it('should redirect to saved route when logged in user visits unknown route with fromPathname in localStorage', async () => { + // This tests the branch where: + // - currentRoute is null (unknown route) + // - isLoggedInState is true + // - fromPathname exists in localStorage + // Expected: redirect to savedRoute and clear localStorage + const savedRoute = ROUTES.ALL_DASHBOARD; + mockLocalStorage[LOCALSTORAGE.UNAUTHENTICATED_ROUTE_HIT] = savedRoute; + + renderPrivateRoute({ + initialRoute: '/unknown-route-that-does-not-exist', + appContext: { isLoggedIn: true }, + }); + + await assertRedirectsTo(savedRoute); + await waitFor(() => { + expect(mockLocalStorage[LOCALSTORAGE.UNAUTHENTICATED_ROUTE_HIT]).toBe(''); + }); + }); + }); + + describe('Workspace Blocked - Trial Expired (Cloud Users)', () => { + it('should redirect to workspace locked when workSpaceBlock is true for cloud users', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ platform: LicensePlatform.CLOUD }), + trialInfo: createMockTrialInfo({ workSpaceBlock: true }), + }, + isCloudUser: true, + }); + + await assertRedirectsTo(ROUTES.WORKSPACE_LOCKED); + }); + + it('should allow ADMIN to access /settings when workspace is blocked', () => { + renderPrivateRoute({ + initialRoute: ROUTES.SETTINGS, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ platform: LicensePlatform.CLOUD }), + trialInfo: createMockTrialInfo({ workSpaceBlock: true }), + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + isCloudUser: true, + }); + + // Admin should be able to access settings even when workspace is blocked + assertDoesNotRedirectTo(ROUTES.WORKSPACE_LOCKED); + }); + + it('should allow ADMIN to access /settings/billing when workspace is blocked', () => { + renderPrivateRoute({ + initialRoute: ROUTES.BILLING, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ platform: LicensePlatform.CLOUD }), + trialInfo: createMockTrialInfo({ workSpaceBlock: true }), + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + isCloudUser: true, + }); + + assertDoesNotRedirectTo(ROUTES.WORKSPACE_LOCKED); + }); + + it('should allow ADMIN to access /settings/org-settings when workspace is blocked', () => { + renderPrivateRoute({ + initialRoute: ROUTES.ORG_SETTINGS, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ platform: LicensePlatform.CLOUD }), + trialInfo: createMockTrialInfo({ workSpaceBlock: true }), + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + isCloudUser: true, + }); + + assertDoesNotRedirectTo(ROUTES.WORKSPACE_LOCKED); + }); + + it('should allow ADMIN to access /settings/members when workspace is blocked', () => { + renderPrivateRoute({ + initialRoute: ROUTES.MEMBERS_SETTINGS, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ platform: LicensePlatform.CLOUD }), + trialInfo: createMockTrialInfo({ workSpaceBlock: true }), + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + isCloudUser: true, + }); + + assertDoesNotRedirectTo(ROUTES.WORKSPACE_LOCKED); + }); + + it('should allow ADMIN to access /settings/my-settings when workspace is blocked', () => { + renderPrivateRoute({ + initialRoute: ROUTES.MY_SETTINGS, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ platform: LicensePlatform.CLOUD }), + trialInfo: createMockTrialInfo({ workSpaceBlock: true }), + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + isCloudUser: true, + }); + + assertDoesNotRedirectTo(ROUTES.WORKSPACE_LOCKED); + }); + + it('should redirect VIEWER to workspace locked even when trying to access settings', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.SETTINGS, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ platform: LicensePlatform.CLOUD }), + trialInfo: createMockTrialInfo({ workSpaceBlock: true }), + user: createMockUser({ role: USER_ROLES.VIEWER as ROLES }), + }, + isCloudUser: true, + }); + + await assertRedirectsTo(ROUTES.WORKSPACE_LOCKED); + }); + + it('should redirect VIEWER to workspace locked when trying to access billing', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.BILLING, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ platform: LicensePlatform.CLOUD }), + trialInfo: createMockTrialInfo({ workSpaceBlock: true }), + user: createMockUser({ role: USER_ROLES.VIEWER as ROLES }), + }, + isCloudUser: true, + }); + + await assertRedirectsTo(ROUTES.WORKSPACE_LOCKED); + }); + + it('should redirect VIEWER to workspace locked when trying to access org-settings', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.ORG_SETTINGS, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ platform: LicensePlatform.CLOUD }), + trialInfo: createMockTrialInfo({ workSpaceBlock: true }), + user: createMockUser({ role: USER_ROLES.VIEWER as ROLES }), + }, + isCloudUser: true, + }); + + await assertRedirectsTo(ROUTES.WORKSPACE_LOCKED); + }); + + it('should redirect VIEWER to workspace locked when trying to access members settings', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.MEMBERS_SETTINGS, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ platform: LicensePlatform.CLOUD }), + trialInfo: createMockTrialInfo({ workSpaceBlock: true }), + user: createMockUser({ role: USER_ROLES.VIEWER as ROLES }), + }, + isCloudUser: true, + }); + + await assertRedirectsTo(ROUTES.WORKSPACE_LOCKED); + }); + + it('should redirect VIEWER to workspace locked when trying to access my-settings', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.MY_SETTINGS, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ platform: LicensePlatform.CLOUD }), + trialInfo: createMockTrialInfo({ workSpaceBlock: true }), + user: createMockUser({ role: USER_ROLES.VIEWER as ROLES }), + }, + isCloudUser: true, + }); + + await assertRedirectsTo(ROUTES.WORKSPACE_LOCKED); + }); + + it('should redirect EDITOR to workspace locked when trying to access settings', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.SETTINGS, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ platform: LicensePlatform.CLOUD }), + trialInfo: createMockTrialInfo({ workSpaceBlock: true }), + user: createMockUser({ role: USER_ROLES.EDITOR as ROLES }), + }, + isCloudUser: true, + }); + + await assertRedirectsTo(ROUTES.WORKSPACE_LOCKED); + }); + + it('should not redirect when already on workspace locked page', () => { + renderPrivateRoute({ + initialRoute: ROUTES.WORKSPACE_LOCKED, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ platform: LicensePlatform.CLOUD }), + trialInfo: createMockTrialInfo({ workSpaceBlock: true }), + }, + isCloudUser: true, + }); + + assertDoesNotRedirectTo(ROUTES.WORKSPACE_LOCKED); + }); + + it('should not redirect self-hosted users to workspace locked even when workSpaceBlock is true', () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ + platform: LicensePlatform.SELF_HOSTED, + }), + trialInfo: createMockTrialInfo({ workSpaceBlock: true }), + }, + isCloudUser: false, + }); + + assertDoesNotRedirectTo(ROUTES.WORKSPACE_LOCKED); + }); + }); + + describe('Workspace Access Restricted - License Terminated/Expired/Cancelled (Cloud Users)', () => { + it('should redirect to workspace access restricted when license is TERMINATED', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ + platform: LicensePlatform.CLOUD, + state: LicenseState.TERMINATED, + }), + }, + isCloudUser: true, + }); + + await assertRedirectsTo(ROUTES.WORKSPACE_ACCESS_RESTRICTED); + }); + + it('should redirect to workspace access restricted when license is EXPIRED', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ + platform: LicensePlatform.CLOUD, + state: LicenseState.EXPIRED, + }), + }, + isCloudUser: true, + }); + + await assertRedirectsTo(ROUTES.WORKSPACE_ACCESS_RESTRICTED); + }); + + it('should redirect to workspace access restricted when license is CANCELLED', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ + platform: LicensePlatform.CLOUD, + state: LicenseState.CANCELLED, + }), + }, + isCloudUser: true, + }); + + await assertRedirectsTo(ROUTES.WORKSPACE_ACCESS_RESTRICTED); + }); + + it('should not redirect when already on workspace access restricted page', () => { + renderPrivateRoute({ + initialRoute: ROUTES.WORKSPACE_ACCESS_RESTRICTED, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ + platform: LicensePlatform.CLOUD, + state: LicenseState.TERMINATED, + }), + }, + isCloudUser: true, + }); + + assertDoesNotRedirectTo(ROUTES.WORKSPACE_ACCESS_RESTRICTED); + }); + + it('should not redirect self-hosted users to workspace access restricted when license is terminated', () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ + platform: LicensePlatform.SELF_HOSTED, + state: LicenseState.TERMINATED, + }), + }, + isCloudUser: false, + }); + + assertDoesNotRedirectTo(ROUTES.WORKSPACE_ACCESS_RESTRICTED); + }); + + it('should not redirect when license is ACTIVE', () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ + platform: LicensePlatform.CLOUD, + state: LicenseState.ACTIVE, + }), + }, + isCloudUser: true, + }); + + assertDoesNotRedirectTo(ROUTES.WORKSPACE_ACCESS_RESTRICTED); + }); + + it('should not redirect when license is EVALUATING', () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ + platform: LicensePlatform.CLOUD, + state: LicenseState.EVALUATING, + }), + }, + isCloudUser: true, + }); + + assertDoesNotRedirectTo(ROUTES.WORKSPACE_ACCESS_RESTRICTED); + }); + }); + + describe('Workspace Suspended - License Defaulted (Cloud Users)', () => { + it('should redirect to workspace suspended when license is DEFAULTED', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ + platform: LicensePlatform.CLOUD, + state: LicenseState.DEFAULTED, + }), + }, + isCloudUser: true, + }); + + await assertRedirectsTo(ROUTES.WORKSPACE_SUSPENDED); + }); + + it('should not redirect when already on workspace suspended page', () => { + renderPrivateRoute({ + initialRoute: ROUTES.WORKSPACE_SUSPENDED, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ + platform: LicensePlatform.CLOUD, + state: LicenseState.DEFAULTED, + }), + }, + isCloudUser: true, + }); + + assertDoesNotRedirectTo(ROUTES.WORKSPACE_SUSPENDED); + }); + + it('should not redirect self-hosted users to workspace suspended when license is defaulted', () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ + platform: LicensePlatform.SELF_HOSTED, + state: LicenseState.DEFAULTED, + }), + }, + isCloudUser: false, + }); + + assertDoesNotRedirectTo(ROUTES.WORKSPACE_SUSPENDED); + }); + }); + + describe('Onboarding Flow (Cloud Users)', () => { + it('should redirect to onboarding when first user has not completed onboarding', async () => { + // Set up exactly one user (not admin@signoz.cloud) to trigger first user check + mockUsersData = [{ email: 'test@example.com' }]; + + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingOrgPreferences: false, + orgPreferences: createMockOrgPreferences(false), // Onboarding NOT complete + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + isCloudUser: true, + }); + + await assertRedirectsTo(ROUTES.ONBOARDING); + }); + + it('should not redirect to onboarding when org preferences are still fetching', () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingOrgPreferences: true, + orgPreferences: null, + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + isCloudUser: true, + }); + + assertDoesNotRedirectTo(ROUTES.ONBOARDING); + }); + + it('should not redirect to onboarding when onboarding is already complete', async () => { + // Set up first user condition - this ensures the ONLY reason we don't redirect + // is because isOnboardingComplete is true + mockUsersData = [{ email: 'test@example.com' }]; + + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingOrgPreferences: false, + orgPreferences: createMockOrgPreferences(true), // Onboarding complete + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + isCloudUser: true, + }); + + // Wait for async operations (useQuery, useEffect) to settle + await act(async () => { + await Promise.resolve(); + }); + + // Critical: if isOnboardingComplete check is broken (always false), + // this test would fail because all other conditions for redirect ARE met + assertDoesNotRedirectTo(ROUTES.ONBOARDING); + }); + + it('should not redirect to onboarding for non-cloud users', () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingOrgPreferences: false, + orgPreferences: createMockOrgPreferences(false), // Onboarding not complete + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + isCloudUser: false, + }); + + assertDoesNotRedirectTo(ROUTES.ONBOARDING); + }); + + it('should not redirect to onboarding when on /workspace-locked route', () => { + renderPrivateRoute({ + initialRoute: ROUTES.WORKSPACE_LOCKED, + appContext: { + isLoggedIn: true, + isFetchingOrgPreferences: false, + orgPreferences: createMockOrgPreferences(false), + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + isCloudUser: true, + }); + + assertDoesNotRedirectTo(ROUTES.ONBOARDING); + }); + + it('should not redirect to onboarding when on /workspace-suspended route', () => { + renderPrivateRoute({ + initialRoute: ROUTES.WORKSPACE_SUSPENDED, + appContext: { + isLoggedIn: true, + isFetchingOrgPreferences: false, + orgPreferences: createMockOrgPreferences(false), + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + isCloudUser: true, + }); + + assertDoesNotRedirectTo(ROUTES.ONBOARDING); + }); + + it('should not redirect to onboarding when workspace is blocked and accessing billing', async () => { + // This tests the scenario where admin tries to access billing to fix payment + // while workspace is blocked and onboarding is not complete + mockUsersData = [{ email: 'test@example.com' }]; + + renderPrivateRoute({ + initialRoute: ROUTES.BILLING, + appContext: { + isLoggedIn: true, + isFetchingOrgPreferences: false, + isFetchingActiveLicense: false, + orgPreferences: createMockOrgPreferences(false), // Onboarding NOT complete + activeLicense: createMockLicense({ platform: LicensePlatform.CLOUD }), + trialInfo: createMockTrialInfo({ workSpaceBlock: true }), + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + isCloudUser: true, + }); + + await act(async () => { + await Promise.resolve(); + }); + + // Should NOT redirect to onboarding - user needs to access billing to fix payment + assertDoesNotRedirectTo(ROUTES.ONBOARDING); + }); + + it('should not redirect to onboarding when workspace is blocked and accessing settings', async () => { + mockUsersData = [{ email: 'test@example.com' }]; + + renderPrivateRoute({ + initialRoute: ROUTES.SETTINGS, + appContext: { + isLoggedIn: true, + isFetchingOrgPreferences: false, + isFetchingActiveLicense: false, + orgPreferences: createMockOrgPreferences(false), + activeLicense: createMockLicense({ platform: LicensePlatform.CLOUD }), + trialInfo: createMockTrialInfo({ workSpaceBlock: true }), + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + isCloudUser: true, + }); + + await act(async () => { + await Promise.resolve(); + }); + + assertDoesNotRedirectTo(ROUTES.ONBOARDING); + }); + + it('should not redirect to onboarding when workspace is suspended (DEFAULTED)', async () => { + mockUsersData = [{ email: 'test@example.com' }]; + + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingOrgPreferences: false, + isFetchingActiveLicense: false, + orgPreferences: createMockOrgPreferences(false), // Onboarding NOT complete + activeLicense: createMockLicense({ + platform: LicensePlatform.CLOUD, + state: LicenseState.DEFAULTED, + }), + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + isCloudUser: true, + }); + + await act(async () => { + await Promise.resolve(); + }); + + // Should redirect to WORKSPACE_SUSPENDED, not ONBOARDING + assertDoesNotRedirectTo(ROUTES.ONBOARDING); + }); + + it('should not redirect to onboarding when workspace is access restricted (TERMINATED)', async () => { + mockUsersData = [{ email: 'test@example.com' }]; + + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingOrgPreferences: false, + isFetchingActiveLicense: false, + orgPreferences: createMockOrgPreferences(false), // Onboarding NOT complete + activeLicense: createMockLicense({ + platform: LicensePlatform.CLOUD, + state: LicenseState.TERMINATED, + }), + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + isCloudUser: true, + }); + + await act(async () => { + await Promise.resolve(); + }); + + // Should redirect to WORKSPACE_ACCESS_RESTRICTED, not ONBOARDING + assertDoesNotRedirectTo(ROUTES.ONBOARDING); + }); + + it('should not redirect to onboarding when workspace is access restricted (EXPIRED)', async () => { + mockUsersData = [{ email: 'test@example.com' }]; + + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingOrgPreferences: false, + isFetchingActiveLicense: false, + orgPreferences: createMockOrgPreferences(false), + activeLicense: createMockLicense({ + platform: LicensePlatform.CLOUD, + state: LicenseState.EXPIRED, + }), + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + isCloudUser: true, + }); + + await act(async () => { + await Promise.resolve(); + }); + + assertDoesNotRedirectTo(ROUTES.ONBOARDING); + }); + }); + + describe('Get Started Route Redirect', () => { + it('should redirect to GET_STARTED_WITH_CLOUD when on GET_STARTED and ONBOARDING_V3 feature flag is active', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.GET_STARTED, + appContext: { + isLoggedIn: true, + featureFlags: [ + { + name: FeatureKeys.ONBOARDING_V3, + active: true, + usage: 0, + usage_limit: -1, + route: '', + }, + ], + }, + }); + + await assertRedirectsTo(ROUTES.GET_STARTED_WITH_CLOUD); + }); + + it('should not redirect when on GET_STARTED and ONBOARDING_V3 feature flag is inactive', () => { + renderPrivateRoute({ + initialRoute: ROUTES.GET_STARTED, + appContext: { + isLoggedIn: true, + featureFlags: [ + { + name: FeatureKeys.ONBOARDING_V3, + active: false, + usage: 0, + usage_limit: -1, + route: '', + }, + ], + }, + }); + + assertDoesNotRedirectTo(ROUTES.GET_STARTED_WITH_CLOUD); + }); + + it('should not redirect when on GET_STARTED and ONBOARDING_V3 feature flag is not present', () => { + renderPrivateRoute({ + initialRoute: ROUTES.GET_STARTED, + appContext: { + isLoggedIn: true, + featureFlags: [], + }, + }); + + assertDoesNotRedirectTo(ROUTES.GET_STARTED_WITH_CLOUD); + }); + + it('should not redirect when on different route even if ONBOARDING_V3 is active', () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + featureFlags: [ + { + name: FeatureKeys.ONBOARDING_V3, + active: true, + usage: 0, + usage_limit: -1, + route: '', + }, + ], + }, + }); + + assertDoesNotRedirectTo(ROUTES.GET_STARTED_WITH_CLOUD); + }); + }); + + describe('Loading States', () => { + it('should not redirect while license is still being fetched', () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: true, + activeLicense: null, + trialInfo: createMockTrialInfo({ workSpaceBlock: true }), + }, + }); + + assertDoesNotRedirectTo(ROUTES.WORKSPACE_LOCKED); + }); + + it('should not fetch users when org data is not available', () => { + // This tests the queryFn branch where orgData is undefined + // so the users fetch returns undefined instead of calling getAll() + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + org: [], // Empty org array means orgData won't be set + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + }); + + assertRendersChildren(); + }); + + it('should not fetch users when org id is undefined', () => { + // This tests the queryFn branch where orgData exists but id is undefined + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + // @ts-expect-error - intentionally passing undefined id to test edge case + org: [{ createdAt: 0, id: undefined, displayName: 'Test Org' }], + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + }); + + assertRendersChildren(); + }); + + it('should not check workspace states when activeLicense is null', () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: null, + }, + }); + + assertDoesNotRedirectTo(ROUTES.WORKSPACE_LOCKED); + assertDoesNotRedirectTo(ROUTES.WORKSPACE_SUSPENDED); + assertDoesNotRedirectTo(ROUTES.WORKSPACE_ACCESS_RESTRICTED); + }); + }); + + describe('Role-based Route Access', () => { + it('should allow ADMIN to access /licenses route', () => { + renderPrivateRoute({ + initialRoute: ROUTES.LIST_LICENSES, + appContext: { + isLoggedIn: true, + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + }); + + assertRendersChildren(); + }); + + it('should redirect VIEWER away from /licenses route', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.LIST_LICENSES, + appContext: { + isLoggedIn: true, + user: createMockUser({ role: USER_ROLES.VIEWER as ROLES }), + }, + }); + + await assertRedirectsTo(ROUTES.UN_AUTHORIZED); + }); + + it('should redirect EDITOR away from /licenses route', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.LIST_LICENSES, + appContext: { + isLoggedIn: true, + user: createMockUser({ role: USER_ROLES.EDITOR as ROLES }), + }, + }); + + await assertRedirectsTo(ROUTES.UN_AUTHORIZED); + }); + + it('should allow all roles to access /services route', () => { + const roles = [USER_ROLES.ADMIN, USER_ROLES.EDITOR, USER_ROLES.VIEWER]; + + roles.forEach((role) => { + jest.clearAllMocks(); + + renderPrivateRoute({ + initialRoute: ROUTES.APPLICATION, + appContext: { + isLoggedIn: true, + user: createMockUser({ role: role as ROLES }), + }, + }); + + assertDoesNotRedirectTo(ROUTES.UN_AUTHORIZED); + }); + }); + + it('should redirect VIEWER from /onboarding route (admin only)', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.ONBOARDING, + appContext: { + isLoggedIn: true, + user: createMockUser({ role: USER_ROLES.VIEWER as ROLES }), + }, + }); + + await assertRedirectsTo(ROUTES.UN_AUTHORIZED); + }); + + it('should not redirect VIEWER from /settings/channels/new due to route matching order (ALL_CHANNELS matches last)', () => { + // Note: This tests the ACTUAL behavior of Private.tsx route matching + // CHANNELS_NEW has path '/settings/channels/new' with permission ['ADMIN'] + // ALL_CHANNELS has path '/settings/channels' with permission ['ADMIN', 'EDITOR', 'VIEWER'] + // Due to non-exact matching and array order, ALL_CHANNELS matches LAST for '/settings/channels/new' + // This is a known limitation - actual permission enforcement happens in the page component + renderPrivateRoute({ + initialRoute: ROUTES.CHANNELS_NEW, + appContext: { + isLoggedIn: true, + user: createMockUser({ role: USER_ROLES.VIEWER as ROLES }), + }, + }); + + assertRendersChildren(); + assertDoesNotRedirectTo(ROUTES.UN_AUTHORIZED); + }); + + it('should allow EDITOR to access /get-started route', () => { + renderPrivateRoute({ + initialRoute: ROUTES.GET_STARTED, + appContext: { + isLoggedIn: true, + user: createMockUser({ role: USER_ROLES.EDITOR as ROLES }), + }, + }); + + assertDoesNotRedirectTo(ROUTES.UN_AUTHORIZED); + }); + }); + + describe('Edge Cases', () => { + it('should handle route with query parameters correctly for private routes', async () => { + mockLocalStorage[LOCALSTORAGE.UNAUTHENTICATED_ROUTE_HIT] = ''; + + renderPrivateRoute({ + initialRoute: `${ROUTES.ALL_DASHBOARD}?tab=metrics`, + appContext: { isLoggedIn: false }, + }); + + await assertRedirectsTo(ROUTES.LOGIN); + }); + + it('should render children when all conditions pass', () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + isFetchingActiveLicense: false, + activeLicense: createMockLicense({ + platform: LicensePlatform.CLOUD, + state: LicenseState.ACTIVE, + }), + trialInfo: createMockTrialInfo({ workSpaceBlock: false }), + user: createMockUser({ role: USER_ROLES.ADMIN as ROLES }), + }, + }); + + assertRendersChildren(); + }); + + it('should handle ANONYMOUS role by redirecting from private routes', async () => { + renderPrivateRoute({ + initialRoute: ROUTES.HOME, + appContext: { + isLoggedIn: true, + user: createMockUser({ role: USER_ROLES.ANONYMOUS as ROLES }), + }, + }); + + await assertRedirectsTo(ROUTES.UN_AUTHORIZED); + }); + }); +}); diff --git a/frontend/src/AppRoutes/pageComponents.ts b/frontend/src/AppRoutes/pageComponents.ts index d03d09e3cba..c3026e4e7af 100644 --- a/frontend/src/AppRoutes/pageComponents.ts +++ b/frontend/src/AppRoutes/pageComponents.ts @@ -157,10 +157,6 @@ export const IngestionSettings = Loadable( () => import(/* webpackChunkName: "Ingestion Settings" */ 'pages/Settings'), ); -export const APIKeys = Loadable( - () => import(/* webpackChunkName: "All Settings" */ 'pages/Settings'), -); - export const MySettings = Loadable( () => import(/* webpackChunkName: "All MySettings" */ 'pages/Settings'), ); diff --git a/frontend/src/AppRoutes/routes.ts b/frontend/src/AppRoutes/routes.ts index da6f5aaafc9..18b3d0e1ac5 100644 --- a/frontend/src/AppRoutes/routes.ts +++ b/frontend/src/AppRoutes/routes.ts @@ -513,6 +513,7 @@ export const oldRoutes = [ '/logs-save-views', '/traces-save-views', '/settings/access-tokens', + '/settings/api-keys', '/messaging-queues', '/alerts/edit', ]; @@ -523,7 +524,8 @@ export const oldNewRoutesMapping: Record = { '/logs-explorer/live': '/logs/logs-explorer/live', '/logs-save-views': '/logs/saved-views', '/traces-save-views': '/traces/saved-views', - '/settings/access-tokens': '/settings/api-keys', + '/settings/access-tokens': '/settings/service-accounts', + '/settings/api-keys': '/settings/service-accounts', '/messaging-queues': '/messaging-queues/overview', '/alerts/edit': '/alerts/overview', }; diff --git a/frontend/src/api/dashboard/public/createPublicDashboard.ts b/frontend/src/api/dashboard/public/createPublicDashboard.ts index bb45d778857..66ef406c768 100644 --- a/frontend/src/api/dashboard/public/createPublicDashboard.ts +++ b/frontend/src/api/dashboard/public/createPublicDashboard.ts @@ -1,6 +1,7 @@ import axios from 'api'; import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; +import { DEFAULT_TIME_RANGE } from 'container/TopNav/DateTimeSelectionV2/constants'; import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { CreatePublicDashboardProps } from 'types/api/dashboard/public/create'; @@ -8,7 +9,7 @@ const createPublicDashboard = async ( props: CreatePublicDashboardProps, ): Promise> => { - const { dashboardId, timeRangeEnabled = false, defaultTimeRange = '30m' } = props; + const { dashboardId, timeRangeEnabled = false, defaultTimeRange = DEFAULT_TIME_RANGE } = props; try { const response = await axios.post( diff --git a/frontend/src/api/dashboard/public/updatePublicDashboard.ts b/frontend/src/api/dashboard/public/updatePublicDashboard.ts index 6daacf5118e..976cac55b0b 100644 --- a/frontend/src/api/dashboard/public/updatePublicDashboard.ts +++ b/frontend/src/api/dashboard/public/updatePublicDashboard.ts @@ -1,6 +1,7 @@ import axios from 'api'; import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; +import { DEFAULT_TIME_RANGE } from 'container/TopNav/DateTimeSelectionV2/constants'; import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { UpdatePublicDashboardProps } from 'types/api/dashboard/public/update'; @@ -8,7 +9,7 @@ const updatePublicDashboard = async ( props: UpdatePublicDashboardProps, ): Promise> => { - const { dashboardId, timeRangeEnabled = false, defaultTimeRange = '30m' } = props; + const { dashboardId, timeRangeEnabled = false, defaultTimeRange = DEFAULT_TIME_RANGE } = props; try { const response = await axios.put( diff --git a/frontend/src/api/generated/services/cloudintegration/index.ts b/frontend/src/api/generated/services/cloudintegration/index.ts new file mode 100644 index 00000000000..a0d1fd0b031 --- /dev/null +++ b/frontend/src/api/generated/services/cloudintegration/index.ts @@ -0,0 +1,1041 @@ +/** + * ! Do not edit manually + * * The file has been auto-generated using Orval for SigNoz + * * regenerate with 'yarn generate:api' + * SigNoz + */ +import type { + InvalidateOptions, + MutationFunction, + QueryClient, + QueryFunction, + QueryKey, + UseMutationOptions, + UseMutationResult, + UseQueryOptions, + UseQueryResult, +} from 'react-query'; +import { useMutation, useQuery } from 'react-query'; + +import type { BodyType, ErrorType } from '../../../generatedAPIInstance'; +import { GeneratedAPIInstance } from '../../../generatedAPIInstance'; +import type { + AgentCheckIn200, + AgentCheckInDeprecated200, + AgentCheckInDeprecatedPathParameters, + AgentCheckInPathParameters, + CloudintegrationtypesConnectionArtifactRequestDTO, + CloudintegrationtypesPostableAgentCheckInRequestDTO, + CloudintegrationtypesUpdatableAccountDTO, + CloudintegrationtypesUpdatableServiceDTO, + CreateAccount200, + CreateAccountPathParameters, + DisconnectAccountPathParameters, + GetAccount200, + GetAccountPathParameters, + GetService200, + GetServicePathParameters, + ListAccounts200, + ListAccountsPathParameters, + ListServicesMetadata200, + ListServicesMetadataPathParameters, + RenderErrorResponseDTO, + UpdateAccountPathParameters, + UpdateServicePathParameters, +} from '../sigNoz.schemas'; + +/** + * [Deprecated] This endpoint is called by the deployed agent to check in + * @deprecated + * @summary Agent check-in + */ +export const agentCheckInDeprecated = ( + { cloudProvider }: AgentCheckInDeprecatedPathParameters, + cloudintegrationtypesPostableAgentCheckInRequestDTO: BodyType, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v1/cloud-integrations/${cloudProvider}/agent-check-in`, + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + data: cloudintegrationtypesPostableAgentCheckInRequestDTO, + signal, + }); +}; + +export const getAgentCheckInDeprecatedMutationOptions = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { + pathParams: AgentCheckInDeprecatedPathParameters; + data: BodyType; + }, + TContext + >; +}): UseMutationOptions< + Awaited>, + TError, + { + pathParams: AgentCheckInDeprecatedPathParameters; + data: BodyType; + }, + TContext +> => { + const mutationKey = ['agentCheckInDeprecated']; + 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>, + { + pathParams: AgentCheckInDeprecatedPathParameters; + data: BodyType; + } + > = (props) => { + const { pathParams, data } = props ?? {}; + + return agentCheckInDeprecated(pathParams, data); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type AgentCheckInDeprecatedMutationResult = NonNullable< + Awaited> +>; +export type AgentCheckInDeprecatedMutationBody = BodyType; +export type AgentCheckInDeprecatedMutationError = ErrorType; + +/** + * @deprecated + * @summary Agent check-in + */ +export const useAgentCheckInDeprecated = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { + pathParams: AgentCheckInDeprecatedPathParameters; + data: BodyType; + }, + TContext + >; +}): UseMutationResult< + Awaited>, + TError, + { + pathParams: AgentCheckInDeprecatedPathParameters; + data: BodyType; + }, + TContext +> => { + const mutationOptions = getAgentCheckInDeprecatedMutationOptions(options); + + return useMutation(mutationOptions); +}; +/** + * This endpoint lists the accounts for the specified cloud provider + * @summary List accounts + */ +export const listAccounts = ( + { cloudProvider }: ListAccountsPathParameters, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v1/cloud_integrations/${cloudProvider}/accounts`, + method: 'GET', + signal, + }); +}; + +export const getListAccountsQueryKey = ({ + cloudProvider, +}: ListAccountsPathParameters) => { + return [`/api/v1/cloud_integrations/${cloudProvider}/accounts`] as const; +}; + +export const getListAccountsQueryOptions = < + TData = Awaited>, + TError = ErrorType +>( + { cloudProvider }: ListAccountsPathParameters, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getListAccountsQueryKey({ cloudProvider }); + + const queryFn: QueryFunction>> = ({ + signal, + }) => listAccounts({ cloudProvider }, signal); + + return { + queryKey, + queryFn, + enabled: !!cloudProvider, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; +}; + +export type ListAccountsQueryResult = NonNullable< + Awaited> +>; +export type ListAccountsQueryError = ErrorType; + +/** + * @summary List accounts + */ + +export function useListAccounts< + TData = Awaited>, + TError = ErrorType +>( + { cloudProvider }: ListAccountsPathParameters, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getListAccountsQueryOptions({ cloudProvider }, options); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary List accounts + */ +export const invalidateListAccounts = async ( + queryClient: QueryClient, + { cloudProvider }: ListAccountsPathParameters, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getListAccountsQueryKey({ cloudProvider }) }, + options, + ); + + return queryClient; +}; + +/** + * This endpoint creates a new cloud integration account for the specified cloud provider + * @summary Create account + */ +export const createAccount = ( + { cloudProvider }: CreateAccountPathParameters, + cloudintegrationtypesConnectionArtifactRequestDTO: BodyType, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v1/cloud_integrations/${cloudProvider}/accounts`, + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + data: cloudintegrationtypesConnectionArtifactRequestDTO, + signal, + }); +}; + +export const getCreateAccountMutationOptions = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { + pathParams: CreateAccountPathParameters; + data: BodyType; + }, + TContext + >; +}): UseMutationOptions< + Awaited>, + TError, + { + pathParams: CreateAccountPathParameters; + data: BodyType; + }, + TContext +> => { + const mutationKey = ['createAccount']; + 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>, + { + pathParams: CreateAccountPathParameters; + data: BodyType; + } + > = (props) => { + const { pathParams, data } = props ?? {}; + + return createAccount(pathParams, data); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type CreateAccountMutationResult = NonNullable< + Awaited> +>; +export type CreateAccountMutationBody = BodyType; +export type CreateAccountMutationError = ErrorType; + +/** + * @summary Create account + */ +export const useCreateAccount = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { + pathParams: CreateAccountPathParameters; + data: BodyType; + }, + TContext + >; +}): UseMutationResult< + Awaited>, + TError, + { + pathParams: CreateAccountPathParameters; + data: BodyType; + }, + TContext +> => { + const mutationOptions = getCreateAccountMutationOptions(options); + + return useMutation(mutationOptions); +}; +/** + * This endpoint disconnects an account for the specified cloud provider + * @summary Disconnect account + */ +export const disconnectAccount = ({ + cloudProvider, + id, +}: DisconnectAccountPathParameters) => { + return GeneratedAPIInstance({ + url: `/api/v1/cloud_integrations/${cloudProvider}/accounts/${id}`, + method: 'DELETE', + }); +}; + +export const getDisconnectAccountMutationOptions = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { pathParams: DisconnectAccountPathParameters }, + TContext + >; +}): UseMutationOptions< + Awaited>, + TError, + { pathParams: DisconnectAccountPathParameters }, + TContext +> => { + const mutationKey = ['disconnectAccount']; + 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>, + { pathParams: DisconnectAccountPathParameters } + > = (props) => { + const { pathParams } = props ?? {}; + + return disconnectAccount(pathParams); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type DisconnectAccountMutationResult = NonNullable< + Awaited> +>; + +export type DisconnectAccountMutationError = ErrorType; + +/** + * @summary Disconnect account + */ +export const useDisconnectAccount = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { pathParams: DisconnectAccountPathParameters }, + TContext + >; +}): UseMutationResult< + Awaited>, + TError, + { pathParams: DisconnectAccountPathParameters }, + TContext +> => { + const mutationOptions = getDisconnectAccountMutationOptions(options); + + return useMutation(mutationOptions); +}; +/** + * This endpoint gets an account for the specified cloud provider + * @summary Get account + */ +export const getAccount = ( + { cloudProvider, id }: GetAccountPathParameters, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v1/cloud_integrations/${cloudProvider}/accounts/${id}`, + method: 'GET', + signal, + }); +}; + +export const getGetAccountQueryKey = ({ + cloudProvider, + id, +}: GetAccountPathParameters) => { + return [`/api/v1/cloud_integrations/${cloudProvider}/accounts/${id}`] as const; +}; + +export const getGetAccountQueryOptions = < + TData = Awaited>, + TError = ErrorType +>( + { cloudProvider, id }: GetAccountPathParameters, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetAccountQueryKey({ cloudProvider, id }); + + const queryFn: QueryFunction>> = ({ + signal, + }) => getAccount({ cloudProvider, id }, signal); + + return { + queryKey, + queryFn, + enabled: !!(cloudProvider && id), + ...queryOptions, + } as UseQueryOptions>, TError, TData> & { + queryKey: QueryKey; + }; +}; + +export type GetAccountQueryResult = NonNullable< + Awaited> +>; +export type GetAccountQueryError = ErrorType; + +/** + * @summary Get account + */ + +export function useGetAccount< + TData = Awaited>, + TError = ErrorType +>( + { cloudProvider, id }: GetAccountPathParameters, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getGetAccountQueryOptions({ cloudProvider, id }, options); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Get account + */ +export const invalidateGetAccount = async ( + queryClient: QueryClient, + { cloudProvider, id }: GetAccountPathParameters, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getGetAccountQueryKey({ cloudProvider, id }) }, + options, + ); + + return queryClient; +}; + +/** + * This endpoint updates an account for the specified cloud provider + * @summary Update account + */ +export const updateAccount = ( + { cloudProvider, id }: UpdateAccountPathParameters, + cloudintegrationtypesUpdatableAccountDTO: BodyType, +) => { + return GeneratedAPIInstance({ + url: `/api/v1/cloud_integrations/${cloudProvider}/accounts/${id}`, + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + data: cloudintegrationtypesUpdatableAccountDTO, + }); +}; + +export const getUpdateAccountMutationOptions = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { + pathParams: UpdateAccountPathParameters; + data: BodyType; + }, + TContext + >; +}): UseMutationOptions< + Awaited>, + TError, + { + pathParams: UpdateAccountPathParameters; + data: BodyType; + }, + TContext +> => { + const mutationKey = ['updateAccount']; + 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>, + { + pathParams: UpdateAccountPathParameters; + data: BodyType; + } + > = (props) => { + const { pathParams, data } = props ?? {}; + + return updateAccount(pathParams, data); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type UpdateAccountMutationResult = NonNullable< + Awaited> +>; +export type UpdateAccountMutationBody = BodyType; +export type UpdateAccountMutationError = ErrorType; + +/** + * @summary Update account + */ +export const useUpdateAccount = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { + pathParams: UpdateAccountPathParameters; + data: BodyType; + }, + TContext + >; +}): UseMutationResult< + Awaited>, + TError, + { + pathParams: UpdateAccountPathParameters; + data: BodyType; + }, + TContext +> => { + const mutationOptions = getUpdateAccountMutationOptions(options); + + return useMutation(mutationOptions); +}; +/** + * This endpoint is called by the deployed agent to check in + * @summary Agent check-in + */ +export const agentCheckIn = ( + { cloudProvider }: AgentCheckInPathParameters, + cloudintegrationtypesPostableAgentCheckInRequestDTO: BodyType, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v1/cloud_integrations/${cloudProvider}/accounts/check_in`, + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + data: cloudintegrationtypesPostableAgentCheckInRequestDTO, + signal, + }); +}; + +export const getAgentCheckInMutationOptions = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { + pathParams: AgentCheckInPathParameters; + data: BodyType; + }, + TContext + >; +}): UseMutationOptions< + Awaited>, + TError, + { + pathParams: AgentCheckInPathParameters; + data: BodyType; + }, + TContext +> => { + const mutationKey = ['agentCheckIn']; + 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>, + { + pathParams: AgentCheckInPathParameters; + data: BodyType; + } + > = (props) => { + const { pathParams, data } = props ?? {}; + + return agentCheckIn(pathParams, data); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type AgentCheckInMutationResult = NonNullable< + Awaited> +>; +export type AgentCheckInMutationBody = BodyType; +export type AgentCheckInMutationError = ErrorType; + +/** + * @summary Agent check-in + */ +export const useAgentCheckIn = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { + pathParams: AgentCheckInPathParameters; + data: BodyType; + }, + TContext + >; +}): UseMutationResult< + Awaited>, + TError, + { + pathParams: AgentCheckInPathParameters; + data: BodyType; + }, + TContext +> => { + const mutationOptions = getAgentCheckInMutationOptions(options); + + return useMutation(mutationOptions); +}; +/** + * This endpoint lists the services metadata for the specified cloud provider + * @summary List services metadata + */ +export const listServicesMetadata = ( + { cloudProvider }: ListServicesMetadataPathParameters, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v1/cloud_integrations/${cloudProvider}/services`, + method: 'GET', + signal, + }); +}; + +export const getListServicesMetadataQueryKey = ({ + cloudProvider, +}: ListServicesMetadataPathParameters) => { + return [`/api/v1/cloud_integrations/${cloudProvider}/services`] as const; +}; + +export const getListServicesMetadataQueryOptions = < + TData = Awaited>, + TError = ErrorType +>( + { cloudProvider }: ListServicesMetadataPathParameters, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getListServicesMetadataQueryKey({ cloudProvider }); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => listServicesMetadata({ cloudProvider }, signal); + + return { + queryKey, + queryFn, + enabled: !!cloudProvider, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; +}; + +export type ListServicesMetadataQueryResult = NonNullable< + Awaited> +>; +export type ListServicesMetadataQueryError = ErrorType; + +/** + * @summary List services metadata + */ + +export function useListServicesMetadata< + TData = Awaited>, + TError = ErrorType +>( + { cloudProvider }: ListServicesMetadataPathParameters, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getListServicesMetadataQueryOptions( + { cloudProvider }, + options, + ); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary List services metadata + */ +export const invalidateListServicesMetadata = async ( + queryClient: QueryClient, + { cloudProvider }: ListServicesMetadataPathParameters, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getListServicesMetadataQueryKey({ cloudProvider }) }, + options, + ); + + return queryClient; +}; + +/** + * This endpoint gets a service for the specified cloud provider + * @summary Get service + */ +export const getService = ( + { cloudProvider, serviceId }: GetServicePathParameters, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v1/cloud_integrations/${cloudProvider}/services/${serviceId}`, + method: 'GET', + signal, + }); +}; + +export const getGetServiceQueryKey = ({ + cloudProvider, + serviceId, +}: GetServicePathParameters) => { + return [ + `/api/v1/cloud_integrations/${cloudProvider}/services/${serviceId}`, + ] as const; +}; + +export const getGetServiceQueryOptions = < + TData = Awaited>, + TError = ErrorType +>( + { cloudProvider, serviceId }: GetServicePathParameters, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetServiceQueryKey({ cloudProvider, serviceId }); + + const queryFn: QueryFunction>> = ({ + signal, + }) => getService({ cloudProvider, serviceId }, signal); + + return { + queryKey, + queryFn, + enabled: !!(cloudProvider && serviceId), + ...queryOptions, + } as UseQueryOptions>, TError, TData> & { + queryKey: QueryKey; + }; +}; + +export type GetServiceQueryResult = NonNullable< + Awaited> +>; +export type GetServiceQueryError = ErrorType; + +/** + * @summary Get service + */ + +export function useGetService< + TData = Awaited>, + TError = ErrorType +>( + { cloudProvider, serviceId }: GetServicePathParameters, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getGetServiceQueryOptions( + { cloudProvider, serviceId }, + options, + ); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Get service + */ +export const invalidateGetService = async ( + queryClient: QueryClient, + { cloudProvider, serviceId }: GetServicePathParameters, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getGetServiceQueryKey({ cloudProvider, serviceId }) }, + options, + ); + + return queryClient; +}; + +/** + * This endpoint updates a service for the specified cloud provider + * @summary Update service + */ +export const updateService = ( + { cloudProvider, serviceId }: UpdateServicePathParameters, + cloudintegrationtypesUpdatableServiceDTO: BodyType, +) => { + return GeneratedAPIInstance({ + url: `/api/v1/cloud_integrations/${cloudProvider}/services/${serviceId}`, + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + data: cloudintegrationtypesUpdatableServiceDTO, + }); +}; + +export const getUpdateServiceMutationOptions = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { + pathParams: UpdateServicePathParameters; + data: BodyType; + }, + TContext + >; +}): UseMutationOptions< + Awaited>, + TError, + { + pathParams: UpdateServicePathParameters; + data: BodyType; + }, + 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>, + { + pathParams: UpdateServicePathParameters; + data: BodyType; + } + > = (props) => { + const { pathParams, data } = props ?? {}; + + return updateService(pathParams, data); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type UpdateServiceMutationResult = NonNullable< + Awaited> +>; +export type UpdateServiceMutationBody = BodyType; +export type UpdateServiceMutationError = ErrorType; + +/** + * @summary Update service + */ +export const useUpdateService = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { + pathParams: UpdateServicePathParameters; + data: BodyType; + }, + TContext + >; +}): UseMutationResult< + Awaited>, + TError, + { + pathParams: UpdateServicePathParameters; + data: BodyType; + }, + TContext +> => { + const mutationOptions = getUpdateServiceMutationOptions(options); + + return useMutation(mutationOptions); +}; diff --git a/frontend/src/api/generated/services/health/index.ts b/frontend/src/api/generated/services/health/index.ts new file mode 100644 index 00000000000..e0544243327 --- /dev/null +++ b/frontend/src/api/generated/services/health/index.ts @@ -0,0 +1,250 @@ +/** + * ! Do not edit manually + * * The file has been auto-generated using Orval for SigNoz + * * regenerate with 'yarn generate:api' + * SigNoz + */ +import type { + InvalidateOptions, + QueryClient, + QueryFunction, + QueryKey, + UseQueryOptions, + UseQueryResult, +} from 'react-query'; +import { useQuery } from 'react-query'; + +import type { ErrorType } from '../../../generatedAPIInstance'; +import { GeneratedAPIInstance } from '../../../generatedAPIInstance'; +import type { + Healthz200, + Healthz503, + Livez200, + Readyz200, + Readyz503, + RenderErrorResponseDTO, +} from '../sigNoz.schemas'; + +/** + * @summary Health check + */ +export const healthz = (signal?: AbortSignal) => { + return GeneratedAPIInstance({ + url: `/api/v2/healthz`, + method: 'GET', + signal, + }); +}; + +export const getHealthzQueryKey = () => { + return [`/api/v2/healthz`] as const; +}; + +export const getHealthzQueryOptions = < + TData = Awaited>, + TError = ErrorType +>(options?: { + query?: UseQueryOptions>, TError, TData>; +}) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getHealthzQueryKey(); + + const queryFn: QueryFunction>> = ({ + signal, + }) => healthz(signal); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; +}; + +export type HealthzQueryResult = NonNullable< + Awaited> +>; +export type HealthzQueryError = ErrorType; + +/** + * @summary Health check + */ + +export function useHealthz< + TData = Awaited>, + TError = ErrorType +>(options?: { + query?: UseQueryOptions>, TError, TData>; +}): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getHealthzQueryOptions(options); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Health check + */ +export const invalidateHealthz = async ( + queryClient: QueryClient, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getHealthzQueryKey() }, + options, + ); + + return queryClient; +}; + +/** + * @summary Liveness check + */ +export const livez = (signal?: AbortSignal) => { + return GeneratedAPIInstance({ + url: `/api/v2/livez`, + method: 'GET', + signal, + }); +}; + +export const getLivezQueryKey = () => { + return [`/api/v2/livez`] as const; +}; + +export const getLivezQueryOptions = < + TData = Awaited>, + TError = ErrorType +>(options?: { + query?: UseQueryOptions>, TError, TData>; +}) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getLivezQueryKey(); + + const queryFn: QueryFunction>> = ({ + signal, + }) => livez(signal); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; +}; + +export type LivezQueryResult = NonNullable>>; +export type LivezQueryError = ErrorType; + +/** + * @summary Liveness check + */ + +export function useLivez< + TData = Awaited>, + TError = ErrorType +>(options?: { + query?: UseQueryOptions>, TError, TData>; +}): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getLivezQueryOptions(options); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Liveness check + */ +export const invalidateLivez = async ( + queryClient: QueryClient, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries({ queryKey: getLivezQueryKey() }, options); + + return queryClient; +}; + +/** + * @summary Readiness check + */ +export const readyz = (signal?: AbortSignal) => { + return GeneratedAPIInstance({ + url: `/api/v2/readyz`, + method: 'GET', + signal, + }); +}; + +export const getReadyzQueryKey = () => { + return [`/api/v2/readyz`] as const; +}; + +export const getReadyzQueryOptions = < + TData = Awaited>, + TError = ErrorType +>(options?: { + query?: UseQueryOptions>, TError, TData>; +}) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getReadyzQueryKey(); + + const queryFn: QueryFunction>> = ({ + signal, + }) => readyz(signal); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; +}; + +export type ReadyzQueryResult = NonNullable>>; +export type ReadyzQueryError = ErrorType; + +/** + * @summary Readiness check + */ + +export function useReadyz< + TData = Awaited>, + TError = ErrorType +>(options?: { + query?: UseQueryOptions>, TError, TData>; +}): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getReadyzQueryOptions(options); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Readiness check + */ +export const invalidateReadyz = async ( + queryClient: QueryClient, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getReadyzQueryKey() }, + options, + ); + + return queryClient; +}; diff --git a/frontend/src/api/generated/services/logs/index.ts b/frontend/src/api/generated/services/logs/index.ts index e02d1242bf0..a1db95ce72f 100644 --- a/frontend/src/api/generated/services/logs/index.ts +++ b/frontend/src/api/generated/services/logs/index.ts @@ -20,11 +20,113 @@ import { useMutation, useQuery } from 'react-query'; import type { BodyType, ErrorType } from '../../../generatedAPIInstance'; import { GeneratedAPIInstance } from '../../../generatedAPIInstance'; import type { + HandleExportRawDataPOSTParams, ListPromotedAndIndexedPaths200, PromotetypesPromotePathDTO, + Querybuildertypesv5QueryRangeRequestDTO, RenderErrorResponseDTO, } from '../sigNoz.schemas'; +/** + * This endpoints allows complex query exporting raw data for traces and logs + * @summary Export raw data + */ +export const handleExportRawDataPOST = ( + querybuildertypesv5QueryRangeRequestDTO: BodyType, + params?: HandleExportRawDataPOSTParams, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v1/export_raw_data`, + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + data: querybuildertypesv5QueryRangeRequestDTO, + params, + signal, + }); +}; + +export const getHandleExportRawDataPOSTMutationOptions = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { + data: BodyType; + params?: HandleExportRawDataPOSTParams; + }, + TContext + >; +}): UseMutationOptions< + Awaited>, + TError, + { + data: BodyType; + params?: HandleExportRawDataPOSTParams; + }, + TContext +> => { + const mutationKey = ['handleExportRawDataPOST']; + 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>, + { + data: BodyType; + params?: HandleExportRawDataPOSTParams; + } + > = (props) => { + const { data, params } = props ?? {}; + + return handleExportRawDataPOST(data, params); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type HandleExportRawDataPOSTMutationResult = NonNullable< + Awaited> +>; +export type HandleExportRawDataPOSTMutationBody = BodyType; +export type HandleExportRawDataPOSTMutationError = ErrorType; + +/** + * @summary Export raw data + */ +export const useHandleExportRawDataPOST = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { + data: BodyType; + params?: HandleExportRawDataPOSTParams; + }, + TContext + >; +}): UseMutationResult< + Awaited>, + TError, + { + data: BodyType; + params?: HandleExportRawDataPOSTParams; + }, + TContext +> => { + const mutationOptions = getHandleExportRawDataPOSTMutationOptions(options); + + return useMutation(mutationOptions); +}; /** * This endpoints promotes and indexes paths * @summary Promote and index paths diff --git a/frontend/src/api/generated/services/metrics/index.ts b/frontend/src/api/generated/services/metrics/index.ts index 7f6703b2f41..20a06783cf0 100644 --- a/frontend/src/api/generated/services/metrics/index.ts +++ b/frontend/src/api/generated/services/metrics/index.ts @@ -31,10 +31,13 @@ import type { GetMetricHighlightsPathParameters, GetMetricMetadata200, GetMetricMetadataPathParameters, + GetMetricsOnboardingStatus200, GetMetricsStats200, GetMetricsTreemap200, + InspectMetrics200, ListMetrics200, ListMetricsParams, + MetricsexplorertypesInspectMetricsRequestDTO, MetricsexplorertypesStatsRequestDTO, MetricsexplorertypesTreemapRequestDTO, MetricsexplorertypesUpdateMetricMetadataRequestDTO, @@ -778,6 +781,176 @@ export const useUpdateMetricMetadata = < return useMutation(mutationOptions); }; +/** + * Returns raw time series data points for a metric within a time range (max 30 minutes). Each series includes labels and timestamp/value pairs. + * @summary Inspect raw metric data points + */ +export const inspectMetrics = ( + metricsexplorertypesInspectMetricsRequestDTO: BodyType, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v2/metrics/inspect`, + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + data: metricsexplorertypesInspectMetricsRequestDTO, + signal, + }); +}; + +export const getInspectMetricsMutationOptions = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: BodyType }, + TContext + >; +}): UseMutationOptions< + Awaited>, + TError, + { data: BodyType }, + TContext +> => { + const mutationKey = ['inspectMetrics']; + 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>, + { data: BodyType } + > = (props) => { + const { data } = props ?? {}; + + return inspectMetrics(data); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type InspectMetricsMutationResult = NonNullable< + Awaited> +>; +export type InspectMetricsMutationBody = BodyType; +export type InspectMetricsMutationError = ErrorType; + +/** + * @summary Inspect raw metric data points + */ +export const useInspectMetrics = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: BodyType }, + TContext + >; +}): UseMutationResult< + Awaited>, + TError, + { data: BodyType }, + TContext +> => { + const mutationOptions = getInspectMetricsMutationOptions(options); + + return useMutation(mutationOptions); +}; +/** + * Lightweight endpoint that checks if any non-SigNoz metrics have been ingested, used for onboarding status detection + * @summary Check if non-SigNoz metrics have been received + */ +export const getMetricsOnboardingStatus = (signal?: AbortSignal) => { + return GeneratedAPIInstance({ + url: `/api/v2/metrics/onboarding`, + method: 'GET', + signal, + }); +}; + +export const getGetMetricsOnboardingStatusQueryKey = () => { + return [`/api/v2/metrics/onboarding`] as const; +}; + +export const getGetMetricsOnboardingStatusQueryOptions = < + TData = Awaited>, + TError = ErrorType +>(options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; +}) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetMetricsOnboardingStatusQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getMetricsOnboardingStatus(signal); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; +}; + +export type GetMetricsOnboardingStatusQueryResult = NonNullable< + Awaited> +>; +export type GetMetricsOnboardingStatusQueryError = ErrorType; + +/** + * @summary Check if non-SigNoz metrics have been received + */ + +export function useGetMetricsOnboardingStatus< + TData = Awaited>, + TError = ErrorType +>(options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; +}): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getGetMetricsOnboardingStatusQueryOptions(options); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Check if non-SigNoz metrics have been received + */ +export const invalidateGetMetricsOnboardingStatus = async ( + queryClient: QueryClient, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getGetMetricsOnboardingStatusQueryKey() }, + options, + ); + + return queryClient; +}; + /** * This endpoint provides list of metrics with their number of samples and timeseries for the given time range * @summary Get metrics statistics diff --git a/frontend/src/api/generated/services/rules/index.ts b/frontend/src/api/generated/services/rules/index.ts new file mode 100644 index 00000000000..24097e84d2d --- /dev/null +++ b/frontend/src/api/generated/services/rules/index.ts @@ -0,0 +1,744 @@ +/** + * ! Do not edit manually + * * The file has been auto-generated using Orval for SigNoz + * * regenerate with 'yarn generate:api' + * SigNoz + */ +import type { + InvalidateOptions, + QueryClient, + QueryFunction, + QueryKey, + UseQueryOptions, + UseQueryResult, +} from 'react-query'; +import { useQuery } from 'react-query'; + +import type { ErrorType } from '../../../generatedAPIInstance'; +import { GeneratedAPIInstance } from '../../../generatedAPIInstance'; +import type { + GetRuleHistoryFilterKeys200, + GetRuleHistoryFilterKeysParams, + GetRuleHistoryFilterKeysPathParameters, + GetRuleHistoryFilterValues200, + GetRuleHistoryFilterValuesParams, + GetRuleHistoryFilterValuesPathParameters, + GetRuleHistoryOverallStatus200, + GetRuleHistoryOverallStatusParams, + GetRuleHistoryOverallStatusPathParameters, + GetRuleHistoryStats200, + GetRuleHistoryStatsParams, + GetRuleHistoryStatsPathParameters, + GetRuleHistoryTimeline200, + GetRuleHistoryTimelineParams, + GetRuleHistoryTimelinePathParameters, + GetRuleHistoryTopContributors200, + GetRuleHistoryTopContributorsParams, + GetRuleHistoryTopContributorsPathParameters, + RenderErrorResponseDTO, +} from '../sigNoz.schemas'; + +/** + * Returns distinct label keys from rule history entries for the selected range. + * @summary Get rule history filter keys + */ +export const getRuleHistoryFilterKeys = ( + { id }: GetRuleHistoryFilterKeysPathParameters, + params?: GetRuleHistoryFilterKeysParams, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v2/rules/${id}/history/filter_keys`, + method: 'GET', + params, + signal, + }); +}; + +export const getGetRuleHistoryFilterKeysQueryKey = ( + { id }: GetRuleHistoryFilterKeysPathParameters, + params?: GetRuleHistoryFilterKeysParams, +) => { + return [ + `/api/v2/rules/${id}/history/filter_keys`, + ...(params ? [params] : []), + ] as const; +}; + +export const getGetRuleHistoryFilterKeysQueryOptions = < + TData = Awaited>, + TError = ErrorType +>( + { id }: GetRuleHistoryFilterKeysPathParameters, + params?: GetRuleHistoryFilterKeysParams, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetRuleHistoryFilterKeysQueryKey({ id }, params); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getRuleHistoryFilterKeys({ id }, params, signal); + + return { + queryKey, + queryFn, + enabled: !!id, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; +}; + +export type GetRuleHistoryFilterKeysQueryResult = NonNullable< + Awaited> +>; +export type GetRuleHistoryFilterKeysQueryError = ErrorType; + +/** + * @summary Get rule history filter keys + */ + +export function useGetRuleHistoryFilterKeys< + TData = Awaited>, + TError = ErrorType +>( + { id }: GetRuleHistoryFilterKeysPathParameters, + params?: GetRuleHistoryFilterKeysParams, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getGetRuleHistoryFilterKeysQueryOptions( + { id }, + params, + options, + ); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Get rule history filter keys + */ +export const invalidateGetRuleHistoryFilterKeys = async ( + queryClient: QueryClient, + { id }: GetRuleHistoryFilterKeysPathParameters, + params?: GetRuleHistoryFilterKeysParams, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getGetRuleHistoryFilterKeysQueryKey({ id }, params) }, + options, + ); + + return queryClient; +}; + +/** + * Returns distinct label values for a given key from rule history entries. + * @summary Get rule history filter values + */ +export const getRuleHistoryFilterValues = ( + { id }: GetRuleHistoryFilterValuesPathParameters, + params?: GetRuleHistoryFilterValuesParams, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v2/rules/${id}/history/filter_values`, + method: 'GET', + params, + signal, + }); +}; + +export const getGetRuleHistoryFilterValuesQueryKey = ( + { id }: GetRuleHistoryFilterValuesPathParameters, + params?: GetRuleHistoryFilterValuesParams, +) => { + return [ + `/api/v2/rules/${id}/history/filter_values`, + ...(params ? [params] : []), + ] as const; +}; + +export const getGetRuleHistoryFilterValuesQueryOptions = < + TData = Awaited>, + TError = ErrorType +>( + { id }: GetRuleHistoryFilterValuesPathParameters, + params?: GetRuleHistoryFilterValuesParams, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? + getGetRuleHistoryFilterValuesQueryKey({ id }, params); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getRuleHistoryFilterValues({ id }, params, signal); + + return { + queryKey, + queryFn, + enabled: !!id, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; +}; + +export type GetRuleHistoryFilterValuesQueryResult = NonNullable< + Awaited> +>; +export type GetRuleHistoryFilterValuesQueryError = ErrorType; + +/** + * @summary Get rule history filter values + */ + +export function useGetRuleHistoryFilterValues< + TData = Awaited>, + TError = ErrorType +>( + { id }: GetRuleHistoryFilterValuesPathParameters, + params?: GetRuleHistoryFilterValuesParams, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getGetRuleHistoryFilterValuesQueryOptions( + { id }, + params, + options, + ); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Get rule history filter values + */ +export const invalidateGetRuleHistoryFilterValues = async ( + queryClient: QueryClient, + { id }: GetRuleHistoryFilterValuesPathParameters, + params?: GetRuleHistoryFilterValuesParams, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getGetRuleHistoryFilterValuesQueryKey({ id }, params) }, + options, + ); + + return queryClient; +}; + +/** + * Returns overall firing/inactive intervals for a rule in the selected time range. + * @summary Get rule overall status timeline + */ +export const getRuleHistoryOverallStatus = ( + { id }: GetRuleHistoryOverallStatusPathParameters, + params: GetRuleHistoryOverallStatusParams, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v2/rules/${id}/history/overall_status`, + method: 'GET', + params, + signal, + }); +}; + +export const getGetRuleHistoryOverallStatusQueryKey = ( + { id }: GetRuleHistoryOverallStatusPathParameters, + params?: GetRuleHistoryOverallStatusParams, +) => { + return [ + `/api/v2/rules/${id}/history/overall_status`, + ...(params ? [params] : []), + ] as const; +}; + +export const getGetRuleHistoryOverallStatusQueryOptions = < + TData = Awaited>, + TError = ErrorType +>( + { id }: GetRuleHistoryOverallStatusPathParameters, + params: GetRuleHistoryOverallStatusParams, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? + getGetRuleHistoryOverallStatusQueryKey({ id }, params); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getRuleHistoryOverallStatus({ id }, params, signal); + + return { + queryKey, + queryFn, + enabled: !!id, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; +}; + +export type GetRuleHistoryOverallStatusQueryResult = NonNullable< + Awaited> +>; +export type GetRuleHistoryOverallStatusQueryError = ErrorType; + +/** + * @summary Get rule overall status timeline + */ + +export function useGetRuleHistoryOverallStatus< + TData = Awaited>, + TError = ErrorType +>( + { id }: GetRuleHistoryOverallStatusPathParameters, + params: GetRuleHistoryOverallStatusParams, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getGetRuleHistoryOverallStatusQueryOptions( + { id }, + params, + options, + ); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Get rule overall status timeline + */ +export const invalidateGetRuleHistoryOverallStatus = async ( + queryClient: QueryClient, + { id }: GetRuleHistoryOverallStatusPathParameters, + params: GetRuleHistoryOverallStatusParams, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getGetRuleHistoryOverallStatusQueryKey({ id }, params) }, + options, + ); + + return queryClient; +}; + +/** + * Returns trigger and resolution statistics for a rule in the selected time range. + * @summary Get rule history stats + */ +export const getRuleHistoryStats = ( + { id }: GetRuleHistoryStatsPathParameters, + params: GetRuleHistoryStatsParams, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v2/rules/${id}/history/stats`, + method: 'GET', + params, + signal, + }); +}; + +export const getGetRuleHistoryStatsQueryKey = ( + { id }: GetRuleHistoryStatsPathParameters, + params?: GetRuleHistoryStatsParams, +) => { + return [ + `/api/v2/rules/${id}/history/stats`, + ...(params ? [params] : []), + ] as const; +}; + +export const getGetRuleHistoryStatsQueryOptions = < + TData = Awaited>, + TError = ErrorType +>( + { id }: GetRuleHistoryStatsPathParameters, + params: GetRuleHistoryStatsParams, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetRuleHistoryStatsQueryKey({ id }, params); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getRuleHistoryStats({ id }, params, signal); + + return { + queryKey, + queryFn, + enabled: !!id, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; +}; + +export type GetRuleHistoryStatsQueryResult = NonNullable< + Awaited> +>; +export type GetRuleHistoryStatsQueryError = ErrorType; + +/** + * @summary Get rule history stats + */ + +export function useGetRuleHistoryStats< + TData = Awaited>, + TError = ErrorType +>( + { id }: GetRuleHistoryStatsPathParameters, + params: GetRuleHistoryStatsParams, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getGetRuleHistoryStatsQueryOptions( + { id }, + params, + options, + ); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Get rule history stats + */ +export const invalidateGetRuleHistoryStats = async ( + queryClient: QueryClient, + { id }: GetRuleHistoryStatsPathParameters, + params: GetRuleHistoryStatsParams, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getGetRuleHistoryStatsQueryKey({ id }, params) }, + options, + ); + + return queryClient; +}; + +/** + * Returns paginated timeline entries for rule state transitions. + * @summary Get rule history timeline + */ +export const getRuleHistoryTimeline = ( + { id }: GetRuleHistoryTimelinePathParameters, + params: GetRuleHistoryTimelineParams, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v2/rules/${id}/history/timeline`, + method: 'GET', + params, + signal, + }); +}; + +export const getGetRuleHistoryTimelineQueryKey = ( + { id }: GetRuleHistoryTimelinePathParameters, + params?: GetRuleHistoryTimelineParams, +) => { + return [ + `/api/v2/rules/${id}/history/timeline`, + ...(params ? [params] : []), + ] as const; +}; + +export const getGetRuleHistoryTimelineQueryOptions = < + TData = Awaited>, + TError = ErrorType +>( + { id }: GetRuleHistoryTimelinePathParameters, + params: GetRuleHistoryTimelineParams, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetRuleHistoryTimelineQueryKey({ id }, params); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getRuleHistoryTimeline({ id }, params, signal); + + return { + queryKey, + queryFn, + enabled: !!id, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; +}; + +export type GetRuleHistoryTimelineQueryResult = NonNullable< + Awaited> +>; +export type GetRuleHistoryTimelineQueryError = ErrorType; + +/** + * @summary Get rule history timeline + */ + +export function useGetRuleHistoryTimeline< + TData = Awaited>, + TError = ErrorType +>( + { id }: GetRuleHistoryTimelinePathParameters, + params: GetRuleHistoryTimelineParams, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getGetRuleHistoryTimelineQueryOptions( + { id }, + params, + options, + ); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Get rule history timeline + */ +export const invalidateGetRuleHistoryTimeline = async ( + queryClient: QueryClient, + { id }: GetRuleHistoryTimelinePathParameters, + params: GetRuleHistoryTimelineParams, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getGetRuleHistoryTimelineQueryKey({ id }, params) }, + options, + ); + + return queryClient; +}; + +/** + * Returns top label combinations contributing to rule firing in the selected time range. + * @summary Get top contributors to rule firing + */ +export const getRuleHistoryTopContributors = ( + { id }: GetRuleHistoryTopContributorsPathParameters, + params: GetRuleHistoryTopContributorsParams, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v2/rules/${id}/history/top_contributors`, + method: 'GET', + params, + signal, + }); +}; + +export const getGetRuleHistoryTopContributorsQueryKey = ( + { id }: GetRuleHistoryTopContributorsPathParameters, + params?: GetRuleHistoryTopContributorsParams, +) => { + return [ + `/api/v2/rules/${id}/history/top_contributors`, + ...(params ? [params] : []), + ] as const; +}; + +export const getGetRuleHistoryTopContributorsQueryOptions = < + TData = Awaited>, + TError = ErrorType +>( + { id }: GetRuleHistoryTopContributorsPathParameters, + params: GetRuleHistoryTopContributorsParams, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? + getGetRuleHistoryTopContributorsQueryKey({ id }, params); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getRuleHistoryTopContributors({ id }, params, signal); + + return { + queryKey, + queryFn, + enabled: !!id, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; +}; + +export type GetRuleHistoryTopContributorsQueryResult = NonNullable< + Awaited> +>; +export type GetRuleHistoryTopContributorsQueryError = ErrorType; + +/** + * @summary Get top contributors to rule firing + */ + +export function useGetRuleHistoryTopContributors< + TData = Awaited>, + TError = ErrorType +>( + { id }: GetRuleHistoryTopContributorsPathParameters, + params: GetRuleHistoryTopContributorsParams, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getGetRuleHistoryTopContributorsQueryOptions( + { id }, + params, + options, + ); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Get top contributors to rule firing + */ +export const invalidateGetRuleHistoryTopContributors = async ( + queryClient: QueryClient, + { id }: GetRuleHistoryTopContributorsPathParameters, + params: GetRuleHistoryTopContributorsParams, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getGetRuleHistoryTopContributorsQueryKey({ id }, params) }, + options, + ); + + return queryClient; +}; diff --git a/frontend/src/api/generated/services/serviceaccount/index.ts b/frontend/src/api/generated/services/serviceaccount/index.ts index 0d1114b074b..4470d928754 100644 --- a/frontend/src/api/generated/services/serviceaccount/index.ts +++ b/frontend/src/api/generated/services/serviceaccount/index.ts @@ -23,9 +23,15 @@ import type { CreateServiceAccount201, CreateServiceAccountKey201, CreateServiceAccountKeyPathParameters, + CreateServiceAccountRole201, + CreateServiceAccountRolePathParameters, DeleteServiceAccountPathParameters, + DeleteServiceAccountRolePathParameters, + GetMyServiceAccount200, GetServiceAccount200, GetServiceAccountPathParameters, + GetServiceAccountRoles200, + GetServiceAccountRolesPathParameters, ListServiceAccountKeys200, ListServiceAccountKeysPathParameters, ListServiceAccounts200, @@ -33,12 +39,10 @@ import type { RevokeServiceAccountKeyPathParameters, ServiceaccounttypesPostableFactorAPIKeyDTO, ServiceaccounttypesPostableServiceAccountDTO, + ServiceaccounttypesPostableServiceAccountRoleDTO, ServiceaccounttypesUpdatableFactorAPIKeyDTO, - ServiceaccounttypesUpdatableServiceAccountDTO, - ServiceaccounttypesUpdatableServiceAccountStatusDTO, UpdateServiceAccountKeyPathParameters, UpdateServiceAccountPathParameters, - UpdateServiceAccountStatusPathParameters, } from '../sigNoz.schemas'; /** @@ -399,13 +403,13 @@ export const invalidateGetServiceAccount = async ( */ export const updateServiceAccount = ( { id }: UpdateServiceAccountPathParameters, - serviceaccounttypesUpdatableServiceAccountDTO: BodyType, + serviceaccounttypesPostableServiceAccountDTO: BodyType, ) => { return GeneratedAPIInstance({ url: `/api/v1/service_accounts/${id}`, method: 'PUT', headers: { 'Content-Type': 'application/json' }, - data: serviceaccounttypesUpdatableServiceAccountDTO, + data: serviceaccounttypesPostableServiceAccountDTO, }); }; @@ -418,7 +422,7 @@ export const getUpdateServiceAccountMutationOptions = < TError, { pathParams: UpdateServiceAccountPathParameters; - data: BodyType; + data: BodyType; }, TContext >; @@ -427,7 +431,7 @@ export const getUpdateServiceAccountMutationOptions = < TError, { pathParams: UpdateServiceAccountPathParameters; - data: BodyType; + data: BodyType; }, TContext > => { @@ -444,7 +448,7 @@ export const getUpdateServiceAccountMutationOptions = < Awaited>, { pathParams: UpdateServiceAccountPathParameters; - data: BodyType; + data: BodyType; } > = (props) => { const { pathParams, data } = props ?? {}; @@ -458,7 +462,7 @@ export const getUpdateServiceAccountMutationOptions = < export type UpdateServiceAccountMutationResult = NonNullable< Awaited> >; -export type UpdateServiceAccountMutationBody = BodyType; +export type UpdateServiceAccountMutationBody = BodyType; export type UpdateServiceAccountMutationError = ErrorType; /** @@ -473,7 +477,7 @@ export const useUpdateServiceAccount = < TError, { pathParams: UpdateServiceAccountPathParameters; - data: BodyType; + data: BodyType; }, TContext >; @@ -482,7 +486,7 @@ export const useUpdateServiceAccount = < TError, { pathParams: UpdateServiceAccountPathParameters; - data: BodyType; + data: BodyType; }, TContext > => { @@ -871,44 +875,150 @@ export const useUpdateServiceAccountKey = < return useMutation(mutationOptions); }; /** - * This endpoint updates an existing service account status - * @summary Updates a service account status + * This endpoint gets all the roles for the existing service account + * @summary Gets service account roles */ -export const updateServiceAccountStatus = ( - { id }: UpdateServiceAccountStatusPathParameters, - serviceaccounttypesUpdatableServiceAccountStatusDTO: BodyType, +export const getServiceAccountRoles = ( + { id }: GetServiceAccountRolesPathParameters, + signal?: AbortSignal, ) => { - return GeneratedAPIInstance({ - url: `/api/v1/service_accounts/${id}/status`, - method: 'PUT', + return GeneratedAPIInstance({ + url: `/api/v1/service_accounts/${id}/roles`, + method: 'GET', + signal, + }); +}; + +export const getGetServiceAccountRolesQueryKey = ({ + id, +}: GetServiceAccountRolesPathParameters) => { + return [`/api/v1/service_accounts/${id}/roles`] as const; +}; + +export const getGetServiceAccountRolesQueryOptions = < + TData = Awaited>, + TError = ErrorType +>( + { id }: GetServiceAccountRolesPathParameters, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetServiceAccountRolesQueryKey({ id }); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getServiceAccountRoles({ id }, signal); + + return { + queryKey, + queryFn, + enabled: !!id, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; +}; + +export type GetServiceAccountRolesQueryResult = NonNullable< + Awaited> +>; +export type GetServiceAccountRolesQueryError = ErrorType; + +/** + * @summary Gets service account roles + */ + +export function useGetServiceAccountRoles< + TData = Awaited>, + TError = ErrorType +>( + { id }: GetServiceAccountRolesPathParameters, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getGetServiceAccountRolesQueryOptions({ id }, options); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Gets service account roles + */ +export const invalidateGetServiceAccountRoles = async ( + queryClient: QueryClient, + { id }: GetServiceAccountRolesPathParameters, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getGetServiceAccountRolesQueryKey({ id }) }, + options, + ); + + return queryClient; +}; + +/** + * This endpoint assigns a role to a service account + * @summary Create service account role + */ +export const createServiceAccountRole = ( + { id }: CreateServiceAccountRolePathParameters, + serviceaccounttypesPostableServiceAccountRoleDTO: BodyType, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v1/service_accounts/${id}/roles`, + method: 'POST', headers: { 'Content-Type': 'application/json' }, - data: serviceaccounttypesUpdatableServiceAccountStatusDTO, + data: serviceaccounttypesPostableServiceAccountRoleDTO, + signal, }); }; -export const getUpdateServiceAccountStatusMutationOptions = < +export const getCreateServiceAccountRoleMutationOptions = < TError = ErrorType, TContext = unknown >(options?: { mutation?: UseMutationOptions< - Awaited>, + Awaited>, TError, { - pathParams: UpdateServiceAccountStatusPathParameters; - data: BodyType; + pathParams: CreateServiceAccountRolePathParameters; + data: BodyType; }, TContext >; }): UseMutationOptions< - Awaited>, + Awaited>, TError, { - pathParams: UpdateServiceAccountStatusPathParameters; - data: BodyType; + pathParams: CreateServiceAccountRolePathParameters; + data: BodyType; }, TContext > => { - const mutationKey = ['updateServiceAccountStatus']; + const mutationKey = ['createServiceAccountRole']; const { mutation: mutationOptions } = options ? options.mutation && 'mutationKey' in options.mutation && @@ -918,52 +1028,299 @@ export const getUpdateServiceAccountStatusMutationOptions = < : { mutation: { mutationKey } }; const mutationFn: MutationFunction< - Awaited>, + Awaited>, { - pathParams: UpdateServiceAccountStatusPathParameters; - data: BodyType; + pathParams: CreateServiceAccountRolePathParameters; + data: BodyType; } > = (props) => { const { pathParams, data } = props ?? {}; - return updateServiceAccountStatus(pathParams, data); + return createServiceAccountRole(pathParams, data); }; return { mutationFn, ...mutationOptions }; }; -export type UpdateServiceAccountStatusMutationResult = NonNullable< - Awaited> +export type CreateServiceAccountRoleMutationResult = NonNullable< + Awaited> >; -export type UpdateServiceAccountStatusMutationBody = BodyType; -export type UpdateServiceAccountStatusMutationError = ErrorType; +export type CreateServiceAccountRoleMutationBody = BodyType; +export type CreateServiceAccountRoleMutationError = ErrorType; /** - * @summary Updates a service account status + * @summary Create service account role */ -export const useUpdateServiceAccountStatus = < +export const useCreateServiceAccountRole = < TError = ErrorType, TContext = unknown >(options?: { mutation?: UseMutationOptions< - Awaited>, + Awaited>, TError, { - pathParams: UpdateServiceAccountStatusPathParameters; - data: BodyType; + pathParams: CreateServiceAccountRolePathParameters; + data: BodyType; }, TContext >; }): UseMutationResult< - Awaited>, + Awaited>, TError, { - pathParams: UpdateServiceAccountStatusPathParameters; - data: BodyType; + pathParams: CreateServiceAccountRolePathParameters; + data: BodyType; }, TContext > => { - const mutationOptions = getUpdateServiceAccountStatusMutationOptions(options); + const mutationOptions = getCreateServiceAccountRoleMutationOptions(options); + + return useMutation(mutationOptions); +}; +/** + * This endpoint revokes a role from service account + * @summary Delete service account role + */ +export const deleteServiceAccountRole = ({ + id, + rid, +}: DeleteServiceAccountRolePathParameters) => { + return GeneratedAPIInstance({ + url: `/api/v1/service_accounts/${id}/roles/${rid}`, + method: 'DELETE', + }); +}; + +export const getDeleteServiceAccountRoleMutationOptions = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { pathParams: DeleteServiceAccountRolePathParameters }, + TContext + >; +}): UseMutationOptions< + Awaited>, + TError, + { pathParams: DeleteServiceAccountRolePathParameters }, + TContext +> => { + const mutationKey = ['deleteServiceAccountRole']; + 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>, + { pathParams: DeleteServiceAccountRolePathParameters } + > = (props) => { + const { pathParams } = props ?? {}; + + return deleteServiceAccountRole(pathParams); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type DeleteServiceAccountRoleMutationResult = NonNullable< + Awaited> +>; + +export type DeleteServiceAccountRoleMutationError = ErrorType; + +/** + * @summary Delete service account role + */ +export const useDeleteServiceAccountRole = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { pathParams: DeleteServiceAccountRolePathParameters }, + TContext + >; +}): UseMutationResult< + Awaited>, + TError, + { pathParams: DeleteServiceAccountRolePathParameters }, + TContext +> => { + const mutationOptions = getDeleteServiceAccountRoleMutationOptions(options); + + return useMutation(mutationOptions); +}; +/** + * This endpoint gets my service account + * @summary Gets my service account + */ +export const getMyServiceAccount = (signal?: AbortSignal) => { + return GeneratedAPIInstance({ + url: `/api/v1/service_accounts/me`, + method: 'GET', + signal, + }); +}; + +export const getGetMyServiceAccountQueryKey = () => { + return [`/api/v1/service_accounts/me`] as const; +}; + +export const getGetMyServiceAccountQueryOptions = < + TData = Awaited>, + TError = ErrorType +>(options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; +}) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetMyServiceAccountQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getMyServiceAccount(signal); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; +}; + +export type GetMyServiceAccountQueryResult = NonNullable< + Awaited> +>; +export type GetMyServiceAccountQueryError = ErrorType; + +/** + * @summary Gets my service account + */ + +export function useGetMyServiceAccount< + TData = Awaited>, + TError = ErrorType +>(options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; +}): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getGetMyServiceAccountQueryOptions(options); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Gets my service account + */ +export const invalidateGetMyServiceAccount = async ( + queryClient: QueryClient, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getGetMyServiceAccountQueryKey() }, + options, + ); + + return queryClient; +}; + +/** + * This endpoint gets my service account + * @summary Updates my service account + */ +export const updateMyServiceAccount = ( + serviceaccounttypesPostableServiceAccountDTO: BodyType, +) => { + return GeneratedAPIInstance({ + url: `/api/v1/service_accounts/me`, + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + data: serviceaccounttypesPostableServiceAccountDTO, + }); +}; + +export const getUpdateMyServiceAccountMutationOptions = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: BodyType }, + TContext + >; +}): UseMutationOptions< + Awaited>, + TError, + { data: BodyType }, + TContext +> => { + const mutationKey = ['updateMyServiceAccount']; + 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>, + { data: BodyType } + > = (props) => { + const { data } = props ?? {}; + + return updateMyServiceAccount(data); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type UpdateMyServiceAccountMutationResult = NonNullable< + Awaited> +>; +export type UpdateMyServiceAccountMutationBody = BodyType; +export type UpdateMyServiceAccountMutationError = ErrorType; + +/** + * @summary Updates my service account + */ +export const useUpdateMyServiceAccount = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: BodyType }, + TContext + >; +}): UseMutationResult< + Awaited>, + TError, + { data: BodyType }, + TContext +> => { + const mutationOptions = getUpdateMyServiceAccountMutationOptions(options); return useMutation(mutationOptions); }; diff --git a/frontend/src/api/generated/services/sigNoz.schemas.ts b/frontend/src/api/generated/services/sigNoz.schemas.ts index d51900606b5..285ca9df4e0 100644 --- a/frontend/src/api/generated/services/sigNoz.schemas.ts +++ b/frontend/src/api/generated/services/sigNoz.schemas.ts @@ -425,19 +425,7 @@ export interface AuthtypesSessionContextDTO { orgs?: AuthtypesOrgSessionContextDTO[] | null; } -export interface AuthtypesTransactionDTO { - object: AuthtypesObjectDTO; - /** - * @type string - */ - relation: string; -} - -export interface AuthtypesUpdateableAuthDomainDTO { - config?: AuthtypesAuthDomainConfigDTO; -} - -export interface DashboardtypesDashboardDTO { +export interface AuthtypesStorableRoleDTO { /** * @type string * @format date-time @@ -446,1347 +434,1940 @@ export interface DashboardtypesDashboardDTO { /** * @type string */ - createdBy?: string; - data?: DashboardtypesStorableDashboardDataDTO; + description?: string; /** * @type string */ - id?: string; + id: string; /** - * @type boolean + * @type string */ - locked?: boolean; + name?: string; /** * @type string */ - org_id?: string; + orgId?: string; /** * @type string - * @format date-time */ - updatedAt?: Date; + type?: string; /** * @type string + * @format date-time */ - updatedBy?: string; + updatedAt?: Date; } -export interface DashboardtypesGettablePublicDasbhboardDTO { - /** - * @type string - */ - defaultTimeRange?: string; +export interface AuthtypesTransactionDTO { + object: AuthtypesObjectDTO; /** * @type string */ - publicPath?: string; - /** - * @type boolean - */ - timeRangeEnabled?: boolean; + relation: string; } -export interface DashboardtypesGettablePublicDashboardDataDTO { - dashboard?: DashboardtypesDashboardDTO; - publicDashboard?: DashboardtypesGettablePublicDasbhboardDTO; +export interface AuthtypesUpdateableAuthDomainDTO { + config?: AuthtypesAuthDomainConfigDTO; } -export interface DashboardtypesPostablePublicDashboardDTO { +export interface AuthtypesUserRoleDTO { /** * @type string + * @format date-time */ - defaultTimeRange?: string; - /** - * @type boolean - */ - timeRangeEnabled?: boolean; -} - -export interface DashboardtypesStorableDashboardDataDTO { - [key: string]: unknown; -} - -export interface DashboardtypesUpdatablePublicDashboardDTO { + createdAt?: Date; /** * @type string */ - defaultTimeRange?: string; + id: string; + role?: AuthtypesStorableRoleDTO; /** - * @type boolean + * @type string */ - timeRangeEnabled?: boolean; -} - -export interface ErrorsJSONDTO { + roleId?: string; /** * @type string + * @format date-time */ - code: string; + updatedAt?: Date; /** - * @type array + * @type string */ - errors?: ErrorsResponseerroradditionalDTO[]; + userId?: string; +} + +export interface AuthtypesUserWithRolesDTO { /** * @type string + * @format date-time */ - message: string; + createdAt?: Date; /** * @type string */ - url?: string; -} - -export interface ErrorsResponseerroradditionalDTO { + displayName?: string; /** * @type string */ - message?: string; -} - -/** - * @nullable - */ -export type FeaturetypesGettableFeatureDTOVariants = { - [key: string]: unknown; -} | null; - -export interface FeaturetypesGettableFeatureDTO { + email?: string; /** * @type string */ - defaultVariant?: string; + id: string; /** - * @type string + * @type boolean */ - description?: string; + isRoot?: boolean; /** * @type string */ - kind?: string; + orgId?: string; /** * @type string */ - name?: string; - resolvedValue?: unknown; + status?: string; /** * @type string + * @format date-time */ - stage?: string; + updatedAt?: Date; /** - * @type object + * @type array * @nullable true */ - variants?: FeaturetypesGettableFeatureDTOVariants; + userRoles?: AuthtypesUserRoleDTO[] | null; } -export interface GatewaytypesGettableCreatedIngestionKeyDTO { +export interface CloudintegrationtypesAWSAccountConfigDTO { /** - * @type string + * @type array */ - id: string; + regions: string[]; +} + +export type CloudintegrationtypesAWSCollectionStrategyDTOS3Buckets = { + [key: string]: string[]; +}; + +export interface CloudintegrationtypesAWSCollectionStrategyDTO { + aws_logs?: CloudintegrationtypesAWSLogsStrategyDTO; + aws_metrics?: CloudintegrationtypesAWSMetricsStrategyDTO; /** - * @type string + * @type object */ - value: string; + s3_buckets?: CloudintegrationtypesAWSCollectionStrategyDTOS3Buckets; } -export interface GatewaytypesGettableCreatedIngestionKeyLimitDTO { +export interface CloudintegrationtypesAWSConnectionArtifactDTO { /** * @type string */ - id: string; + connectionURL: string; } -export interface GatewaytypesGettableIngestionKeysDTO { - _pagination?: GatewaytypesPaginationDTO; +export interface CloudintegrationtypesAWSConnectionArtifactRequestDTO { + /** + * @type string + */ + deploymentRegion: string; /** * @type array - * @nullable true */ - keys?: GatewaytypesIngestionKeyDTO[] | null; + regions: string[]; } -export interface GatewaytypesIngestionKeyDTO { +export interface CloudintegrationtypesAWSIntegrationConfigDTO { /** - * @type string - * @format date-time + * @type array */ - created_at?: Date; + enabledRegions: string[]; + telemetry: CloudintegrationtypesAWSCollectionStrategyDTO; +} + +export type CloudintegrationtypesAWSLogsStrategyDTOCloudwatchLogsSubscriptionsItem = { /** * @type string - * @format date-time */ - expires_at?: Date; + filter_pattern?: string; /** * @type string */ - id?: string; + log_group_name_prefix?: string; +}; + +export interface CloudintegrationtypesAWSLogsStrategyDTO { /** * @type array * @nullable true */ - limits?: GatewaytypesLimitDTO[] | null; + cloudwatch_logs_subscriptions?: + | CloudintegrationtypesAWSLogsStrategyDTOCloudwatchLogsSubscriptionsItem[] + | null; +} + +export type CloudintegrationtypesAWSMetricsStrategyDTOCloudwatchMetricStreamFiltersItem = { + /** + * @type array + */ + MetricNames?: string[]; /** * @type string */ - name?: string; + Namespace?: string; +}; + +export interface CloudintegrationtypesAWSMetricsStrategyDTO { /** * @type array * @nullable true */ - tags?: string[] | null; + cloudwatch_metric_stream_filters?: + | CloudintegrationtypesAWSMetricsStrategyDTOCloudwatchMetricStreamFiltersItem[] + | null; +} + +export interface CloudintegrationtypesAWSServiceConfigDTO { + logs?: CloudintegrationtypesAWSServiceLogsConfigDTO; + metrics?: CloudintegrationtypesAWSServiceMetricsConfigDTO; +} + +export type CloudintegrationtypesAWSServiceLogsConfigDTOS3Buckets = { + [key: string]: string[]; +}; + +export interface CloudintegrationtypesAWSServiceLogsConfigDTO { /** - * @type string - * @format date-time + * @type boolean */ - updated_at?: Date; + enabled?: boolean; /** - * @type string + * @type object */ - value?: string; + s3_buckets?: CloudintegrationtypesAWSServiceLogsConfigDTOS3Buckets; +} + +export interface CloudintegrationtypesAWSServiceMetricsConfigDTO { /** - * @type string + * @type boolean */ - workspace_id?: string; + enabled?: boolean; } -export interface GatewaytypesLimitDTO { - config?: GatewaytypesLimitConfigDTO; +export interface CloudintegrationtypesAccountDTO { + agentReport: CloudintegrationtypesAgentReportDTO; + config: CloudintegrationtypesAccountConfigDTO; /** * @type string * @format date-time */ - created_at?: Date; + createdAt?: Date; /** * @type string */ - id?: string; + id: string; /** * @type string */ - key_id?: string; - metric?: GatewaytypesLimitMetricDTO; + orgId: string; /** * @type string */ - signal?: string; + provider: string; /** - * @type array + * @type string * @nullable true */ - tags?: string[] | null; + providerAccountId: string | null; /** * @type string * @format date-time + * @nullable true */ - updated_at?: Date; + removedAt: Date | null; + /** + * @type string + * @format date-time + */ + updatedAt?: Date; } -export interface GatewaytypesLimitConfigDTO { - day?: GatewaytypesLimitValueDTO; - second?: GatewaytypesLimitValueDTO; +export interface CloudintegrationtypesAccountConfigDTO { + aws: CloudintegrationtypesAWSAccountConfigDTO; } -export interface GatewaytypesLimitMetricDTO { - day?: GatewaytypesLimitMetricValueDTO; - second?: GatewaytypesLimitMetricValueDTO; -} +/** + * @nullable + */ +export type CloudintegrationtypesAgentReportDTOData = { + [key: string]: unknown; +} | null; -export interface GatewaytypesLimitMetricValueDTO { +/** + * @nullable + */ +export type CloudintegrationtypesAgentReportDTO = { /** - * @type integer - * @format int64 + * @type object + * @nullable true */ - count?: number; + data: CloudintegrationtypesAgentReportDTOData; /** * @type integer * @format int64 */ - size?: number; -} + timestampMillis: number; +} | null; -export interface GatewaytypesLimitValueDTO { - /** - * @type integer - * @nullable true - */ - count?: number | null; +export interface CloudintegrationtypesAssetsDTO { /** - * @type integer + * @type array * @nullable true */ - size?: number | null; + dashboards?: CloudintegrationtypesDashboardDTO[] | null; } -export interface GatewaytypesPaginationDTO { - /** - * @type integer - */ - page?: number; +export interface CloudintegrationtypesCollectedLogAttributeDTO { /** - * @type integer + * @type string */ - pages?: number; + name?: string; /** - * @type integer + * @type string */ - per_page?: number; + path?: string; /** - * @type integer + * @type string */ - total?: number; + type?: string; } -export interface GatewaytypesPostableIngestionKeyDTO { +export interface CloudintegrationtypesCollectedMetricDTO { /** * @type string - * @format date-time */ - expires_at?: Date; + description?: string; /** * @type string */ - name: string; - /** - * @type array - * @nullable true - */ - tags?: string[] | null; -} - -export interface GatewaytypesPostableIngestionKeyLimitDTO { - config?: GatewaytypesLimitConfigDTO; + name?: string; /** * @type string */ - signal?: string; + type?: string; /** - * @type array - * @nullable true + * @type string */ - tags?: string[] | null; + unit?: string; } -export interface GatewaytypesUpdatableIngestionKeyLimitDTO { - config: GatewaytypesLimitConfigDTO; - /** - * @type array - * @nullable true - */ - tags?: string[] | null; +export interface CloudintegrationtypesCollectionStrategyDTO { + aws: CloudintegrationtypesAWSCollectionStrategyDTO; } -export interface GlobaltypesAPIKeyConfigDTO { - /** - * @type boolean - */ - enabled?: boolean; +export interface CloudintegrationtypesConnectionArtifactDTO { + aws: CloudintegrationtypesAWSConnectionArtifactDTO; } -export interface GlobaltypesConfigDTO { +export interface CloudintegrationtypesConnectionArtifactRequestDTO { + aws: CloudintegrationtypesAWSConnectionArtifactRequestDTO; +} + +export interface CloudintegrationtypesDashboardDTO { + definition?: DashboardtypesStorableDashboardDataDTO; /** * @type string */ - external_url?: string; - identN?: GlobaltypesIdentNConfigDTO; + description?: string; /** * @type string */ - ingestion_url?: string; + id?: string; + /** + * @type string + */ + title?: string; } -export interface GlobaltypesIdentNConfigDTO { - apikey?: GlobaltypesAPIKeyConfigDTO; - impersonation?: GlobaltypesImpersonationConfigDTO; - tokenizer?: GlobaltypesTokenizerConfigDTO; +export interface CloudintegrationtypesDataCollectedDTO { + /** + * @type array + * @nullable true + */ + logs?: CloudintegrationtypesCollectedLogAttributeDTO[] | null; + /** + * @type array + * @nullable true + */ + metrics?: CloudintegrationtypesCollectedMetricDTO[] | null; } -export interface GlobaltypesImpersonationConfigDTO { +export interface CloudintegrationtypesGettableAccountWithArtifactDTO { + connectionArtifact: CloudintegrationtypesConnectionArtifactDTO; /** - * @type boolean + * @type string */ - enabled?: boolean; + id: string; } -export interface GlobaltypesTokenizerConfigDTO { +export interface CloudintegrationtypesGettableAccountsDTO { /** - * @type boolean + * @type array */ - enabled?: boolean; + accounts: CloudintegrationtypesAccountDTO[]; } -export interface MetricsexplorertypesListMetricDTO { +export interface CloudintegrationtypesGettableAgentCheckInResponseDTO { /** * @type string */ - description: string; + account_id: string; /** - * @type boolean + * @type string */ - isMonotonic: boolean; + cloud_account_id: string; /** * @type string */ - metricName: string; - temporality: MetrictypesTemporalityDTO; - type: MetrictypesTypeDTO; + cloudIntegrationId: string; + integration_config: CloudintegrationtypesIntegrationConfigDTO; + integrationConfig: CloudintegrationtypesProviderIntegrationConfigDTO; /** * @type string */ - unit: string; -} - -export interface MetricsexplorertypesListMetricsResponseDTO { + providerAccountId: string; /** - * @type array + * @type string + * @format date-time * @nullable true */ - metrics: MetricsexplorertypesListMetricDTO[] | null; -} - -export interface MetricsexplorertypesMetricAlertDTO { + removed_at: Date | null; /** * @type string + * @format date-time + * @nullable true */ - alertId: string; + removedAt: Date | null; +} + +export interface CloudintegrationtypesGettableServicesMetadataDTO { /** - * @type string + * @type array */ - alertName: string; + services: CloudintegrationtypesServiceMetadataDTO[]; } -export interface MetricsexplorertypesMetricAlertsResponseDTO { +/** + * @nullable + */ +export type CloudintegrationtypesIntegrationConfigDTO = { /** * @type array - * @nullable true */ - alerts: MetricsexplorertypesMetricAlertDTO[] | null; -} + enabled_regions: string[]; + telemetry: CloudintegrationtypesAWSCollectionStrategyDTO; +} | null; -export interface MetricsexplorertypesMetricAttributeDTO { +/** + * @nullable + */ +export type CloudintegrationtypesPostableAgentCheckInRequestDTOData = { + [key: string]: unknown; +} | null; + +export interface CloudintegrationtypesPostableAgentCheckInRequestDTO { /** * @type string */ - key: string; + account_id?: string; /** - * @type integer - * @minimum 0 + * @type string */ - valueCount: number; + cloud_account_id?: string; /** - * @type array - * @nullable true + * @type string */ - values: string[] | null; -} - -export interface MetricsexplorertypesMetricAttributesResponseDTO { + cloudIntegrationId?: string; /** - * @type array + * @type object * @nullable true */ - attributes: MetricsexplorertypesMetricAttributeDTO[] | null; + data: CloudintegrationtypesPostableAgentCheckInRequestDTOData; /** - * @type integer - * @format int64 + * @type string */ - totalKeys: number; + providerAccountId?: string; } -export interface MetricsexplorertypesMetricDashboardDTO { +export interface CloudintegrationtypesProviderIntegrationConfigDTO { + aws: CloudintegrationtypesAWSIntegrationConfigDTO; +} + +export interface CloudintegrationtypesServiceDTO { + assets: CloudintegrationtypesAssetsDTO; + dataCollected: CloudintegrationtypesDataCollectedDTO; /** * @type string */ - dashboardId: string; + icon: string; /** * @type string */ - dashboardName: string; + id: string; /** * @type string */ - widgetId: string; + overview: string; + serviceConfig?: CloudintegrationtypesServiceConfigDTO; + supported_signals: CloudintegrationtypesSupportedSignalsDTO; + telemetryCollectionStrategy: CloudintegrationtypesCollectionStrategyDTO; /** * @type string */ - widgetName: string; + title: string; } -export interface MetricsexplorertypesMetricDashboardsResponseDTO { - /** - * @type array - * @nullable true - */ - dashboards: MetricsexplorertypesMetricDashboardDTO[] | null; +export interface CloudintegrationtypesServiceConfigDTO { + aws: CloudintegrationtypesAWSServiceConfigDTO; } -export interface MetricsexplorertypesMetricHighlightsResponseDTO { +export interface CloudintegrationtypesServiceMetadataDTO { /** - * @type integer - * @minimum 0 + * @type boolean */ - activeTimeSeries: number; + enabled: boolean; /** - * @type integer - * @minimum 0 + * @type string */ - dataPoints: number; + icon: string; /** - * @type integer - * @minimum 0 + * @type string */ - lastReceived: number; + id: string; /** - * @type integer - * @minimum 0 + * @type string */ - totalTimeSeries: number; + title: string; } -export interface MetricsexplorertypesMetricMetadataDTO { +export interface CloudintegrationtypesSupportedSignalsDTO { /** - * @type string + * @type boolean */ - description: string; + logs?: boolean; /** * @type boolean */ - isMonotonic: boolean; - temporality: MetrictypesTemporalityDTO; - type: MetrictypesTypeDTO; + metrics?: boolean; +} + +export interface CloudintegrationtypesUpdatableAccountDTO { + config: CloudintegrationtypesAccountConfigDTO; +} + +export interface CloudintegrationtypesUpdatableServiceDTO { + config: CloudintegrationtypesServiceConfigDTO; +} + +export interface DashboardtypesDashboardDTO { /** * @type string + * @format date-time */ - unit: string; -} - -export interface MetricsexplorertypesStatDTO { + createdAt?: Date; /** * @type string */ - description: string; + createdBy?: string; + data?: DashboardtypesStorableDashboardDataDTO; /** * @type string */ - metricName: string; + id?: string; /** - * @type integer - * @minimum 0 + * @type boolean */ - samples: number; + locked?: boolean; /** - * @type integer - * @minimum 0 + * @type string */ - timeseries: number; - type: MetrictypesTypeDTO; + org_id?: string; /** * @type string + * @format date-time */ - unit: string; + updatedAt?: Date; + /** + * @type string + */ + updatedBy?: string; } -export interface MetricsexplorertypesStatsRequestDTO { +export interface DashboardtypesGettablePublicDasbhboardDTO { /** - * @type integer - * @format int64 + * @type string */ - end: number; - filter?: Querybuildertypesv5FilterDTO; + defaultTimeRange?: string; /** - * @type integer + * @type string */ - limit: number; + publicPath?: string; /** - * @type integer + * @type boolean */ - offset?: number; - orderBy?: Querybuildertypesv5OrderByDTO; + timeRangeEnabled?: boolean; +} + +export interface DashboardtypesGettablePublicDashboardDataDTO { + dashboard?: DashboardtypesDashboardDTO; + publicDashboard?: DashboardtypesGettablePublicDasbhboardDTO; +} + +export interface DashboardtypesPostablePublicDashboardDTO { /** - * @type integer - * @format int64 + * @type string */ - start: number; + defaultTimeRange?: string; + /** + * @type boolean + */ + timeRangeEnabled?: boolean; } -export interface MetricsexplorertypesStatsResponseDTO { +export interface DashboardtypesStorableDashboardDataDTO { + [key: string]: unknown; +} + +export interface DashboardtypesUpdatablePublicDashboardDTO { /** - * @type array - * @nullable true + * @type string */ - metrics: MetricsexplorertypesStatDTO[] | null; + defaultTimeRange?: string; /** - * @type integer - * @minimum 0 + * @type boolean */ - total: number; + timeRangeEnabled?: boolean; } -export interface MetricsexplorertypesTreemapEntryDTO { +export interface ErrorsJSONDTO { /** * @type string */ - metricName: string; + code: string; /** - * @type number - * @format double + * @type array */ - percentage: number; + errors?: ErrorsResponseerroradditionalDTO[]; /** - * @type integer - * @minimum 0 - */ - totalValue: number; -} - -export enum MetricsexplorertypesTreemapModeDTO { - timeseries = 'timeseries', - samples = 'samples', -} -export interface MetricsexplorertypesTreemapRequestDTO { - /** - * @type integer - * @format int64 + * @type string */ - end: number; - filter?: Querybuildertypesv5FilterDTO; + message: string; /** - * @type integer + * @type string */ - limit: number; - mode: MetricsexplorertypesTreemapModeDTO; + url?: string; +} + +export interface ErrorsResponseerroradditionalDTO { /** - * @type integer - * @format int64 + * @type string */ - start: number; + message?: string; } -export interface MetricsexplorertypesTreemapResponseDTO { +/** + * @nullable + */ +export type FactoryResponseDTOServices = { [key: string]: string[] } | null; + +export interface FactoryResponseDTO { /** - * @type array - * @nullable true + * @type boolean */ - samples: MetricsexplorertypesTreemapEntryDTO[] | null; + healthy?: boolean; /** - * @type array + * @type object * @nullable true */ - timeseries: MetricsexplorertypesTreemapEntryDTO[] | null; + services?: FactoryResponseDTOServices; } -export interface MetricsexplorertypesUpdateMetricMetadataRequestDTO { +/** + * @nullable + */ +export type FeaturetypesGettableFeatureDTOVariants = { + [key: string]: unknown; +} | null; + +export interface FeaturetypesGettableFeatureDTO { /** * @type string */ - description: string; + defaultVariant?: string; /** - * @type boolean + * @type string */ - isMonotonic: boolean; + description?: string; /** * @type string */ - metricName: string; - temporality: MetrictypesTemporalityDTO; - type: MetrictypesTypeDTO; + kind?: string; /** * @type string */ - unit: string; + name?: string; + resolvedValue?: unknown; + /** + * @type string + */ + stage?: string; + /** + * @type object + * @nullable true + */ + variants?: FeaturetypesGettableFeatureDTOVariants; } -export interface MetrictypesComparisonSpaceAggregationParamDTO { +export interface GatewaytypesGettableCreatedIngestionKeyDTO { /** * @type string */ - operator: string; + id: string; /** - * @type number - * @format double + * @type string */ - threshold: number; + value: string; } -export enum MetrictypesSpaceAggregationDTO { - sum = 'sum', - avg = 'avg', - min = 'min', - max = 'max', - count = 'count', - p50 = 'p50', - p75 = 'p75', - p90 = 'p90', - p95 = 'p95', - p99 = 'p99', -} -export enum MetrictypesTemporalityDTO { - delta = 'delta', - cumulative = 'cumulative', - unspecified = 'unspecified', -} -export enum MetrictypesTimeAggregationDTO { - latest = 'latest', - sum = 'sum', - avg = 'avg', - min = 'min', - max = 'max', - count = 'count', - count_distinct = 'count_distinct', - rate = 'rate', - increase = 'increase', -} -export enum MetrictypesTypeDTO { - gauge = 'gauge', - sum = 'sum', - histogram = 'histogram', - summary = 'summary', - exponentialhistogram = 'exponentialhistogram', -} -export interface PreferencetypesPreferenceDTO { +export interface GatewaytypesGettableCreatedIngestionKeyLimitDTO { /** - * @type array - * @nullable true + * @type string */ - allowedScopes?: string[] | null; + id: string; +} + +export interface GatewaytypesGettableIngestionKeysDTO { + _pagination?: GatewaytypesPaginationDTO; /** * @type array * @nullable true */ - allowedValues?: string[] | null; - defaultValue?: PreferencetypesValueDTO; + keys?: GatewaytypesIngestionKeyDTO[] | null; +} + +export interface GatewaytypesIngestionKeyDTO { /** * @type string + * @format date-time */ - description?: string; + created_at?: Date; /** * @type string + * @format date-time */ - name?: string; - value?: PreferencetypesValueDTO; + expires_at?: Date; /** * @type string */ - valueType?: string; -} - -export interface PreferencetypesUpdatablePreferenceDTO { - value?: unknown; -} - -export interface PreferencetypesValueDTO { - [key: string]: unknown; -} - -export interface PromotetypesPromotePathDTO { + id?: string; /** * @type array + * @nullable true */ - indexes?: PromotetypesWrappedIndexDTO[]; + limits?: GatewaytypesLimitDTO[] | null; /** * @type string */ - path?: string; + name?: string; /** - * @type boolean + * @type array + * @nullable true */ - promote?: boolean; -} - -export interface PromotetypesWrappedIndexDTO { + tags?: string[] | null; /** * @type string + * @format date-time */ - column_type?: string; + updated_at?: Date; /** - * @type integer + * @type string */ - granularity?: number; + value?: string; /** * @type string */ - type?: string; + workspace_id?: string; } -export type Querybuildertypesv5AggregationBucketDTOMeta = { +export interface GatewaytypesLimitDTO { + config?: GatewaytypesLimitConfigDTO; /** * @type string + * @format date-time */ - unit?: string; -}; - -export interface Querybuildertypesv5AggregationBucketDTO { + created_at?: Date; /** * @type string */ - alias?: string; - /** - * @type array - */ - anomalyScores?: Querybuildertypesv5TimeSeriesDTO[]; - /** - * @type integer - */ - index?: number; - /** - * @type array - */ - lowerBoundSeries?: Querybuildertypesv5TimeSeriesDTO[]; + id?: string; /** - * @type object + * @type string */ - meta?: Querybuildertypesv5AggregationBucketDTOMeta; + key_id?: string; + metric?: GatewaytypesLimitMetricDTO; /** - * @type array + * @type string */ - predictedSeries?: Querybuildertypesv5TimeSeriesDTO[]; + signal?: string; /** * @type array * @nullable true */ - series?: Querybuildertypesv5TimeSeriesDTO[] | null; + tags?: string[] | null; /** - * @type array + * @type string + * @format date-time */ - upperBoundSeries?: Querybuildertypesv5TimeSeriesDTO[]; + updated_at?: Date; } -export interface Querybuildertypesv5BucketDTO { - /** - * @type number - * @format double - */ - step?: number; +export interface GatewaytypesLimitConfigDTO { + day?: GatewaytypesLimitValueDTO; + second?: GatewaytypesLimitValueDTO; } -export interface Querybuildertypesv5ClickHouseQueryDTO { +export interface GatewaytypesLimitMetricDTO { + day?: GatewaytypesLimitMetricValueDTO; + second?: GatewaytypesLimitMetricValueDTO; +} + +export interface GatewaytypesLimitMetricValueDTO { /** - * @type boolean + * @type integer + * @format int64 */ - disabled?: boolean; + count?: number; /** - * @type string + * @type integer + * @format int64 */ - legend?: string; + size?: number; +} + +export interface GatewaytypesLimitValueDTO { /** - * @type string + * @type integer + * @nullable true */ - name?: string; + count?: number | null; /** - * @type string + * @type integer + * @nullable true */ - query?: string; + size?: number | null; } -export type Querybuildertypesv5ColumnDescriptorDTOMeta = { +export interface GatewaytypesPaginationDTO { /** - * @type string + * @type integer */ - unit?: string; -}; - -export interface Querybuildertypesv5ColumnDescriptorDTO { + page?: number; /** * @type integer - * @format int64 */ - aggregationIndex?: number; - columnType?: Querybuildertypesv5ColumnTypeDTO; + pages?: number; /** - * @type string + * @type integer */ - description?: string; - fieldContext?: TelemetrytypesFieldContextDTO; - fieldDataType?: TelemetrytypesFieldDataTypeDTO; + per_page?: number; /** - * @type object + * @type integer */ - meta?: Querybuildertypesv5ColumnDescriptorDTOMeta; + total?: number; +} + +export interface GatewaytypesPostableIngestionKeyDTO { /** * @type string + * @format date-time */ - name: string; + expires_at?: Date; /** * @type string */ - queryName?: string; - signal?: TelemetrytypesSignalDTO; + name: string; /** - * @type string + * @type array + * @nullable true */ - unit?: string; + tags?: string[] | null; } -export enum Querybuildertypesv5ColumnTypeDTO { - group = 'group', - aggregation = 'aggregation', -} -/** - * Composite query containing one or more query envelopes. Each query envelope specifies its type and corresponding spec. - */ -export interface Querybuildertypesv5CompositeQueryDTO { +export interface GatewaytypesPostableIngestionKeyLimitDTO { + config?: GatewaytypesLimitConfigDTO; + /** + * @type string + */ + signal?: string; /** * @type array * @nullable true */ - queries?: Querybuildertypesv5QueryEnvelopeDTO[] | null; + tags?: string[] | null; } -export type Querybuildertypesv5ExecStatsDTOStepIntervals = { - [key: string]: number; -}; - -/** - * Execution statistics for the query, including rows scanned, bytes scanned, and duration. - */ -export interface Querybuildertypesv5ExecStatsDTO { +export interface GatewaytypesUpdatableIngestionKeyLimitDTO { + config: GatewaytypesLimitConfigDTO; /** - * @type integer - * @minimum 0 + * @type array + * @nullable true */ - bytesScanned?: number; + tags?: string[] | null; +} + +export interface GlobaltypesAPIKeyConfigDTO { /** - * @type integer - * @minimum 0 + * @type boolean */ - durationMs?: number; + enabled?: boolean; +} + +export interface GlobaltypesConfigDTO { /** - * @type integer - * @minimum 0 + * @type string */ - rowsScanned?: number; + external_url?: string; + identN?: GlobaltypesIdentNConfigDTO; /** - * @type object + * @type string */ - stepIntervals?: Querybuildertypesv5ExecStatsDTOStepIntervals; + ingestion_url?: string; } -export interface Querybuildertypesv5FilterDTO { - /** - * @type string - */ - expression?: string; +export interface GlobaltypesIdentNConfigDTO { + apikey?: GlobaltypesAPIKeyConfigDTO; + impersonation?: GlobaltypesImpersonationConfigDTO; + tokenizer?: GlobaltypesTokenizerConfigDTO; } -export interface Querybuildertypesv5FormatOptionsDTO { +export interface GlobaltypesImpersonationConfigDTO { /** * @type boolean */ - fillGaps?: boolean; + enabled?: boolean; +} + +export interface GlobaltypesTokenizerConfigDTO { /** * @type boolean */ - formatTableResultForUI?: boolean; + enabled?: boolean; } -export interface Querybuildertypesv5FunctionDTO { +export interface MetricsexplorertypesInspectMetricsRequestDTO { /** - * @type array + * @type integer + * @format int64 */ - args?: Querybuildertypesv5FunctionArgDTO[]; - name?: Querybuildertypesv5FunctionNameDTO; -} - -export interface Querybuildertypesv5FunctionArgDTO { + end: number; + filter?: Querybuildertypesv5FilterDTO; /** * @type string */ - name?: string; - value?: unknown; + metricName: string; + /** + * @type integer + * @format int64 + */ + start: number; } -export enum Querybuildertypesv5FunctionNameDTO { - cutoffmin = 'cutoffmin', - cutoffmax = 'cutoffmax', - clampmin = 'clampmin', - clampmax = 'clampmax', - absolute = 'absolute', - runningdiff = 'runningdiff', - log2 = 'log2', - log10 = 'log10', - cumulativesum = 'cumulativesum', - ewma3 = 'ewma3', - ewma5 = 'ewma5', - ewma7 = 'ewma7', - median3 = 'median3', - median5 = 'median5', - median7 = 'median7', - timeshift = 'timeshift', - anomaly = 'anomaly', - fillzero = 'fillzero', +export interface MetricsexplorertypesInspectMetricsResponseDTO { + /** + * @type array + * @nullable true + */ + series: Querybuildertypesv5TimeSeriesDTO[] | null; } -export interface Querybuildertypesv5GroupByKeyDTO { + +export interface MetricsexplorertypesListMetricDTO { /** * @type string */ - description?: string; - fieldContext?: TelemetrytypesFieldContextDTO; - fieldDataType?: TelemetrytypesFieldDataTypeDTO; + description: string; /** - * @type string + * @type boolean */ - name: string; - signal?: TelemetrytypesSignalDTO; + isMonotonic: boolean; /** * @type string */ - unit?: string; -} - -export interface Querybuildertypesv5HavingDTO { + metricName: string; + temporality: MetrictypesTemporalityDTO; + type: MetrictypesTypeDTO; /** * @type string */ - expression?: string; -} - -export interface Querybuildertypesv5LabelDTO { - key?: TelemetrytypesTelemetryFieldKeyDTO; - value?: unknown; + unit: string; } -export interface Querybuildertypesv5LimitByDTO { +export interface MetricsexplorertypesListMetricsResponseDTO { /** * @type array * @nullable true */ - keys?: string[] | null; - /** - * @type string - */ - value?: string; + metrics: MetricsexplorertypesListMetricDTO[] | null; } -export interface Querybuildertypesv5LogAggregationDTO { +export interface MetricsexplorertypesMetricAlertDTO { /** * @type string */ - alias?: string; + alertId: string; /** * @type string */ - expression?: string; + alertName: string; } -export interface Querybuildertypesv5MetricAggregationDTO { - comparisonSpaceAggregationParam?: MetrictypesComparisonSpaceAggregationParamDTO; +export interface MetricsexplorertypesMetricAlertsResponseDTO { /** - * @type string + * @type array + * @nullable true */ - metricName?: string; - reduceTo?: Querybuildertypesv5ReduceToDTO; - spaceAggregation?: MetrictypesSpaceAggregationDTO; - temporality?: MetrictypesTemporalityDTO; - timeAggregation?: MetrictypesTimeAggregationDTO; -} - -export interface Querybuildertypesv5OrderByDTO { - direction?: Querybuildertypesv5OrderDirectionDTO; - key?: Querybuildertypesv5OrderByKeyDTO; + alerts: MetricsexplorertypesMetricAlertDTO[] | null; } -export interface Querybuildertypesv5OrderByKeyDTO { +export interface MetricsexplorertypesMetricAttributeDTO { /** * @type string */ - description?: string; - fieldContext?: TelemetrytypesFieldContextDTO; - fieldDataType?: TelemetrytypesFieldDataTypeDTO; + key: string; /** - * @type string + * @type integer + * @minimum 0 */ - name: string; - signal?: TelemetrytypesSignalDTO; + valueCount: number; /** - * @type string + * @type array + * @nullable true */ - unit?: string; + values: string[] | null; } -export enum Querybuildertypesv5OrderDirectionDTO { - asc = 'asc', - desc = 'desc', -} -export interface Querybuildertypesv5PromQueryDTO { +export interface MetricsexplorertypesMetricAttributesResponseDTO { /** - * @type boolean + * @type array + * @nullable true */ - disabled?: boolean; + attributes: MetricsexplorertypesMetricAttributeDTO[] | null; + /** + * @type integer + * @format int64 + */ + totalKeys: number; +} + +export interface MetricsexplorertypesMetricDashboardDTO { /** * @type string */ - legend?: string; + dashboardId: string; /** * @type string */ - name?: string; + dashboardName: string; /** * @type string */ - query?: string; + widgetId: string; /** - * @type boolean + * @type string */ - stats?: boolean; - step?: Querybuildertypesv5StepDTO; + widgetName: string; } -export interface Querybuildertypesv5QueryBuilderFormulaDTO { - /** - * @type boolean - */ - disabled?: boolean; - /** - * @type string - */ - expression?: string; +export interface MetricsexplorertypesMetricDashboardsResponseDTO { /** * @type array + * @nullable true */ - functions?: Querybuildertypesv5FunctionDTO[]; - having?: Querybuildertypesv5HavingDTO; + dashboards: MetricsexplorertypesMetricDashboardDTO[] | null; +} + +export interface MetricsexplorertypesMetricHighlightsResponseDTO { /** - * @type string + * @type integer + * @minimum 0 */ - legend?: string; + activeTimeSeries: number; /** * @type integer + * @minimum 0 */ - limit?: number; + dataPoints: number; /** - * @type string + * @type integer + * @minimum 0 */ - name?: string; + lastReceived: number; /** - * @type array + * @type integer + * @minimum 0 */ - order?: Querybuildertypesv5OrderByDTO[]; + totalTimeSeries: number; } -export interface Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5LogAggregationDTO { - /** - * @type array - */ - aggregations?: Querybuildertypesv5LogAggregationDTO[]; +export interface MetricsexplorertypesMetricMetadataDTO { /** * @type string */ - cursor?: string; + description: string; /** * @type boolean */ - disabled?: boolean; - filter?: Querybuildertypesv5FilterDTO; + isMonotonic: boolean; + temporality: MetrictypesTemporalityDTO; + type: MetrictypesTypeDTO; /** - * @type array + * @type string */ - functions?: Querybuildertypesv5FunctionDTO[]; + unit: string; +} + +export interface MetricsexplorertypesMetricsOnboardingResponseDTO { /** - * @type array + * @type boolean */ - groupBy?: Querybuildertypesv5GroupByKeyDTO[]; - having?: Querybuildertypesv5HavingDTO; + hasMetrics: boolean; +} + +export interface MetricsexplorertypesStatDTO { /** * @type string */ - legend?: string; - /** - * @type integer - */ - limit?: number; - limitBy?: Querybuildertypesv5LimitByDTO; + description: string; /** * @type string */ - name?: string; + metricName: string; /** * @type integer + * @minimum 0 */ - offset?: number; - /** - * @type array - */ - order?: Querybuildertypesv5OrderByDTO[]; + samples: number; /** - * @type array + * @type integer + * @minimum 0 */ - secondaryAggregations?: Querybuildertypesv5SecondaryAggregationDTO[]; + timeseries: number; + type: MetrictypesTypeDTO; /** - * @type array + * @type string */ - selectFields?: TelemetrytypesTelemetryFieldKeyDTO[]; - signal?: TelemetrytypesSignalDTO; - source?: TelemetrytypesSourceDTO; - stepInterval?: Querybuildertypesv5StepDTO; + unit: string; } -export interface Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5MetricAggregationDTO { - /** - * @type array - */ - aggregations?: Querybuildertypesv5MetricAggregationDTO[]; - /** - * @type string - */ - cursor?: string; +export interface MetricsexplorertypesStatsRequestDTO { /** - * @type boolean + * @type integer + * @format int64 */ - disabled?: boolean; + end: number; filter?: Querybuildertypesv5FilterDTO; - /** - * @type array - */ - functions?: Querybuildertypesv5FunctionDTO[]; - /** - * @type array - */ - groupBy?: Querybuildertypesv5GroupByKeyDTO[]; - having?: Querybuildertypesv5HavingDTO; - /** - * @type string - */ - legend?: string; /** * @type integer */ - limit?: number; - limitBy?: Querybuildertypesv5LimitByDTO; - /** - * @type string - */ - name?: string; + limit: number; /** * @type integer */ offset?: number; + orderBy?: Querybuildertypesv5OrderByDTO; /** - * @type array + * @type integer + * @format int64 */ - order?: Querybuildertypesv5OrderByDTO[]; + start: number; +} + +export interface MetricsexplorertypesStatsResponseDTO { /** * @type array + * @nullable true */ - secondaryAggregations?: Querybuildertypesv5SecondaryAggregationDTO[]; + metrics: MetricsexplorertypesStatDTO[] | null; /** - * @type array + * @type integer + * @minimum 0 */ - selectFields?: TelemetrytypesTelemetryFieldKeyDTO[]; - signal?: TelemetrytypesSignalDTO; - source?: TelemetrytypesSourceDTO; - stepInterval?: Querybuildertypesv5StepDTO; + total: number; } -export interface Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5TraceAggregationDTO { - /** - * @type array - */ - aggregations?: Querybuildertypesv5TraceAggregationDTO[]; +export interface MetricsexplorertypesTreemapEntryDTO { /** * @type string */ - cursor?: string; - /** - * @type boolean - */ - disabled?: boolean; - filter?: Querybuildertypesv5FilterDTO; - /** - * @type array - */ - functions?: Querybuildertypesv5FunctionDTO[]; - /** - * @type array - */ - groupBy?: Querybuildertypesv5GroupByKeyDTO[]; - having?: Querybuildertypesv5HavingDTO; + metricName: string; /** - * @type string + * @type number + * @format double */ - legend?: string; + percentage: number; /** * @type integer + * @minimum 0 */ - limit?: number; - limitBy?: Querybuildertypesv5LimitByDTO; + totalValue: number; +} + +export enum MetricsexplorertypesTreemapModeDTO { + timeseries = 'timeseries', + samples = 'samples', +} +export interface MetricsexplorertypesTreemapRequestDTO { /** - * @type string + * @type integer + * @format int64 */ - name?: string; + end: number; + filter?: Querybuildertypesv5FilterDTO; /** * @type integer */ - offset?: number; + limit: number; + mode: MetricsexplorertypesTreemapModeDTO; /** - * @type array + * @type integer + * @format int64 */ - order?: Querybuildertypesv5OrderByDTO[]; + start: number; +} + +export interface MetricsexplorertypesTreemapResponseDTO { /** * @type array + * @nullable true */ - secondaryAggregations?: Querybuildertypesv5SecondaryAggregationDTO[]; + samples: MetricsexplorertypesTreemapEntryDTO[] | null; /** * @type array + * @nullable true */ - selectFields?: TelemetrytypesTelemetryFieldKeyDTO[]; - signal?: TelemetrytypesSignalDTO; - source?: TelemetrytypesSourceDTO; - stepInterval?: Querybuildertypesv5StepDTO; + timeseries: MetricsexplorertypesTreemapEntryDTO[] | null; } -export interface Querybuildertypesv5QueryBuilderTraceOperatorDTO { - /** - * @type array - */ - aggregations?: Querybuildertypesv5TraceAggregationDTO[]; +export interface MetricsexplorertypesUpdateMetricMetadataRequestDTO { /** * @type string */ - cursor?: string; + description: string; /** * @type boolean */ - disabled?: boolean; - /** - * @type string - */ - expression?: string; - filter?: Querybuildertypesv5FilterDTO; - /** - * @type array - */ - functions?: Querybuildertypesv5FunctionDTO[]; - /** - * @type array - */ - groupBy?: Querybuildertypesv5GroupByKeyDTO[]; - having?: Querybuildertypesv5HavingDTO; + isMonotonic: boolean; /** * @type string */ - legend?: string; - /** - * @type integer - */ - limit?: number; + metricName: string; + temporality: MetrictypesTemporalityDTO; + type: MetrictypesTypeDTO; /** * @type string */ - name?: string; - /** - * @type integer - */ - offset?: number; - /** - * @type array - */ - order?: Querybuildertypesv5OrderByDTO[]; + unit: string; +} + +export interface MetrictypesComparisonSpaceAggregationParamDTO { /** * @type string */ - returnSpansFrom?: string; + operator: string; /** - * @type array + * @type number + * @format double + */ + threshold: number; +} + +export enum MetrictypesSpaceAggregationDTO { + sum = 'sum', + avg = 'avg', + min = 'min', + max = 'max', + count = 'count', + p50 = 'p50', + p75 = 'p75', + p90 = 'p90', + p95 = 'p95', + p99 = 'p99', +} +export enum MetrictypesTemporalityDTO { + delta = 'delta', + cumulative = 'cumulative', + unspecified = 'unspecified', +} +export enum MetrictypesTimeAggregationDTO { + latest = 'latest', + sum = 'sum', + avg = 'avg', + min = 'min', + max = 'max', + count = 'count', + count_distinct = 'count_distinct', + rate = 'rate', + increase = 'increase', +} +export enum MetrictypesTypeDTO { + gauge = 'gauge', + sum = 'sum', + histogram = 'histogram', + summary = 'summary', + exponentialhistogram = 'exponentialhistogram', +} +export interface PreferencetypesPreferenceDTO { + /** + * @type array + * @nullable true + */ + allowedScopes?: string[] | null; + /** + * @type array + * @nullable true + */ + allowedValues?: string[] | null; + defaultValue?: PreferencetypesValueDTO; + /** + * @type string + */ + description?: string; + /** + * @type string + */ + name?: string; + value?: PreferencetypesValueDTO; + /** + * @type string + */ + valueType?: string; +} + +export interface PreferencetypesUpdatablePreferenceDTO { + value?: unknown; +} + +export interface PreferencetypesValueDTO { + [key: string]: unknown; +} + +export interface PromotetypesPromotePathDTO { + /** + * @type array + */ + indexes?: PromotetypesWrappedIndexDTO[]; + /** + * @type string + */ + path?: string; + /** + * @type boolean + */ + promote?: boolean; +} + +export interface PromotetypesWrappedIndexDTO { + /** + * @type string + */ + column_type?: string; + /** + * @type integer + */ + granularity?: number; + /** + * @type string + */ + type?: string; +} + +export type Querybuildertypesv5AggregationBucketDTOMeta = { + /** + * @type string + */ + unit?: string; +}; + +export interface Querybuildertypesv5AggregationBucketDTO { + /** + * @type string + */ + alias?: string; + /** + * @type array + */ + anomalyScores?: Querybuildertypesv5TimeSeriesDTO[]; + /** + * @type integer + */ + index?: number; + /** + * @type array + */ + lowerBoundSeries?: Querybuildertypesv5TimeSeriesDTO[]; + /** + * @type object + */ + meta?: Querybuildertypesv5AggregationBucketDTOMeta; + /** + * @type array + */ + predictedSeries?: Querybuildertypesv5TimeSeriesDTO[]; + /** + * @type array + * @nullable true + */ + series?: Querybuildertypesv5TimeSeriesDTO[] | null; + /** + * @type array + */ + upperBoundSeries?: Querybuildertypesv5TimeSeriesDTO[]; +} + +export interface Querybuildertypesv5BucketDTO { + /** + * @type number + * @format double + */ + step?: number; +} + +export interface Querybuildertypesv5ClickHouseQueryDTO { + /** + * @type boolean + */ + disabled?: boolean; + /** + * @type string + */ + legend?: string; + /** + * @type string + */ + name?: string; + /** + * @type string + */ + query?: string; +} + +export type Querybuildertypesv5ColumnDescriptorDTOMeta = { + /** + * @type string + */ + unit?: string; +}; + +export interface Querybuildertypesv5ColumnDescriptorDTO { + /** + * @type integer + * @format int64 + */ + aggregationIndex?: number; + columnType?: Querybuildertypesv5ColumnTypeDTO; + /** + * @type string + */ + description?: string; + fieldContext?: TelemetrytypesFieldContextDTO; + fieldDataType?: TelemetrytypesFieldDataTypeDTO; + /** + * @type object + */ + meta?: Querybuildertypesv5ColumnDescriptorDTOMeta; + /** + * @type string + */ + name: string; + /** + * @type string + */ + queryName?: string; + signal?: TelemetrytypesSignalDTO; + /** + * @type string + */ + unit?: string; +} + +export enum Querybuildertypesv5ColumnTypeDTO { + group = 'group', + aggregation = 'aggregation', +} +/** + * Composite query containing one or more query envelopes. Each query envelope specifies its type and corresponding spec. + */ +export interface Querybuildertypesv5CompositeQueryDTO { + /** + * @type array + * @nullable true + */ + queries?: Querybuildertypesv5QueryEnvelopeDTO[] | null; +} + +export type Querybuildertypesv5ExecStatsDTOStepIntervals = { + [key: string]: number; +}; + +/** + * Execution statistics for the query, including rows scanned, bytes scanned, and duration. + */ +export interface Querybuildertypesv5ExecStatsDTO { + /** + * @type integer + * @minimum 0 + */ + bytesScanned?: number; + /** + * @type integer + * @minimum 0 + */ + durationMs?: number; + /** + * @type integer + * @minimum 0 + */ + rowsScanned?: number; + /** + * @type object + */ + stepIntervals?: Querybuildertypesv5ExecStatsDTOStepIntervals; +} + +export interface Querybuildertypesv5FilterDTO { + /** + * @type string + */ + expression?: string; +} + +export interface Querybuildertypesv5FormatOptionsDTO { + /** + * @type boolean + */ + fillGaps?: boolean; + /** + * @type boolean + */ + formatTableResultForUI?: boolean; +} + +export interface Querybuildertypesv5FunctionDTO { + /** + * @type array + */ + args?: Querybuildertypesv5FunctionArgDTO[]; + name?: Querybuildertypesv5FunctionNameDTO; +} + +export interface Querybuildertypesv5FunctionArgDTO { + /** + * @type string + */ + name?: string; + value?: unknown; +} + +export enum Querybuildertypesv5FunctionNameDTO { + cutoffmin = 'cutoffmin', + cutoffmax = 'cutoffmax', + clampmin = 'clampmin', + clampmax = 'clampmax', + absolute = 'absolute', + runningdiff = 'runningdiff', + log2 = 'log2', + log10 = 'log10', + cumulativesum = 'cumulativesum', + ewma3 = 'ewma3', + ewma5 = 'ewma5', + ewma7 = 'ewma7', + median3 = 'median3', + median5 = 'median5', + median7 = 'median7', + timeshift = 'timeshift', + anomaly = 'anomaly', + fillzero = 'fillzero', +} +export interface Querybuildertypesv5GroupByKeyDTO { + /** + * @type string + */ + description?: string; + fieldContext?: TelemetrytypesFieldContextDTO; + fieldDataType?: TelemetrytypesFieldDataTypeDTO; + /** + * @type string + */ + name: string; + signal?: TelemetrytypesSignalDTO; + /** + * @type string + */ + unit?: string; +} + +export interface Querybuildertypesv5HavingDTO { + /** + * @type string + */ + expression?: string; +} + +export interface Querybuildertypesv5LabelDTO { + key?: TelemetrytypesTelemetryFieldKeyDTO; + value?: unknown; +} + +export interface Querybuildertypesv5LimitByDTO { + /** + * @type array + * @nullable true + */ + keys?: string[] | null; + /** + * @type string + */ + value?: string; +} + +export interface Querybuildertypesv5LogAggregationDTO { + /** + * @type string + */ + alias?: string; + /** + * @type string + */ + expression?: string; +} + +export interface Querybuildertypesv5MetricAggregationDTO { + comparisonSpaceAggregationParam?: MetrictypesComparisonSpaceAggregationParamDTO; + /** + * @type string + */ + metricName?: string; + reduceTo?: Querybuildertypesv5ReduceToDTO; + spaceAggregation?: MetrictypesSpaceAggregationDTO; + temporality?: MetrictypesTemporalityDTO; + timeAggregation?: MetrictypesTimeAggregationDTO; +} + +export interface Querybuildertypesv5OrderByDTO { + direction?: Querybuildertypesv5OrderDirectionDTO; + key?: Querybuildertypesv5OrderByKeyDTO; +} + +export interface Querybuildertypesv5OrderByKeyDTO { + /** + * @type string + */ + description?: string; + fieldContext?: TelemetrytypesFieldContextDTO; + fieldDataType?: TelemetrytypesFieldDataTypeDTO; + /** + * @type string + */ + name: string; + signal?: TelemetrytypesSignalDTO; + /** + * @type string + */ + unit?: string; +} + +export enum Querybuildertypesv5OrderDirectionDTO { + asc = 'asc', + desc = 'desc', +} +export interface Querybuildertypesv5PromQueryDTO { + /** + * @type boolean + */ + disabled?: boolean; + /** + * @type string + */ + legend?: string; + /** + * @type string + */ + name?: string; + /** + * @type string + */ + query?: string; + /** + * @type boolean + */ + stats?: boolean; + step?: Querybuildertypesv5StepDTO; +} + +export interface Querybuildertypesv5QueryBuilderFormulaDTO { + /** + * @type boolean + */ + disabled?: boolean; + /** + * @type string + */ + expression?: string; + /** + * @type array + */ + functions?: Querybuildertypesv5FunctionDTO[]; + having?: Querybuildertypesv5HavingDTO; + /** + * @type string + */ + legend?: string; + /** + * @type integer + */ + limit?: number; + /** + * @type string + */ + name?: string; + /** + * @type array + */ + order?: Querybuildertypesv5OrderByDTO[]; +} + +export interface Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5LogAggregationDTO { + /** + * @type array + */ + aggregations?: Querybuildertypesv5LogAggregationDTO[]; + /** + * @type string + */ + cursor?: string; + /** + * @type boolean + */ + disabled?: boolean; + filter?: Querybuildertypesv5FilterDTO; + /** + * @type array + */ + functions?: Querybuildertypesv5FunctionDTO[]; + /** + * @type array + */ + groupBy?: Querybuildertypesv5GroupByKeyDTO[]; + having?: Querybuildertypesv5HavingDTO; + /** + * @type string + */ + legend?: string; + /** + * @type integer + */ + limit?: number; + limitBy?: Querybuildertypesv5LimitByDTO; + /** + * @type string + */ + name?: string; + /** + * @type integer + */ + offset?: number; + /** + * @type array + */ + order?: Querybuildertypesv5OrderByDTO[]; + /** + * @type array + */ + secondaryAggregations?: Querybuildertypesv5SecondaryAggregationDTO[]; + /** + * @type array + */ + selectFields?: TelemetrytypesTelemetryFieldKeyDTO[]; + signal?: TelemetrytypesSignalDTO; + source?: TelemetrytypesSourceDTO; + stepInterval?: Querybuildertypesv5StepDTO; +} + +export interface Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5MetricAggregationDTO { + /** + * @type array + */ + aggregations?: Querybuildertypesv5MetricAggregationDTO[]; + /** + * @type string + */ + cursor?: string; + /** + * @type boolean + */ + disabled?: boolean; + filter?: Querybuildertypesv5FilterDTO; + /** + * @type array + */ + functions?: Querybuildertypesv5FunctionDTO[]; + /** + * @type array + */ + groupBy?: Querybuildertypesv5GroupByKeyDTO[]; + having?: Querybuildertypesv5HavingDTO; + /** + * @type string + */ + legend?: string; + /** + * @type integer + */ + limit?: number; + limitBy?: Querybuildertypesv5LimitByDTO; + /** + * @type string + */ + name?: string; + /** + * @type integer + */ + offset?: number; + /** + * @type array + */ + order?: Querybuildertypesv5OrderByDTO[]; + /** + * @type array + */ + secondaryAggregations?: Querybuildertypesv5SecondaryAggregationDTO[]; + /** + * @type array + */ + selectFields?: TelemetrytypesTelemetryFieldKeyDTO[]; + signal?: TelemetrytypesSignalDTO; + source?: TelemetrytypesSourceDTO; + stepInterval?: Querybuildertypesv5StepDTO; +} + +export interface Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5TraceAggregationDTO { + /** + * @type array + */ + aggregations?: Querybuildertypesv5TraceAggregationDTO[]; + /** + * @type string + */ + cursor?: string; + /** + * @type boolean + */ + disabled?: boolean; + filter?: Querybuildertypesv5FilterDTO; + /** + * @type array + */ + functions?: Querybuildertypesv5FunctionDTO[]; + /** + * @type array + */ + groupBy?: Querybuildertypesv5GroupByKeyDTO[]; + having?: Querybuildertypesv5HavingDTO; + /** + * @type string + */ + legend?: string; + /** + * @type integer + */ + limit?: number; + limitBy?: Querybuildertypesv5LimitByDTO; + /** + * @type string + */ + name?: string; + /** + * @type integer + */ + offset?: number; + /** + * @type array + */ + order?: Querybuildertypesv5OrderByDTO[]; + /** + * @type array + */ + secondaryAggregations?: Querybuildertypesv5SecondaryAggregationDTO[]; + /** + * @type array + */ + selectFields?: TelemetrytypesTelemetryFieldKeyDTO[]; + signal?: TelemetrytypesSignalDTO; + source?: TelemetrytypesSourceDTO; + stepInterval?: Querybuildertypesv5StepDTO; +} + +export interface Querybuildertypesv5QueryBuilderTraceOperatorDTO { + /** + * @type array + */ + aggregations?: Querybuildertypesv5TraceAggregationDTO[]; + /** + * @type string + */ + cursor?: string; + /** + * @type boolean + */ + disabled?: boolean; + /** + * @type string + */ + expression?: string; + filter?: Querybuildertypesv5FilterDTO; + /** + * @type array + */ + functions?: Querybuildertypesv5FunctionDTO[]; + /** + * @type array + */ + groupBy?: Querybuildertypesv5GroupByKeyDTO[]; + having?: Querybuildertypesv5HavingDTO; + /** + * @type string + */ + legend?: string; + /** + * @type integer + */ + limit?: number; + /** + * @type string + */ + name?: string; + /** + * @type integer + */ + offset?: number; + /** + * @type array + */ + order?: Querybuildertypesv5OrderByDTO[]; + /** + * @type string + */ + returnSpansFrom?: string; + /** + * @type array */ selectFields?: TelemetrytypesTelemetryFieldKeyDTO[]; stepInterval?: Querybuildertypesv5StepDTO; @@ -1860,307 +2441,725 @@ export interface Querybuildertypesv5QueryEnvelopeBuilderTraceDTO { type?: Querybuildertypesv5QueryTypeDTO; } -export interface Querybuildertypesv5QueryEnvelopeClickHouseSQLDTO { - spec?: Querybuildertypesv5ClickHouseQueryDTO; - type?: Querybuildertypesv5QueryTypeDTO; +export interface Querybuildertypesv5QueryEnvelopeClickHouseSQLDTO { + spec?: Querybuildertypesv5ClickHouseQueryDTO; + type?: Querybuildertypesv5QueryTypeDTO; +} + +export interface Querybuildertypesv5QueryEnvelopeFormulaDTO { + spec?: Querybuildertypesv5QueryBuilderFormulaDTO; + type?: Querybuildertypesv5QueryTypeDTO; +} + +export interface Querybuildertypesv5QueryEnvelopePromQLDTO { + spec?: Querybuildertypesv5PromQueryDTO; + type?: Querybuildertypesv5QueryTypeDTO; +} + +export interface Querybuildertypesv5QueryEnvelopeTraceOperatorDTO { + spec?: Querybuildertypesv5QueryBuilderTraceOperatorDTO; + type?: Querybuildertypesv5QueryTypeDTO; +} + +export type Querybuildertypesv5QueryRangeRequestDTOVariables = { + [key: string]: Querybuildertypesv5VariableItemDTO; +}; + +/** + * Request body for the v5 query range endpoint. Supports builder queries (traces, logs, metrics), formulas, joins, trace operators, PromQL, and ClickHouse SQL queries. + */ +export interface Querybuildertypesv5QueryRangeRequestDTO { + compositeQuery?: Querybuildertypesv5CompositeQueryDTO; + /** + * @type integer + * @minimum 0 + */ + end?: number; + formatOptions?: Querybuildertypesv5FormatOptionsDTO; + /** + * @type boolean + */ + noCache?: boolean; + requestType?: Querybuildertypesv5RequestTypeDTO; + /** + * @type string + */ + schemaVersion?: string; + /** + * @type integer + * @minimum 0 + */ + start?: number; + /** + * @type object + */ + variables?: Querybuildertypesv5QueryRangeRequestDTOVariables; +} + +/** + * Response from the v5 query range endpoint. The data.results array contains typed results depending on the requestType: TimeSeriesData for time_series, ScalarData for scalar, or RawData for raw requests. + */ +export interface Querybuildertypesv5QueryRangeResponseDTO { + data?: Querybuildertypesv5QueryDataDTO; + meta?: Querybuildertypesv5ExecStatsDTO; + type?: Querybuildertypesv5RequestTypeDTO; + warning?: Querybuildertypesv5QueryWarnDataDTO; +} + +export enum Querybuildertypesv5QueryTypeDTO { + builder_query = 'builder_query', + builder_formula = 'builder_formula', + builder_trace_operator = 'builder_trace_operator', + clickhouse_sql = 'clickhouse_sql', + promql = 'promql', +} +export interface Querybuildertypesv5QueryWarnDataDTO { + /** + * @type string + */ + message?: string; + /** + * @type string + */ + url?: string; + /** + * @type array + */ + warnings?: Querybuildertypesv5QueryWarnDataAdditionalDTO[]; +} + +export interface Querybuildertypesv5QueryWarnDataAdditionalDTO { + /** + * @type string + */ + message?: string; +} + +export interface Querybuildertypesv5RawDataDTO { + /** + * @type string + */ + nextCursor?: string; + /** + * @type string + */ + queryName?: string; + /** + * @type array + * @nullable true + */ + rows?: Querybuildertypesv5RawRowDTO[] | null; +} + +/** + * @nullable + */ +export type Querybuildertypesv5RawRowDTOData = { + [key: string]: unknown; +} | null; + +export interface Querybuildertypesv5RawRowDTO { + /** + * @type object + * @nullable true + */ + data?: Querybuildertypesv5RawRowDTOData; + /** + * @type string + * @format date-time + */ + timestamp?: Date; +} + +export enum Querybuildertypesv5ReduceToDTO { + sum = 'sum', + count = 'count', + avg = 'avg', + min = 'min', + max = 'max', + last = 'last', + median = 'median', +} +export enum Querybuildertypesv5RequestTypeDTO { + scalar = 'scalar', + time_series = 'time_series', + raw = 'raw', + raw_stream = 'raw_stream', + trace = 'trace', +} +export interface Querybuildertypesv5ScalarDataDTO { + /** + * @type array + * @nullable true + */ + columns?: Querybuildertypesv5ColumnDescriptorDTO[] | null; + /** + * @type array + * @nullable true + */ + data?: unknown[][] | null; + /** + * @type string + */ + queryName?: string; +} + +export interface Querybuildertypesv5SecondaryAggregationDTO { + /** + * @type string + */ + alias?: string; + /** + * @type string + */ + expression?: string; + /** + * @type array + */ + groupBy?: Querybuildertypesv5GroupByKeyDTO[]; + /** + * @type integer + */ + limit?: number; + limitBy?: Querybuildertypesv5LimitByDTO; + /** + * @type array + */ + order?: Querybuildertypesv5OrderByDTO[]; + stepInterval?: Querybuildertypesv5StepDTO; +} + +/** + * Step interval. Accepts a Go duration string (e.g., "60s", "1m", "1h") or a number representing seconds (e.g., 60). + */ +export type Querybuildertypesv5StepDTO = string | number; + +export interface Querybuildertypesv5TimeSeriesDTO { + /** + * @type array + */ + labels?: Querybuildertypesv5LabelDTO[]; + /** + * @type array + * @nullable true + */ + values?: Querybuildertypesv5TimeSeriesValueDTO[] | null; +} + +export interface Querybuildertypesv5TimeSeriesDataDTO { + /** + * @type array + * @nullable true + */ + aggregations?: Querybuildertypesv5AggregationBucketDTO[] | null; + /** + * @type string + */ + queryName?: string; +} + +export interface Querybuildertypesv5TimeSeriesValueDTO { + bucket?: Querybuildertypesv5BucketDTO; + /** + * @type boolean + */ + partial?: boolean; + /** + * @type integer + * @format int64 + */ + timestamp?: number; + /** + * @type number + * @format double + */ + value?: number; + /** + * @type array + */ + values?: number[]; +} + +export interface Querybuildertypesv5TraceAggregationDTO { + /** + * @type string + */ + alias?: string; + /** + * @type string + */ + expression?: string; } -export interface Querybuildertypesv5QueryEnvelopeFormulaDTO { - spec?: Querybuildertypesv5QueryBuilderFormulaDTO; - type?: Querybuildertypesv5QueryTypeDTO; +export interface Querybuildertypesv5VariableItemDTO { + type?: Querybuildertypesv5VariableTypeDTO; + value?: unknown; } -export interface Querybuildertypesv5QueryEnvelopePromQLDTO { - spec?: Querybuildertypesv5PromQueryDTO; - type?: Querybuildertypesv5QueryTypeDTO; +export enum Querybuildertypesv5VariableTypeDTO { + query = 'query', + dynamic = 'dynamic', + custom = 'custom', + text = 'text', } - -export interface Querybuildertypesv5QueryEnvelopeTraceOperatorDTO { - spec?: Querybuildertypesv5QueryBuilderTraceOperatorDTO; - type?: Querybuildertypesv5QueryTypeDTO; +export interface RenderErrorResponseDTO { + error: ErrorsJSONDTO; + /** + * @type string + */ + status: string; } -export type Querybuildertypesv5QueryRangeRequestDTOVariables = { - [key: string]: Querybuildertypesv5VariableItemDTO; -}; - -/** - * Request body for the v5 query range endpoint. Supports builder queries (traces, logs, metrics), formulas, joins, trace operators, PromQL, and ClickHouse SQL queries. - */ -export interface Querybuildertypesv5QueryRangeRequestDTO { - compositeQuery?: Querybuildertypesv5CompositeQueryDTO; +export interface RulestatehistorytypesGettableRuleStateHistoryDTO { /** * @type integer * @minimum 0 */ - end?: number; - formatOptions?: Querybuildertypesv5FormatOptionsDTO; + fingerprint: number; + /** + * @type array + * @nullable true + */ + labels: Querybuildertypesv5LabelDTO[] | null; + overallState: RuletypesAlertStateDTO; /** * @type boolean */ - noCache?: boolean; - requestType?: Querybuildertypesv5RequestTypeDTO; + overallStateChanged: boolean; /** * @type string */ - schemaVersion?: string; + ruleId: string; + /** + * @type string + */ + ruleName: string; + state: RuletypesAlertStateDTO; + /** + * @type boolean + */ + stateChanged: boolean; + /** + * @type integer + * @format int64 + */ + unixMilli: number; + /** + * @type number + * @format double + */ + value: number; +} + +export interface RulestatehistorytypesGettableRuleStateHistoryContributorDTO { /** * @type integer * @minimum 0 */ - start?: number; + count: number; /** - * @type object + * @type integer + * @minimum 0 */ - variables?: Querybuildertypesv5QueryRangeRequestDTOVariables; + fingerprint: number; + /** + * @type array + * @nullable true + */ + labels: Querybuildertypesv5LabelDTO[] | null; + /** + * @type string + */ + relatedLogsLink?: string; + /** + * @type string + */ + relatedTracesLink?: string; } -/** - * Response from the v5 query range endpoint. The data.results array contains typed results depending on the requestType: TimeSeriesData for time_series, ScalarData for scalar, or RawData for raw requests. - */ -export interface Querybuildertypesv5QueryRangeResponseDTO { - data?: Querybuildertypesv5QueryDataDTO; - meta?: Querybuildertypesv5ExecStatsDTO; - type?: Querybuildertypesv5RequestTypeDTO; - warning?: Querybuildertypesv5QueryWarnDataDTO; +export interface RulestatehistorytypesGettableRuleStateHistoryStatsDTO { + /** + * @type number + * @format double + */ + currentAvgResolutionTime: number; + currentAvgResolutionTimeSeries: Querybuildertypesv5TimeSeriesDTO; + currentTriggersSeries: Querybuildertypesv5TimeSeriesDTO; + /** + * @type number + * @format double + */ + pastAvgResolutionTime: number; + pastAvgResolutionTimeSeries: Querybuildertypesv5TimeSeriesDTO; + pastTriggersSeries: Querybuildertypesv5TimeSeriesDTO; + /** + * @type integer + * @minimum 0 + */ + totalCurrentTriggers: number; + /** + * @type integer + * @minimum 0 + */ + totalPastTriggers: number; } -export enum Querybuildertypesv5QueryTypeDTO { - builder_query = 'builder_query', - builder_formula = 'builder_formula', - builder_trace_operator = 'builder_trace_operator', - clickhouse_sql = 'clickhouse_sql', - promql = 'promql', +export interface RulestatehistorytypesGettableRuleStateTimelineDTO { + /** + * @type array + * @nullable true + */ + items: RulestatehistorytypesGettableRuleStateHistoryDTO[] | null; + /** + * @type string + */ + nextCursor?: string; + /** + * @type integer + * @minimum 0 + */ + total: number; } -export interface Querybuildertypesv5QueryWarnDataDTO { + +export interface RulestatehistorytypesGettableRuleStateWindowDTO { + /** + * @type integer + * @format int64 + */ + end: number; + /** + * @type integer + * @format int64 + */ + start: number; + state: RuletypesAlertStateDTO; +} + +export enum RuletypesAlertStateDTO { + inactive = 'inactive', + pending = 'pending', + recovering = 'recovering', + firing = 'firing', + nodata = 'nodata', + disabled = 'disabled', +} +export interface ServiceaccounttypesGettableFactorAPIKeyDTO { /** * @type string + * @format date-time */ - message?: string; + createdAt?: Date; + /** + * @type integer + * @minimum 0 + */ + expiresAt: number; /** * @type string */ - url?: string; + id: string; /** - * @type array + * @type string + * @format date-time */ - warnings?: Querybuildertypesv5QueryWarnDataAdditionalDTO[]; + lastObservedAt: Date; + /** + * @type string + */ + name?: string; + /** + * @type string + */ + serviceAccountId: string; + /** + * @type string + * @format date-time + */ + updatedAt?: Date; } -export interface Querybuildertypesv5QueryWarnDataAdditionalDTO { +export interface ServiceaccounttypesGettableFactorAPIKeyWithKeyDTO { /** * @type string */ - message?: string; + id: string; + /** + * @type string + */ + key: string; } -export interface Querybuildertypesv5RawDataDTO { +export interface ServiceaccounttypesPostableFactorAPIKeyDTO { + /** + * @type integer + * @minimum 0 + */ + expiresAt: number; /** * @type string */ - nextCursor?: string; + name: string; +} + +export interface ServiceaccounttypesPostableServiceAccountDTO { /** * @type string */ - queryName?: string; + name: string; +} + +export interface ServiceaccounttypesPostableServiceAccountRoleDTO { /** - * @type array - * @nullable true + * @type string */ - rows?: Querybuildertypesv5RawRowDTO[] | null; + id: string; } -/** - * @nullable - */ -export type Querybuildertypesv5RawRowDTOData = { - [key: string]: unknown; -} | null; +export interface ServiceaccounttypesServiceAccountDTO { + /** + * @type string + * @format date-time + */ + createdAt?: Date; + /** + * @type string + */ + email: string; + /** + * @type string + */ + id: string; + /** + * @type string + */ + name: string; + /** + * @type string + */ + orgId: string; + /** + * @type string + */ + status: string; + /** + * @type string + * @format date-time + */ + updatedAt?: Date; +} -export interface Querybuildertypesv5RawRowDTO { +export interface ServiceaccounttypesServiceAccountRoleDTO { /** - * @type object - * @nullable true + * @type string + * @format date-time */ - data?: Querybuildertypesv5RawRowDTOData; + createdAt?: Date; + /** + * @type string + */ + id: string; + role: AuthtypesRoleDTO; + /** + * @type string + */ + roleId: string; + /** + * @type string + */ + serviceAccountId: string; /** * @type string * @format date-time */ - timestamp?: Date; + updatedAt?: Date; } -export enum Querybuildertypesv5ReduceToDTO { - sum = 'sum', - count = 'count', - avg = 'avg', - min = 'min', - max = 'max', - last = 'last', - median = 'median', -} -export enum Querybuildertypesv5RequestTypeDTO { - scalar = 'scalar', - time_series = 'time_series', - raw = 'raw', - raw_stream = 'raw_stream', - trace = 'trace', -} -export interface Querybuildertypesv5ScalarDataDTO { +export interface ServiceaccounttypesServiceAccountWithRolesDTO { /** - * @type array - * @nullable true + * @type string + * @format date-time */ - columns?: Querybuildertypesv5ColumnDescriptorDTO[] | null; + createdAt?: Date; /** - * @type array - * @nullable true + * @type string */ - data?: unknown[][] | null; + email: string; /** * @type string */ - queryName?: string; -} - -export interface Querybuildertypesv5SecondaryAggregationDTO { + id: string; /** * @type string */ - alias?: string; + name: string; /** * @type string */ - expression?: string; + orgId: string; /** * @type array + * @nullable true */ - groupBy?: Querybuildertypesv5GroupByKeyDTO[]; + serviceAccountRoles: ServiceaccounttypesServiceAccountRoleDTO[] | null; + /** + * @type string + */ + status: string; + /** + * @type string + * @format date-time + */ + updatedAt?: Date; +} + +export interface ServiceaccounttypesUpdatableFactorAPIKeyDTO { /** * @type integer + * @minimum 0 */ - limit?: number; - limitBy?: Querybuildertypesv5LimitByDTO; + expiresAt: number; /** - * @type array + * @type string */ - order?: Querybuildertypesv5OrderByDTO[]; - stepInterval?: Querybuildertypesv5StepDTO; + name: string; } +export enum TelemetrytypesFieldContextDTO { + metric = 'metric', + log = 'log', + span = 'span', + resource = 'resource', + attribute = 'attribute', + body = 'body', +} +export enum TelemetrytypesFieldDataTypeDTO { + string = 'string', + bool = 'bool', + float64 = 'float64', + int64 = 'int64', + number = 'number', +} /** - * Step interval. Accepts a Go duration string (e.g., "60s", "1m", "1h") or a number representing seconds (e.g., 60). + * @nullable */ -export type Querybuildertypesv5StepDTO = string | number; +export type TelemetrytypesGettableFieldKeysDTOKeys = { + [key: string]: TelemetrytypesTelemetryFieldKeyDTO[]; +} | null; -export interface Querybuildertypesv5TimeSeriesDTO { +export interface TelemetrytypesGettableFieldKeysDTO { /** - * @type array + * @type boolean */ - labels?: Querybuildertypesv5LabelDTO[]; + complete: boolean; /** - * @type array + * @type object * @nullable true */ - values?: Querybuildertypesv5TimeSeriesValueDTO[] | null; + keys: TelemetrytypesGettableFieldKeysDTOKeys; } -export interface Querybuildertypesv5TimeSeriesDataDTO { +export interface TelemetrytypesGettableFieldValuesDTO { /** - * @type array - * @nullable true + * @type boolean */ - aggregations?: Querybuildertypesv5AggregationBucketDTO[] | null; + complete: boolean; + values: TelemetrytypesTelemetryFieldValuesDTO; +} + +export enum TelemetrytypesSignalDTO { + traces = 'traces', + logs = 'logs', + metrics = 'metrics', +} +export enum TelemetrytypesSourceDTO { + meter = 'meter', +} +export interface TelemetrytypesTelemetryFieldKeyDTO { /** * @type string */ - queryName?: string; + description?: string; + fieldContext?: TelemetrytypesFieldContextDTO; + fieldDataType?: TelemetrytypesFieldDataTypeDTO; + /** + * @type string + */ + name: string; + signal?: TelemetrytypesSignalDTO; + /** + * @type string + */ + unit?: string; } -export interface Querybuildertypesv5TimeSeriesValueDTO { - bucket?: Querybuildertypesv5BucketDTO; +export interface TelemetrytypesTelemetryFieldValuesDTO { /** - * @type boolean + * @type array */ - partial?: boolean; + boolValues?: boolean[]; /** - * @type integer - * @format int64 + * @type array */ - timestamp?: number; + numberValues?: number[]; /** - * @type number - * @format double + * @type array */ - value?: number; + relatedValues?: string[]; /** * @type array */ - values?: number[]; + stringValues?: string[]; } -export interface Querybuildertypesv5TraceAggregationDTO { +export interface TypesChangePasswordRequestDTO { /** * @type string */ - alias?: string; + newPassword?: string; /** * @type string */ - expression?: string; -} - -export interface Querybuildertypesv5VariableItemDTO { - type?: Querybuildertypesv5VariableTypeDTO; - value?: unknown; -} - -export enum Querybuildertypesv5VariableTypeDTO { - query = 'query', - dynamic = 'dynamic', - custom = 'custom', - text = 'text', -} -export interface RenderErrorResponseDTO { - error: ErrorsJSONDTO; + oldPassword?: string; /** * @type string */ - status: string; + userId?: string; } -export interface ServiceaccounttypesFactorAPIKeyDTO { +export interface TypesDeprecatedUserDTO { /** * @type string * @format date-time */ createdAt?: Date; /** - * @type integer - * @minimum 0 + * @type string */ - expiresAt: number; + displayName?: string; /** * @type string */ - id: string; + email?: string; /** * @type string */ - key: string; + id: string; + /** + * @type boolean + */ + isRoot?: boolean; /** * @type string - * @format date-time */ - lastObservedAt: Date; + orgId?: string; /** * @type string */ - name?: string; + role?: string; /** * @type string */ - serviceAccountId: string; + status?: string; /** * @type string * @format date-time @@ -2168,79 +3167,81 @@ export interface ServiceaccounttypesFactorAPIKeyDTO { updatedAt?: Date; } -export interface ServiceaccounttypesGettableFactorAPIKeyWithKeyDTO { +export interface TypesIdentifiableDTO { /** * @type string */ id: string; +} + +export interface TypesInviteDTO { /** * @type string + * @format date-time */ - key: string; -} - -export interface ServiceaccounttypesPostableFactorAPIKeyDTO { + createdAt?: Date; /** - * @type integer - * @minimum 0 + * @type string */ - expiresAt: number; + email?: string; /** * @type string */ - name: string; -} - -export interface ServiceaccounttypesPostableServiceAccountDTO { + id: string; /** * @type string */ - email: string; + inviteLink?: string; /** * @type string */ - name: string; + name?: string; /** - * @type array + * @type string */ - roles: string[]; -} - -export interface ServiceaccounttypesServiceAccountDTO { + orgId?: string; /** * @type string - * @format date-time */ - createdAt?: Date; + role?: string; + /** + * @type string + */ + token?: string; /** * @type string * @format date-time */ - deletedAt: Date; + updatedAt?: Date; +} + +export interface TypesOrganizationDTO { /** * @type string */ - email: string; + alias?: string; /** * @type string + * @format date-time */ - id: string; + createdAt?: Date; /** * @type string */ - name: string; + displayName?: string; /** * @type string */ - orgId: string; + id: string; /** - * @type array + * @type integer + * @minimum 0 */ - roles: string[]; + key?: number; /** * @type string */ - status: string; + name?: string; /** * @type string * @format date-time @@ -2248,19 +3249,14 @@ export interface ServiceaccounttypesServiceAccountDTO { updatedAt?: Date; } -export interface ServiceaccounttypesUpdatableFactorAPIKeyDTO { - /** - * @type integer - * @minimum 0 - */ - expiresAt: number; +export interface TypesPostableBulkInviteRequestDTO { /** - * @type string + * @type array */ - name: string; + invites: TypesPostableInviteDTO[]; } -export interface ServiceaccounttypesUpdatableServiceAccountDTO { +export interface TypesPostableForgotPasswordDTO { /** * @type string */ @@ -2268,736 +3264,993 @@ export interface ServiceaccounttypesUpdatableServiceAccountDTO { /** * @type string */ - name: string; + frontendBaseURL?: string; /** - * @type array + * @type string */ - roles: string[]; + orgId: string; } -export interface ServiceaccounttypesUpdatableServiceAccountStatusDTO { +export interface TypesPostableInviteDTO { /** * @type string */ - status: string; -} - -export enum TelemetrytypesFieldContextDTO { - metric = 'metric', - log = 'log', - span = 'span', - resource = 'resource', - attribute = 'attribute', - body = 'body', -} -export enum TelemetrytypesFieldDataTypeDTO { - string = 'string', - bool = 'bool', - float64 = 'float64', - int64 = 'int64', - number = 'number', -} -/** - * @nullable - */ -export type TelemetrytypesGettableFieldKeysDTOKeys = { - [key: string]: TelemetrytypesTelemetryFieldKeyDTO[]; -} | null; - -export interface TelemetrytypesGettableFieldKeysDTO { + email?: string; /** - * @type boolean + * @type string */ - complete: boolean; + frontendBaseUrl?: string; /** - * @type object - * @nullable true + * @type string */ - keys: TelemetrytypesGettableFieldKeysDTOKeys; -} - -export interface TelemetrytypesGettableFieldValuesDTO { + name?: string; /** - * @type boolean + * @type string */ - complete: boolean; - values: TelemetrytypesTelemetryFieldValuesDTO; + role?: string; } -export enum TelemetrytypesSignalDTO { - traces = 'traces', - logs = 'logs', - metrics = 'metrics', -} -export enum TelemetrytypesSourceDTO { - meter = 'meter', -} -export interface TelemetrytypesTelemetryFieldKeyDTO { +export interface TypesPostableResetPasswordDTO { /** * @type string */ - description?: string; - fieldContext?: TelemetrytypesFieldContextDTO; - fieldDataType?: TelemetrytypesFieldDataTypeDTO; + password?: string; /** * @type string */ - name: string; - signal?: TelemetrytypesSignalDTO; + token?: string; +} + +export interface TypesPostableRoleDTO { /** * @type string */ - unit?: string; + name: string; } -export interface TelemetrytypesTelemetryFieldValuesDTO { +export interface TypesResetPasswordTokenDTO { /** - * @type array + * @type string + * @format date-time */ - boolValues?: boolean[]; + expiresAt?: Date; /** - * @type array + * @type string */ - numberValues?: number[]; + id: string; /** - * @type array + * @type string */ - relatedValues?: string[]; + passwordId?: string; /** - * @type array + * @type string */ - stringValues?: string[]; + token?: string; } -export interface TypesChangePasswordRequestDTO { +export interface TypesUpdatableUserDTO { /** * @type string */ - newPassword?: string; + displayName: string; +} + +export interface TypesUserDTO { /** * @type string + * @format date-time */ - oldPassword?: string; + createdAt?: Date; /** * @type string */ - userId?: string; -} - -export interface TypesGettableAPIKeyDTO { + displayName?: string; /** * @type string - * @format date-time */ - createdAt?: Date; + email?: string; /** * @type string */ - createdBy?: string; - createdByUser?: TypesUserDTO; + id: string; /** - * @type integer - * @format int64 + * @type boolean */ - expiresAt?: number; + isRoot?: boolean; /** * @type string */ - id: string; + orgId?: string; /** - * @type integer - * @format int64 + * @type string */ - lastUsed?: number; + status?: string; /** * @type string + * @format date-time */ - name?: string; + updatedAt?: Date; +} + +export interface ZeustypesGettableHostDTO { /** - * @type boolean + * @type array + * @nullable true */ - revoked?: boolean; + hosts: ZeustypesHostDTO[] | null; /** * @type string */ - role?: string; + name: string; /** * @type string */ - token?: string; + state: string; /** * @type string - * @format date-time */ - updatedAt?: Date; + tier: string; +} + +export interface ZeustypesHostDTO { + /** + * @type boolean + */ + is_default: boolean; /** * @type string */ - updatedBy?: string; - updatedByUser?: TypesUserDTO; + name: string; /** * @type string */ - userId?: string; + url: string; } -export interface TypesIdentifiableDTO { +export interface ZeustypesPostableHostDTO { /** * @type string */ - id: string; + name: string; } -export interface TypesInviteDTO { +export interface ZeustypesPostableProfileDTO { /** * @type string - * @format date-time */ - createdAt?: Date; + existing_observability_tool: string; /** - * @type string + * @type boolean */ - email?: string; + has_existing_observability_tool: boolean; /** - * @type string + * @type integer + * @format int64 */ - id: string; + logs_scale_per_day_in_gb: number; + /** + * @type integer + * @format int64 + */ + number_of_hosts: number; + /** + * @type integer + * @format int64 + */ + number_of_services: number; + /** + * @type array + * @nullable true + */ + reasons_for_interest_in_signoz: string[] | null; /** * @type string */ - inviteLink?: string; + timeline_for_migrating_to_signoz: string; + /** + * @type boolean + */ + uses_otel: boolean; /** * @type string */ - name?: string; + where_did_you_discover_signoz: string; +} + +export type AuthzCheck200 = { + /** + * @type array + */ + data: AuthtypesGettableTransactionDTO[]; /** * @type string */ - orgId?: string; + status: string; +}; + +export type AuthzResources200 = { + data: AuthtypesGettableResourcesDTO; /** * @type string */ - role?: string; + status: string; +}; + +export type ChangePasswordPathParameters = { + id: string; +}; +export type AgentCheckInDeprecatedPathParameters = { + cloudProvider: string; +}; +export type AgentCheckInDeprecated200 = { + data: CloudintegrationtypesGettableAgentCheckInResponseDTO; /** * @type string */ - token?: string; + status: string; +}; + +export type ListAccountsPathParameters = { + cloudProvider: string; +}; +export type ListAccounts200 = { + data: CloudintegrationtypesGettableAccountsDTO; /** * @type string - * @format date-time */ - updatedAt?: Date; -} + status: string; +}; -export interface TypesOrganizationDTO { +export type CreateAccountPathParameters = { + cloudProvider: string; +}; +export type CreateAccount200 = { + data: CloudintegrationtypesGettableAccountWithArtifactDTO; /** * @type string */ - alias?: string; + status: string; +}; + +export type DisconnectAccountPathParameters = { + cloudProvider: string; + id: string; +}; +export type GetAccountPathParameters = { + cloudProvider: string; + id: string; +}; +export type GetAccount200 = { + data: CloudintegrationtypesAccountDTO; /** * @type string - * @format date-time */ - createdAt?: Date; + status: string; +}; + +export type UpdateAccountPathParameters = { + cloudProvider: string; + id: string; +}; +export type AgentCheckInPathParameters = { + cloudProvider: string; +}; +export type AgentCheckIn200 = { + data: CloudintegrationtypesGettableAgentCheckInResponseDTO; /** * @type string */ - displayName?: string; + status: string; +}; + +export type ListServicesMetadataPathParameters = { + cloudProvider: string; +}; +export type ListServicesMetadata200 = { + data: CloudintegrationtypesGettableServicesMetadataDTO; /** * @type string */ - id: string; + status: string; +}; + +export type GetServicePathParameters = { + cloudProvider: string; + serviceId: string; +}; +export type GetService200 = { + data: CloudintegrationtypesServiceDTO; /** - * @type integer - * @minimum 0 + * @type string */ - key?: number; + status: string; +}; + +export type UpdateServicePathParameters = { + cloudProvider: string; + serviceId: string; +}; +export type CreateSessionByGoogleCallback303 = { + data: AuthtypesGettableTokenDTO; /** * @type string */ - name?: string; + status: string; +}; + +export type CreateSessionByOIDCCallback303 = { + data: AuthtypesGettableTokenDTO; /** * @type string - * @format date-time */ - updatedAt?: Date; -} + status: string; +}; -export interface TypesPostableAPIKeyDTO { +export type CreateSessionBySAMLCallbackParams = { /** - * @type integer - * @format int64 + * @type string + * @description undefined */ - expiresInDays?: number; + RelayState?: string; /** * @type string + * @description undefined */ - name?: string; + SAMLResponse?: string; +}; + +export type CreateSessionBySAMLCallbackBody = { /** * @type string */ - role?: string; -} - -export interface TypesPostableBulkInviteRequestDTO { + RelayState?: string; /** - * @type array + * @type string */ - invites: TypesPostableInviteDTO[]; -} + SAMLResponse?: string; +}; -export interface TypesPostableForgotPasswordDTO { +export type CreateSessionBySAMLCallback303 = { + data: AuthtypesGettableTokenDTO; /** * @type string */ - email: string; + status: string; +}; + +export type DeletePublicDashboardPathParameters = { + id: string; +}; +export type GetPublicDashboardPathParameters = { + id: string; +}; +export type GetPublicDashboard200 = { + data: DashboardtypesGettablePublicDasbhboardDTO; /** * @type string */ - frontendBaseURL?: string; + status: string; +}; + +export type CreatePublicDashboardPathParameters = { + id: string; +}; +export type CreatePublicDashboard201 = { + data: TypesIdentifiableDTO; /** * @type string */ - orgId: string; -} + status: string; +}; -export interface TypesPostableInviteDTO { +export type UpdatePublicDashboardPathParameters = { + id: string; +}; +export type ListAuthDomains200 = { /** - * @type string + * @type array */ - email?: string; + data: AuthtypesGettableAuthDomainDTO[]; /** * @type string */ - frontendBaseUrl?: string; + status: string; +}; + +export type CreateAuthDomain200 = { + data: AuthtypesGettableAuthDomainDTO; /** * @type string */ - name?: string; + status: string; +}; + +export type DeleteAuthDomainPathParameters = { + id: string; +}; +export type UpdateAuthDomainPathParameters = { + id: string; +}; +export type HandleExportRawDataPOSTParams = { /** + * @enum csv,jsonl * @type string + * @description The output format for the export. */ - role?: string; -} + format?: HandleExportRawDataPOSTFormat; +}; -export interface TypesPostableResetPasswordDTO { +export enum HandleExportRawDataPOSTFormat { + csv = 'csv', + jsonl = 'jsonl', +} +export type GetFieldsKeysParams = { /** - * @type string + * @description undefined */ - password?: string; + signal?: TelemetrytypesSignalDTO; /** - * @type string + * @description undefined */ - token?: string; -} - -export interface TypesResetPasswordTokenDTO { + source?: TelemetrytypesSourceDTO; /** - * @type string - * @format date-time + * @type integer + * @description undefined */ - expiresAt?: Date; + limit?: number; /** - * @type string + * @type integer + * @format int64 + * @description undefined */ - id: string; + startUnixMilli?: number; /** - * @type string + * @type integer + * @format int64 + * @description undefined */ - passwordId?: string; + endUnixMilli?: number; /** - * @type string + * @description undefined */ - token?: string; -} - -export interface TypesStorableAPIKeyDTO { + fieldContext?: TelemetrytypesFieldContextDTO; /** - * @type string - * @format date-time + * @description undefined */ - createdAt?: Date; + fieldDataType?: TelemetrytypesFieldDataTypeDTO; /** * @type string + * @description undefined */ - createdBy?: string; + metricName?: string; /** * @type string + * @description undefined */ - id: string; + searchText?: string; +}; + +export type GetFieldsKeys200 = { + data: TelemetrytypesGettableFieldKeysDTO; /** * @type string */ - name?: string; + status: string; +}; + +export type GetFieldsValuesParams = { /** - * @type boolean + * @description undefined */ - revoked?: boolean; + signal?: TelemetrytypesSignalDTO; /** - * @type string + * @description undefined */ - role?: string; + source?: TelemetrytypesSourceDTO; /** - * @type string + * @type integer + * @description undefined */ - token?: string; + limit?: number; /** - * @type string - * @format date-time + * @type integer + * @format int64 + * @description undefined */ - updatedAt?: Date; + startUnixMilli?: number; /** - * @type string + * @type integer + * @format int64 + * @description undefined */ - updatedBy?: string; + endUnixMilli?: number; /** - * @type string + * @description undefined */ - userId?: string; -} - -export interface TypesUserDTO { + fieldContext?: TelemetrytypesFieldContextDTO; /** - * @type string - * @format date-time + * @description undefined */ - createdAt?: Date; + fieldDataType?: TelemetrytypesFieldDataTypeDTO; /** * @type string + * @description undefined */ - displayName?: string; + metricName?: string; /** * @type string + * @description undefined */ - email?: string; + searchText?: string; /** * @type string + * @description undefined */ - id: string; + name?: string; /** - * @type boolean + * @type string + * @description undefined */ - isRoot?: boolean; + existingQuery?: string; +}; + +export type GetFieldsValues200 = { + data: TelemetrytypesGettableFieldValuesDTO; /** * @type string */ - orgId?: string; + status: string; +}; + +export type GetResetPasswordTokenPathParameters = { + id: string; +}; +export type GetResetPasswordToken200 = { + data: TypesResetPasswordTokenDTO; /** * @type string */ - role?: string; + status: string; +}; + +export type GetGlobalConfig200 = { + data: GlobaltypesConfigDTO; /** * @type string */ - status?: string; + status: string; +}; + +export type CreateInvite201 = { + data: TypesInviteDTO; /** * @type string - * @format date-time */ - updatedAt?: Date; -} + status: string; +}; -export interface ZeustypesGettableHostDTO { +export type ListPromotedAndIndexedPaths200 = { /** * @type array * @nullable true */ - hosts: ZeustypesHostDTO[] | null; + data: PromotetypesPromotePathDTO[] | null; /** * @type string */ - name: string; + status: string; +}; + +export type ListOrgPreferences200 = { /** - * @type string + * @type array */ - state: string; + data: PreferencetypesPreferenceDTO[]; /** * @type string */ - tier: string; -} + status: string; +}; -export interface ZeustypesHostDTO { - /** - * @type boolean - */ - is_default: boolean; +export type GetOrgPreferencePathParameters = { + name: string; +}; +export type GetOrgPreference200 = { + data: PreferencetypesPreferenceDTO; /** * @type string */ + status: string; +}; + +export type UpdateOrgPreferencePathParameters = { name: string; +}; +export type GetPublicDashboardDataPathParameters = { + id: string; +}; +export type GetPublicDashboardData200 = { + data: DashboardtypesGettablePublicDashboardDataDTO; /** * @type string */ - url: string; -} + status: string; +}; -export interface ZeustypesPostableHostDTO { +export type GetPublicDashboardWidgetQueryRangePathParameters = { + id: string; + idx: string; +}; +export type GetPublicDashboardWidgetQueryRange200 = { + data: Querybuildertypesv5QueryRangeResponseDTO; /** * @type string */ - name: string; -} + status: string; +}; -export interface ZeustypesPostableProfileDTO { - /** - * @type string - */ - existing_observability_tool: string; +export type ListRoles200 = { /** - * @type boolean + * @type array */ - has_existing_observability_tool: boolean; + data: AuthtypesRoleDTO[]; /** - * @type integer - * @format int64 + * @type string */ - logs_scale_per_day_in_gb: number; + status: string; +}; + +export type CreateRole201 = { + data: TypesIdentifiableDTO; /** - * @type integer - * @format int64 + * @type string */ - number_of_hosts: number; + status: string; +}; + +export type DeleteRolePathParameters = { + id: string; +}; +export type GetRolePathParameters = { + id: string; +}; +export type GetRole200 = { + data: AuthtypesRoleDTO; /** - * @type integer - * @format int64 + * @type string */ - number_of_services: number; + status: string; +}; + +export type PatchRolePathParameters = { + id: string; +}; +export type GetObjectsPathParameters = { + id: string; + relation: string; +}; +export type GetObjects200 = { /** * @type array - * @nullable true */ - reasons_for_interest_in_signoz: string[] | null; + data: AuthtypesGettableObjectsDTO[]; /** * @type string */ - timeline_for_migrating_to_signoz: string; + status: string; +}; + +export type PatchObjectsPathParameters = { + id: string; + relation: string; +}; +export type ListServiceAccounts200 = { /** - * @type boolean + * @type array */ - uses_otel: boolean; + data: ServiceaccounttypesServiceAccountDTO[]; /** * @type string */ - where_did_you_discover_signoz: string; -} + status: string; +}; -export type AuthzCheck200 = { - /** - * @type array - */ - data: AuthtypesGettableTransactionDTO[]; +export type CreateServiceAccount201 = { + data: TypesIdentifiableDTO; /** * @type string */ status: string; }; -export type AuthzResources200 = { - data: AuthtypesGettableResourcesDTO; +export type DeleteServiceAccountPathParameters = { + id: string; +}; +export type GetServiceAccountPathParameters = { + id: string; +}; +export type GetServiceAccount200 = { + data: ServiceaccounttypesServiceAccountWithRolesDTO; /** * @type string */ status: string; }; -export type ChangePasswordPathParameters = { +export type UpdateServiceAccountPathParameters = { id: string; }; -export type CreateSessionByGoogleCallback303 = { - data: AuthtypesGettableTokenDTO; +export type ListServiceAccountKeysPathParameters = { + id: string; +}; +export type ListServiceAccountKeys200 = { + /** + * @type array + */ + data: ServiceaccounttypesGettableFactorAPIKeyDTO[]; /** * @type string */ status: string; }; -export type CreateSessionByOIDCCallback303 = { - data: AuthtypesGettableTokenDTO; +export type CreateServiceAccountKeyPathParameters = { + id: string; +}; +export type CreateServiceAccountKey201 = { + data: ServiceaccounttypesGettableFactorAPIKeyWithKeyDTO; /** * @type string */ status: string; }; -export type CreateSessionBySAMLCallbackParams = { +export type RevokeServiceAccountKeyPathParameters = { + id: string; + fid: string; +}; +export type UpdateServiceAccountKeyPathParameters = { + id: string; + fid: string; +}; +export type GetServiceAccountRolesPathParameters = { + id: string; +}; +export type GetServiceAccountRoles200 = { /** - * @type string - * @description undefined + * @type array + * @nullable true */ - RelayState?: string; + data: AuthtypesRoleDTO[] | null; /** * @type string - * @description undefined */ - SAMLResponse?: string; + status: string; }; -export type CreateSessionBySAMLCallbackBody = { +export type CreateServiceAccountRolePathParameters = { + id: string; +}; +export type CreateServiceAccountRole201 = { + data: TypesIdentifiableDTO; /** * @type string */ - RelayState?: string; + status: string; +}; + +export type DeleteServiceAccountRolePathParameters = { + id: string; + rid: string; +}; +export type GetMyServiceAccount200 = { + data: ServiceaccounttypesServiceAccountWithRolesDTO; /** * @type string */ - SAMLResponse?: string; + status: string; }; -export type CreateSessionBySAMLCallback303 = { - data: AuthtypesGettableTokenDTO; +export type ListUsersDeprecated200 = { + /** + * @type array + */ + data: TypesDeprecatedUserDTO[]; /** * @type string */ status: string; }; -export type DeletePublicDashboardPathParameters = { +export type DeleteUserPathParameters = { id: string; }; -export type GetPublicDashboardPathParameters = { +export type GetUserDeprecatedPathParameters = { id: string; }; -export type GetPublicDashboard200 = { - data: DashboardtypesGettablePublicDasbhboardDTO; +export type GetUserDeprecated200 = { + data: TypesDeprecatedUserDTO; /** * @type string */ status: string; }; -export type CreatePublicDashboardPathParameters = { +export type UpdateUserDeprecatedPathParameters = { id: string; }; -export type CreatePublicDashboard201 = { - data: TypesIdentifiableDTO; +export type UpdateUserDeprecated200 = { + data: TypesDeprecatedUserDTO; /** * @type string */ status: string; }; -export type UpdatePublicDashboardPathParameters = { - id: string; +export type GetMyUserDeprecated200 = { + data: TypesDeprecatedUserDTO; + /** + * @type string + */ + status: string; }; -export type ListAuthDomains200 = { + +export type ListUserPreferences200 = { /** * @type array */ - data: AuthtypesGettableAuthDomainDTO[]; + data: PreferencetypesPreferenceDTO[]; /** * @type string */ status: string; }; -export type CreateAuthDomain200 = { - data: AuthtypesGettableAuthDomainDTO; +export type GetUserPreferencePathParameters = { + name: string; +}; +export type GetUserPreference200 = { + data: PreferencetypesPreferenceDTO; /** * @type string */ status: string; }; -export type DeleteAuthDomainPathParameters = { - id: string; -}; -export type UpdateAuthDomainPathParameters = { - id: string; +export type UpdateUserPreferencePathParameters = { + name: string; }; -export type GetFieldsKeysParams = { +export type GetFeatures200 = { /** - * @description undefined + * @type array */ - signal?: TelemetrytypesSignalDTO; + data: FeaturetypesGettableFeatureDTO[]; /** - * @description undefined + * @type string */ - source?: TelemetrytypesSourceDTO; + status: string; +}; + +export type GetIngestionKeysParams = { /** * @type integer * @description undefined */ - limit?: number; + page?: number; /** * @type integer - * @format int64 * @description undefined */ - startUnixMilli?: number; + per_page?: number; +}; + +export type GetIngestionKeys200 = { + data: GatewaytypesGettableIngestionKeysDTO; /** - * @type integer - * @format int64 - * @description undefined + * @type string */ - endUnixMilli?: number; + status: string; +}; + +export type CreateIngestionKey201 = { + data: GatewaytypesGettableCreatedIngestionKeyDTO; + /** + * @type string + */ + status: string; +}; + +export type DeleteIngestionKeyPathParameters = { + keyId: string; +}; +export type UpdateIngestionKeyPathParameters = { + keyId: string; +}; +export type CreateIngestionKeyLimitPathParameters = { + keyId: string; +}; +export type CreateIngestionKeyLimit201 = { + data: GatewaytypesGettableCreatedIngestionKeyLimitDTO; + /** + * @type string + */ + status: string; +}; + +export type DeleteIngestionKeyLimitPathParameters = { + limitId: string; +}; +export type UpdateIngestionKeyLimitPathParameters = { + limitId: string; +}; +export type SearchIngestionKeysParams = { /** + * @type string * @description undefined */ - fieldContext?: TelemetrytypesFieldContextDTO; + name: string; /** + * @type integer * @description undefined */ - fieldDataType?: TelemetrytypesFieldDataTypeDTO; + page?: number; /** - * @type string + * @type integer * @description undefined */ - metricName?: string; + per_page?: number; +}; + +export type SearchIngestionKeys200 = { + data: GatewaytypesGettableIngestionKeysDTO; /** * @type string - * @description undefined */ - searchText?: string; + status: string; }; -export type GetFieldsKeys200 = { - data: TelemetrytypesGettableFieldKeysDTO; +export type Healthz200 = { + data: FactoryResponseDTO; /** * @type string */ status: string; }; -export type GetFieldsValuesParams = { +export type Healthz503 = { + data: FactoryResponseDTO; /** - * @description undefined + * @type string */ - signal?: TelemetrytypesSignalDTO; + status: string; +}; + +export type Livez200 = { + data: FactoryResponseDTO; /** - * @description undefined + * @type string */ - source?: TelemetrytypesSourceDTO; + status: string; +}; + +export type ListMetricsParams = { /** * @type integer + * @nullable true * @description undefined */ - limit?: number; + start?: number | null; /** * @type integer - * @format int64 + * @nullable true * @description undefined */ - startUnixMilli?: number; + end?: number | null; /** * @type integer - * @format int64 - * @description undefined - */ - endUnixMilli?: number; - /** * @description undefined */ - fieldContext?: TelemetrytypesFieldContextDTO; - /** - * @description undefined - */ - fieldDataType?: TelemetrytypesFieldDataTypeDTO; - /** - * @type string - * @description undefined - */ - metricName?: string; + limit?: number; /** * @type string * @description undefined @@ -3007,572 +4260,492 @@ export type GetFieldsValuesParams = { * @type string * @description undefined */ - name?: string; - /** - * @type string - * @description undefined - */ - existingQuery?: string; + source?: string; }; -export type GetFieldsValues200 = { - data: TelemetrytypesGettableFieldValuesDTO; +export type ListMetrics200 = { + data: MetricsexplorertypesListMetricsResponseDTO; /** * @type string */ status: string; }; -export type GetResetPasswordTokenPathParameters = { - id: string; +export type GetMetricAlertsPathParameters = { + metricName: string; }; -export type GetResetPasswordToken200 = { - data: TypesResetPasswordTokenDTO; +export type GetMetricAlerts200 = { + data: MetricsexplorertypesMetricAlertsResponseDTO; /** * @type string */ status: string; }; -export type GetGlobalConfig200 = { - data: GlobaltypesConfigDTO; +export type GetMetricAttributesPathParameters = { + metricName: string; +}; +export type GetMetricAttributesParams = { /** - * @type string + * @type integer + * @nullable true + * @description undefined */ - status: string; + start?: number | null; + /** + * @type integer + * @nullable true + * @description undefined + */ + end?: number | null; }; -export type CreateInvite201 = { - data: TypesInviteDTO; +export type GetMetricAttributes200 = { + data: MetricsexplorertypesMetricAttributesResponseDTO; /** * @type string */ status: string; }; -export type ListPromotedAndIndexedPaths200 = { - /** - * @type array - * @nullable true - */ - data: PromotetypesPromotePathDTO[] | null; +export type GetMetricDashboardsPathParameters = { + metricName: string; +}; +export type GetMetricDashboards200 = { + data: MetricsexplorertypesMetricDashboardsResponseDTO; /** * @type string */ status: string; }; -export type ListOrgPreferences200 = { - /** - * @type array - */ - data: PreferencetypesPreferenceDTO[]; +export type GetMetricHighlightsPathParameters = { + metricName: string; +}; +export type GetMetricHighlights200 = { + data: MetricsexplorertypesMetricHighlightsResponseDTO; /** * @type string */ status: string; }; -export type GetOrgPreferencePathParameters = { - name: string; +export type GetMetricMetadataPathParameters = { + metricName: string; }; -export type GetOrgPreference200 = { - data: PreferencetypesPreferenceDTO; +export type GetMetricMetadata200 = { + data: MetricsexplorertypesMetricMetadataDTO; /** * @type string */ status: string; }; -export type UpdateOrgPreferencePathParameters = { - name: string; +export type UpdateMetricMetadataPathParameters = { + metricName: string; }; -export type ListAPIKeys200 = { - /** - * @type array - */ - data: TypesGettableAPIKeyDTO[]; +export type InspectMetrics200 = { + data: MetricsexplorertypesInspectMetricsResponseDTO; /** * @type string */ status: string; }; -export type CreateAPIKey201 = { - data: TypesGettableAPIKeyDTO; +export type GetMetricsOnboardingStatus200 = { + data: MetricsexplorertypesMetricsOnboardingResponseDTO; /** * @type string */ status: string; }; -export type RevokeAPIKeyPathParameters = { - id: string; -}; -export type UpdateAPIKeyPathParameters = { - id: string; -}; -export type GetPublicDashboardDataPathParameters = { - id: string; -}; -export type GetPublicDashboardData200 = { - data: DashboardtypesGettablePublicDashboardDataDTO; +export type GetMetricsStats200 = { + data: MetricsexplorertypesStatsResponseDTO; /** * @type string */ status: string; }; -export type GetPublicDashboardWidgetQueryRangePathParameters = { - id: string; - idx: string; -}; -export type GetPublicDashboardWidgetQueryRange200 = { - data: Querybuildertypesv5QueryRangeResponseDTO; +export type GetMetricsTreemap200 = { + data: MetricsexplorertypesTreemapResponseDTO; /** * @type string */ status: string; }; -export type ListRoles200 = { - /** - * @type array - */ - data: AuthtypesRoleDTO[]; +export type GetMyOrganization200 = { + data: TypesOrganizationDTO; /** * @type string */ status: string; }; -export type CreateRole201 = { - data: TypesIdentifiableDTO; +export type Readyz200 = { + data: FactoryResponseDTO; /** * @type string */ status: string; }; -export type DeleteRolePathParameters = { - id: string; -}; -export type GetRolePathParameters = { - id: string; -}; -export type GetRole200 = { - data: AuthtypesRoleDTO; +export type Readyz503 = { + data: FactoryResponseDTO; /** * @type string */ status: string; }; -export type PatchRolePathParameters = { - id: string; -}; -export type GetObjectsPathParameters = { +export type GetUsersByRoleIDPathParameters = { id: string; - relation: string; }; -export type GetObjects200 = { +export type GetUsersByRoleID200 = { /** * @type array */ - data: AuthtypesGettableObjectsDTO[]; + data: TypesUserDTO[]; /** * @type string */ status: string; }; -export type PatchObjectsPathParameters = { +export type GetRuleHistoryFilterKeysPathParameters = { id: string; - relation: string; }; -export type ListServiceAccounts200 = { +export type GetRuleHistoryFilterKeysParams = { /** - * @type array + * @description undefined */ - data: ServiceaccounttypesServiceAccountDTO[]; + signal?: TelemetrytypesSignalDTO; /** - * @type string + * @description undefined */ - status: string; -}; - -export type CreateServiceAccount201 = { - data: TypesIdentifiableDTO; + source?: TelemetrytypesSourceDTO; /** - * @type string + * @type integer + * @description undefined */ - status: string; -}; - -export type DeleteServiceAccountPathParameters = { - id: string; -}; -export type GetServiceAccountPathParameters = { - id: string; -}; -export type GetServiceAccount200 = { - data: ServiceaccounttypesServiceAccountDTO; + limit?: number; /** - * @type string + * @type integer + * @format int64 + * @description undefined */ - status: string; -}; - -export type UpdateServiceAccountPathParameters = { - id: string; -}; -export type ListServiceAccountKeysPathParameters = { - id: string; -}; -export type ListServiceAccountKeys200 = { + startUnixMilli?: number; /** - * @type array + * @type integer + * @format int64 + * @description undefined */ - data: ServiceaccounttypesFactorAPIKeyDTO[]; + endUnixMilli?: number; /** - * @type string + * @description undefined */ - status: string; -}; - -export type CreateServiceAccountKeyPathParameters = { - id: string; -}; -export type CreateServiceAccountKey201 = { - data: ServiceaccounttypesGettableFactorAPIKeyWithKeyDTO; + fieldContext?: TelemetrytypesFieldContextDTO; /** - * @type string + * @description undefined */ - status: string; -}; - -export type RevokeServiceAccountKeyPathParameters = { - id: string; - fid: string; -}; -export type UpdateServiceAccountKeyPathParameters = { - id: string; - fid: string; -}; -export type UpdateServiceAccountStatusPathParameters = { - id: string; -}; -export type ListUsers200 = { + fieldDataType?: TelemetrytypesFieldDataTypeDTO; /** - * @type array + * @type string + * @description undefined */ - data: TypesUserDTO[]; + metricName?: string; /** * @type string + * @description undefined */ - status: string; + searchText?: string; }; -export type DeleteUserPathParameters = { - id: string; -}; -export type GetUserPathParameters = { - id: string; -}; -export type GetUser200 = { - data: TypesUserDTO; +export type GetRuleHistoryFilterKeys200 = { + data: TelemetrytypesGettableFieldKeysDTO; /** * @type string */ status: string; }; -export type UpdateUserPathParameters = { +export type GetRuleHistoryFilterValuesPathParameters = { id: string; }; -export type UpdateUser200 = { - data: TypesUserDTO; +export type GetRuleHistoryFilterValuesParams = { /** - * @type string + * @description undefined */ - status: string; -}; - -export type GetMyUser200 = { - data: TypesUserDTO; + signal?: TelemetrytypesSignalDTO; /** - * @type string + * @description undefined */ - status: string; -}; - -export type ListUserPreferences200 = { + source?: TelemetrytypesSourceDTO; /** - * @type array + * @type integer + * @description undefined */ - data: PreferencetypesPreferenceDTO[]; + limit?: number; /** - * @type string + * @type integer + * @format int64 + * @description undefined */ - status: string; -}; - -export type GetUserPreferencePathParameters = { - name: string; -}; -export type GetUserPreference200 = { - data: PreferencetypesPreferenceDTO; + startUnixMilli?: number; /** - * @type string + * @type integer + * @format int64 + * @description undefined */ - status: string; -}; - -export type UpdateUserPreferencePathParameters = { - name: string; -}; -export type GetFeatures200 = { + endUnixMilli?: number; /** - * @type array + * @description undefined */ - data: FeaturetypesGettableFeatureDTO[]; + fieldContext?: TelemetrytypesFieldContextDTO; + /** + * @description undefined + */ + fieldDataType?: TelemetrytypesFieldDataTypeDTO; /** * @type string + * @description undefined */ - status: string; -}; - -export type GetIngestionKeysParams = { + metricName?: string; /** - * @type integer + * @type string * @description undefined */ - page?: number; + searchText?: string; /** - * @type integer + * @type string * @description undefined */ - per_page?: number; -}; - -export type GetIngestionKeys200 = { - data: GatewaytypesGettableIngestionKeysDTO; + name?: string; /** * @type string + * @description undefined */ - status: string; + existingQuery?: string; }; -export type CreateIngestionKey201 = { - data: GatewaytypesGettableCreatedIngestionKeyDTO; +export type GetRuleHistoryFilterValues200 = { + data: TelemetrytypesGettableFieldValuesDTO; /** * @type string */ status: string; }; -export type DeleteIngestionKeyPathParameters = { - keyId: string; -}; -export type UpdateIngestionKeyPathParameters = { - keyId: string; +export type GetRuleHistoryOverallStatusPathParameters = { + id: string; }; -export type CreateIngestionKeyLimitPathParameters = { - keyId: string; +export type GetRuleHistoryOverallStatusParams = { + /** + * @type integer + * @format int64 + * @description undefined + */ + start: number; + /** + * @type integer + * @format int64 + * @description undefined + */ + end: number; }; -export type CreateIngestionKeyLimit201 = { - data: GatewaytypesGettableCreatedIngestionKeyLimitDTO; + +export type GetRuleHistoryOverallStatus200 = { + /** + * @type array + * @nullable true + */ + data: RulestatehistorytypesGettableRuleStateWindowDTO[] | null; /** * @type string */ status: string; }; -export type DeleteIngestionKeyLimitPathParameters = { - limitId: string; -}; -export type UpdateIngestionKeyLimitPathParameters = { - limitId: string; +export type GetRuleHistoryStatsPathParameters = { + id: string; }; -export type SearchIngestionKeysParams = { - /** - * @type string - * @description undefined - */ - name: string; +export type GetRuleHistoryStatsParams = { /** * @type integer + * @format int64 * @description undefined */ - page?: number; + start: number; /** * @type integer + * @format int64 * @description undefined */ - per_page?: number; + end: number; }; -export type SearchIngestionKeys200 = { - data: GatewaytypesGettableIngestionKeysDTO; +export type GetRuleHistoryStats200 = { + data: RulestatehistorytypesGettableRuleStateHistoryStatsDTO; /** * @type string */ status: string; }; -export type ListMetricsParams = { +export type GetRuleHistoryTimelinePathParameters = { + id: string; +}; +export type GetRuleHistoryTimelineParams = { /** * @type integer - * @nullable true + * @format int64 * @description undefined */ - start?: number | null; + start: number; /** * @type integer - * @nullable true + * @format int64 * @description undefined */ - end?: number | null; + end: number; /** - * @type integer * @description undefined */ - limit?: number; + state?: RuletypesAlertStateDTO; /** * @type string * @description undefined */ - searchText?: string; + filterExpression?: string; /** - * @type string + * @type integer + * @format int64 * @description undefined */ - source?: string; -}; - -export type ListMetrics200 = { - data: MetricsexplorertypesListMetricsResponseDTO; + limit?: number; + /** + * @description undefined + */ + order?: Querybuildertypesv5OrderDirectionDTO; /** * @type string + * @description undefined */ - status: string; + cursor?: string; }; -export type GetMetricAlertsPathParameters = { - metricName: string; -}; -export type GetMetricAlerts200 = { - data: MetricsexplorertypesMetricAlertsResponseDTO; +export type GetRuleHistoryTimeline200 = { + data: RulestatehistorytypesGettableRuleStateTimelineDTO; /** * @type string */ status: string; }; -export type GetMetricAttributesPathParameters = { - metricName: string; +export type GetRuleHistoryTopContributorsPathParameters = { + id: string; }; -export type GetMetricAttributesParams = { +export type GetRuleHistoryTopContributorsParams = { /** * @type integer - * @nullable true + * @format int64 * @description undefined */ - start?: number | null; + start: number; /** * @type integer - * @nullable true + * @format int64 * @description undefined */ - end?: number | null; + end: number; }; -export type GetMetricAttributes200 = { - data: MetricsexplorertypesMetricAttributesResponseDTO; +export type GetRuleHistoryTopContributors200 = { /** - * @type string + * @type array + * @nullable true */ - status: string; -}; - -export type GetMetricDashboardsPathParameters = { - metricName: string; -}; -export type GetMetricDashboards200 = { - data: MetricsexplorertypesMetricDashboardsResponseDTO; + data: RulestatehistorytypesGettableRuleStateHistoryContributorDTO[] | null; /** * @type string */ status: string; }; -export type GetMetricHighlightsPathParameters = { - metricName: string; -}; -export type GetMetricHighlights200 = { - data: MetricsexplorertypesMetricHighlightsResponseDTO; +export type GetSessionContext200 = { + data: AuthtypesSessionContextDTO; /** * @type string */ status: string; }; -export type GetMetricMetadataPathParameters = { - metricName: string; -}; -export type GetMetricMetadata200 = { - data: MetricsexplorertypesMetricMetadataDTO; +export type CreateSessionByEmailPassword200 = { + data: AuthtypesGettableTokenDTO; /** * @type string */ status: string; }; -export type UpdateMetricMetadataPathParameters = { - metricName: string; -}; -export type GetMetricsStats200 = { - data: MetricsexplorertypesStatsResponseDTO; +export type RotateSession200 = { + data: AuthtypesGettableTokenDTO; /** * @type string */ status: string; }; -export type GetMetricsTreemap200 = { - data: MetricsexplorertypesTreemapResponseDTO; +export type ListUsers200 = { /** - * @type string + * @type array */ - status: string; -}; - -export type GetMyOrganization200 = { - data: TypesOrganizationDTO; + data: TypesUserDTO[]; /** * @type string */ status: string; }; -export type GetSessionContext200 = { - data: AuthtypesSessionContextDTO; +export type GetUserPathParameters = { + id: string; +}; +export type GetUser200 = { + data: AuthtypesUserWithRolesDTO; /** * @type string */ status: string; }; -export type CreateSessionByEmailPassword200 = { - data: AuthtypesGettableTokenDTO; +export type UpdateUserPathParameters = { + id: string; +}; +export type GetRolesByUserIDPathParameters = { + id: string; +}; +export type GetRolesByUserID200 = { + /** + * @type array + */ + data: AuthtypesRoleDTO[]; /** * @type string */ status: string; }; -export type RotateSession200 = { - data: AuthtypesGettableTokenDTO; +export type SetRoleByUserIDPathParameters = { + id: string; +}; +export type RemoveUserRoleByUserIDAndRoleIDPathParameters = { + id: string; + roleId: string; +}; +export type GetMyUser200 = { + data: AuthtypesUserWithRolesDTO; /** * @type string */ diff --git a/frontend/src/api/generated/services/users/index.ts b/frontend/src/api/generated/services/users/index.ts index 7fcccb7e62b..bf110e7bde5 100644 --- a/frontend/src/api/generated/services/users/index.ts +++ b/frontend/src/api/generated/services/users/index.ts @@ -21,28 +21,35 @@ import type { BodyType, ErrorType } from '../../../generatedAPIInstance'; import { GeneratedAPIInstance } from '../../../generatedAPIInstance'; import type { ChangePasswordPathParameters, - CreateAPIKey201, CreateInvite201, DeleteUserPathParameters, GetMyUser200, + GetMyUserDeprecated200, GetResetPasswordToken200, GetResetPasswordTokenPathParameters, + GetRolesByUserID200, + GetRolesByUserIDPathParameters, GetUser200, + GetUserDeprecated200, + GetUserDeprecatedPathParameters, GetUserPathParameters, - ListAPIKeys200, + GetUsersByRoleID200, + GetUsersByRoleIDPathParameters, ListUsers200, + ListUsersDeprecated200, + RemoveUserRoleByUserIDAndRoleIDPathParameters, RenderErrorResponseDTO, - RevokeAPIKeyPathParameters, + SetRoleByUserIDPathParameters, TypesChangePasswordRequestDTO, - TypesPostableAPIKeyDTO, + TypesDeprecatedUserDTO, TypesPostableBulkInviteRequestDTO, TypesPostableForgotPasswordDTO, TypesPostableInviteDTO, TypesPostableResetPasswordDTO, - TypesStorableAPIKeyDTO, - TypesUserDTO, - UpdateAPIKeyPathParameters, - UpdateUser200, + TypesPostableRoleDTO, + TypesUpdatableUserDTO, + UpdateUserDeprecated200, + UpdateUserDeprecatedPathParameters, UpdateUserPathParameters, } from '../sigNoz.schemas'; @@ -416,66 +423,149 @@ export const useCreateBulkInvite = < return useMutation(mutationOptions); }; /** - * This endpoint lists all api keys - * @summary List api keys + * This endpoint resets the password by token + * @summary Reset password + */ +export const resetPassword = ( + typesPostableResetPasswordDTO: BodyType, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v1/resetPassword`, + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + data: typesPostableResetPasswordDTO, + signal, + }); +}; + +export const getResetPasswordMutationOptions = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: BodyType }, + TContext + >; +}): UseMutationOptions< + Awaited>, + TError, + { data: BodyType }, + TContext +> => { + const mutationKey = ['resetPassword']; + 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>, + { data: BodyType } + > = (props) => { + const { data } = props ?? {}; + + return resetPassword(data); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type ResetPasswordMutationResult = NonNullable< + Awaited> +>; +export type ResetPasswordMutationBody = BodyType; +export type ResetPasswordMutationError = ErrorType; + +/** + * @summary Reset password + */ +export const useResetPassword = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: BodyType }, + TContext + >; +}): UseMutationResult< + Awaited>, + TError, + { data: BodyType }, + TContext +> => { + const mutationOptions = getResetPasswordMutationOptions(options); + + return useMutation(mutationOptions); +}; +/** + * This endpoint lists all users + * @summary List users */ -export const listAPIKeys = (signal?: AbortSignal) => { - return GeneratedAPIInstance({ - url: `/api/v1/pats`, +export const listUsersDeprecated = (signal?: AbortSignal) => { + return GeneratedAPIInstance({ + url: `/api/v1/user`, method: 'GET', signal, }); }; -export const getListAPIKeysQueryKey = () => { - return [`/api/v1/pats`] as const; +export const getListUsersDeprecatedQueryKey = () => { + return [`/api/v1/user`] as const; }; -export const getListAPIKeysQueryOptions = < - TData = Awaited>, +export const getListUsersDeprecatedQueryOptions = < + TData = Awaited>, TError = ErrorType >(options?: { query?: UseQueryOptions< - Awaited>, + Awaited>, TError, TData >; }) => { const { query: queryOptions } = options ?? {}; - const queryKey = queryOptions?.queryKey ?? getListAPIKeysQueryKey(); + const queryKey = queryOptions?.queryKey ?? getListUsersDeprecatedQueryKey(); - const queryFn: QueryFunction>> = ({ - signal, - }) => listAPIKeys(signal); + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => listUsersDeprecated(signal); return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< - Awaited>, + Awaited>, TError, TData > & { queryKey: QueryKey }; }; -export type ListAPIKeysQueryResult = NonNullable< - Awaited> +export type ListUsersDeprecatedQueryResult = NonNullable< + Awaited> >; -export type ListAPIKeysQueryError = ErrorType; +export type ListUsersDeprecatedQueryError = ErrorType; /** - * @summary List api keys + * @summary List users */ -export function useListAPIKeys< - TData = Awaited>, +export function useListUsersDeprecated< + TData = Awaited>, TError = ErrorType >(options?: { query?: UseQueryOptions< - Awaited>, + Awaited>, TError, TData >; }): UseQueryResult & { queryKey: QueryKey } { - const queryOptions = getListAPIKeysQueryOptions(options); + const queryOptions = getListUsersDeprecatedQueryOptions(options); const query = useQuery(queryOptions) as UseQueryResult & { queryKey: QueryKey; @@ -487,14 +577,14 @@ export function useListAPIKeys< } /** - * @summary List api keys + * @summary List users */ -export const invalidateListAPIKeys = async ( +export const invalidateListUsersDeprecated = async ( queryClient: QueryClient, options?: InvalidateOptions, ): Promise => { await queryClient.invalidateQueries( - { queryKey: getListAPIKeysQueryKey() }, + { queryKey: getListUsersDeprecatedQueryKey() }, options, ); @@ -502,39 +592,33 @@ export const invalidateListAPIKeys = async ( }; /** - * This endpoint creates an api key - * @summary Create api key + * This endpoint deletes the user by id + * @summary Delete user */ -export const createAPIKey = ( - typesPostableAPIKeyDTO: BodyType, - signal?: AbortSignal, -) => { - return GeneratedAPIInstance({ - url: `/api/v1/pats`, - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - data: typesPostableAPIKeyDTO, - signal, +export const deleteUser = ({ id }: DeleteUserPathParameters) => { + return GeneratedAPIInstance({ + url: `/api/v1/user/${id}`, + method: 'DELETE', }); }; -export const getCreateAPIKeyMutationOptions = < +export const getDeleteUserMutationOptions = < TError = ErrorType, TContext = unknown >(options?: { mutation?: UseMutationOptions< - Awaited>, + Awaited>, TError, - { data: BodyType }, + { pathParams: DeleteUserPathParameters }, TContext >; }): UseMutationOptions< - Awaited>, + Awaited>, TError, - { data: BodyType }, + { pathParams: DeleteUserPathParameters }, TContext > => { - const mutationKey = ['createAPIKey']; + const mutationKey = ['deleteUser']; const { mutation: mutationOptions } = options ? options.mutation && 'mutationKey' in options.mutation && @@ -544,162 +628,189 @@ export const getCreateAPIKeyMutationOptions = < : { mutation: { mutationKey } }; const mutationFn: MutationFunction< - Awaited>, - { data: BodyType } + Awaited>, + { pathParams: DeleteUserPathParameters } > = (props) => { - const { data } = props ?? {}; + const { pathParams } = props ?? {}; - return createAPIKey(data); + return deleteUser(pathParams); }; return { mutationFn, ...mutationOptions }; }; -export type CreateAPIKeyMutationResult = NonNullable< - Awaited> +export type DeleteUserMutationResult = NonNullable< + Awaited> >; -export type CreateAPIKeyMutationBody = BodyType; -export type CreateAPIKeyMutationError = ErrorType; + +export type DeleteUserMutationError = ErrorType; /** - * @summary Create api key + * @summary Delete user */ -export const useCreateAPIKey = < +export const useDeleteUser = < TError = ErrorType, TContext = unknown >(options?: { mutation?: UseMutationOptions< - Awaited>, + Awaited>, TError, - { data: BodyType }, + { pathParams: DeleteUserPathParameters }, TContext >; }): UseMutationResult< - Awaited>, + Awaited>, TError, - { data: BodyType }, + { pathParams: DeleteUserPathParameters }, TContext > => { - const mutationOptions = getCreateAPIKeyMutationOptions(options); + const mutationOptions = getDeleteUserMutationOptions(options); return useMutation(mutationOptions); }; /** - * This endpoint revokes an api key - * @summary Revoke api key + * This endpoint returns the user by id + * @summary Get user */ -export const revokeAPIKey = ({ id }: RevokeAPIKeyPathParameters) => { - return GeneratedAPIInstance({ - url: `/api/v1/pats/${id}`, - method: 'DELETE', +export const getUserDeprecated = ( + { id }: GetUserDeprecatedPathParameters, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v1/user/${id}`, + method: 'GET', + signal, }); }; -export const getRevokeAPIKeyMutationOptions = < - TError = ErrorType, - TContext = unknown ->(options?: { - mutation?: UseMutationOptions< - Awaited>, - TError, - { pathParams: RevokeAPIKeyPathParameters }, - TContext - >; -}): UseMutationOptions< - Awaited>, - TError, - { pathParams: RevokeAPIKeyPathParameters }, - TContext -> => { - const mutationKey = ['revokeAPIKey']; - const { mutation: mutationOptions } = options - ? options.mutation && - 'mutationKey' in options.mutation && - options.mutation.mutationKey - ? options - : { ...options, mutation: { ...options.mutation, mutationKey } } - : { mutation: { mutationKey } }; +export const getGetUserDeprecatedQueryKey = ({ + id, +}: GetUserDeprecatedPathParameters) => { + return [`/api/v1/user/${id}`] as const; +}; - const mutationFn: MutationFunction< - Awaited>, - { pathParams: RevokeAPIKeyPathParameters } - > = (props) => { - const { pathParams } = props ?? {}; +export const getGetUserDeprecatedQueryOptions = < + TData = Awaited>, + TError = ErrorType +>( + { id }: GetUserDeprecatedPathParameters, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +) => { + const { query: queryOptions } = options ?? {}; - return revokeAPIKey(pathParams); - }; + const queryKey = + queryOptions?.queryKey ?? getGetUserDeprecatedQueryKey({ id }); - return { mutationFn, ...mutationOptions }; + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getUserDeprecated({ id }, signal); + + return { + queryKey, + queryFn, + enabled: !!id, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; }; -export type RevokeAPIKeyMutationResult = NonNullable< - Awaited> +export type GetUserDeprecatedQueryResult = NonNullable< + Awaited> >; +export type GetUserDeprecatedQueryError = ErrorType; + +/** + * @summary Get user + */ + +export function useGetUserDeprecated< + TData = Awaited>, + TError = ErrorType +>( + { id }: GetUserDeprecatedPathParameters, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getGetUserDeprecatedQueryOptions({ id }, options); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; -export type RevokeAPIKeyMutationError = ErrorType; + return query; +} /** - * @summary Revoke api key + * @summary Get user */ -export const useRevokeAPIKey = < - TError = ErrorType, - TContext = unknown ->(options?: { - mutation?: UseMutationOptions< - Awaited>, - TError, - { pathParams: RevokeAPIKeyPathParameters }, - TContext - >; -}): UseMutationResult< - Awaited>, - TError, - { pathParams: RevokeAPIKeyPathParameters }, - TContext -> => { - const mutationOptions = getRevokeAPIKeyMutationOptions(options); +export const invalidateGetUserDeprecated = async ( + queryClient: QueryClient, + { id }: GetUserDeprecatedPathParameters, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getGetUserDeprecatedQueryKey({ id }) }, + options, + ); - return useMutation(mutationOptions); + return queryClient; }; + /** - * This endpoint updates an api key - * @summary Update api key + * This endpoint updates the user by id + * @summary Update user */ -export const updateAPIKey = ( - { id }: UpdateAPIKeyPathParameters, - typesStorableAPIKeyDTO: BodyType, +export const updateUserDeprecated = ( + { id }: UpdateUserDeprecatedPathParameters, + typesDeprecatedUserDTO: BodyType, ) => { - return GeneratedAPIInstance({ - url: `/api/v1/pats/${id}`, + return GeneratedAPIInstance({ + url: `/api/v1/user/${id}`, method: 'PUT', headers: { 'Content-Type': 'application/json' }, - data: typesStorableAPIKeyDTO, + data: typesDeprecatedUserDTO, }); }; -export const getUpdateAPIKeyMutationOptions = < +export const getUpdateUserDeprecatedMutationOptions = < TError = ErrorType, TContext = unknown >(options?: { mutation?: UseMutationOptions< - Awaited>, + Awaited>, TError, { - pathParams: UpdateAPIKeyPathParameters; - data: BodyType; + pathParams: UpdateUserDeprecatedPathParameters; + data: BodyType; }, TContext >; }): UseMutationOptions< - Awaited>, + Awaited>, TError, { - pathParams: UpdateAPIKeyPathParameters; - data: BodyType; + pathParams: UpdateUserDeprecatedPathParameters; + data: BodyType; }, TContext > => { - const mutationKey = ['updateAPIKey']; + const mutationKey = ['updateUserDeprecated']; const { mutation: mutationOptions } = options ? options.mutation && 'mutationKey' in options.mutation && @@ -709,89 +820,175 @@ export const getUpdateAPIKeyMutationOptions = < : { mutation: { mutationKey } }; const mutationFn: MutationFunction< - Awaited>, + Awaited>, { - pathParams: UpdateAPIKeyPathParameters; - data: BodyType; + pathParams: UpdateUserDeprecatedPathParameters; + data: BodyType; } > = (props) => { const { pathParams, data } = props ?? {}; - return updateAPIKey(pathParams, data); + return updateUserDeprecated(pathParams, data); }; return { mutationFn, ...mutationOptions }; }; -export type UpdateAPIKeyMutationResult = NonNullable< - Awaited> +export type UpdateUserDeprecatedMutationResult = NonNullable< + Awaited> >; -export type UpdateAPIKeyMutationBody = BodyType; -export type UpdateAPIKeyMutationError = ErrorType; +export type UpdateUserDeprecatedMutationBody = BodyType; +export type UpdateUserDeprecatedMutationError = ErrorType; /** - * @summary Update api key + * @summary Update user */ -export const useUpdateAPIKey = < +export const useUpdateUserDeprecated = < TError = ErrorType, TContext = unknown >(options?: { mutation?: UseMutationOptions< - Awaited>, + Awaited>, TError, { - pathParams: UpdateAPIKeyPathParameters; - data: BodyType; + pathParams: UpdateUserDeprecatedPathParameters; + data: BodyType; }, TContext >; }): UseMutationResult< - Awaited>, + Awaited>, TError, { - pathParams: UpdateAPIKeyPathParameters; - data: BodyType; + pathParams: UpdateUserDeprecatedPathParameters; + data: BodyType; }, TContext > => { - const mutationOptions = getUpdateAPIKeyMutationOptions(options); + const mutationOptions = getUpdateUserDeprecatedMutationOptions(options); return useMutation(mutationOptions); }; /** - * This endpoint resets the password by token - * @summary Reset password + * This endpoint returns the user I belong to + * @summary Get my user */ -export const resetPassword = ( - typesPostableResetPasswordDTO: BodyType, +export const getMyUserDeprecated = (signal?: AbortSignal) => { + return GeneratedAPIInstance({ + url: `/api/v1/user/me`, + method: 'GET', + signal, + }); +}; + +export const getGetMyUserDeprecatedQueryKey = () => { + return [`/api/v1/user/me`] as const; +}; + +export const getGetMyUserDeprecatedQueryOptions = < + TData = Awaited>, + TError = ErrorType +>(options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; +}) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetMyUserDeprecatedQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getMyUserDeprecated(signal); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; +}; + +export type GetMyUserDeprecatedQueryResult = NonNullable< + Awaited> +>; +export type GetMyUserDeprecatedQueryError = ErrorType; + +/** + * @summary Get my user + */ + +export function useGetMyUserDeprecated< + TData = Awaited>, + TError = ErrorType +>(options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; +}): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getGetMyUserDeprecatedQueryOptions(options); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Get my user + */ +export const invalidateGetMyUserDeprecated = async ( + queryClient: QueryClient, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getGetMyUserDeprecatedQueryKey() }, + options, + ); + + return queryClient; +}; + +/** + * This endpoint initiates the forgot password flow by sending a reset password email + * @summary Forgot password + */ +export const forgotPassword = ( + typesPostableForgotPasswordDTO: BodyType, signal?: AbortSignal, ) => { return GeneratedAPIInstance({ - url: `/api/v1/resetPassword`, + url: `/api/v2/factor_password/forgot`, method: 'POST', headers: { 'Content-Type': 'application/json' }, - data: typesPostableResetPasswordDTO, + data: typesPostableForgotPasswordDTO, signal, }); }; -export const getResetPasswordMutationOptions = < +export const getForgotPasswordMutationOptions = < TError = ErrorType, TContext = unknown >(options?: { mutation?: UseMutationOptions< - Awaited>, + Awaited>, TError, - { data: BodyType }, + { data: BodyType }, TContext >; }): UseMutationOptions< - Awaited>, + Awaited>, TError, - { data: BodyType }, + { data: BodyType }, TContext > => { - const mutationKey = ['resetPassword']; + const mutationKey = ['forgotPassword']; const { mutation: mutationOptions } = options ? options.mutation && 'mutationKey' in options.mutation && @@ -801,60 +998,163 @@ export const getResetPasswordMutationOptions = < : { mutation: { mutationKey } }; const mutationFn: MutationFunction< - Awaited>, - { data: BodyType } + Awaited>, + { data: BodyType } > = (props) => { const { data } = props ?? {}; - return resetPassword(data); + return forgotPassword(data); }; return { mutationFn, ...mutationOptions }; }; -export type ResetPasswordMutationResult = NonNullable< - Awaited> +export type ForgotPasswordMutationResult = NonNullable< + Awaited> >; -export type ResetPasswordMutationBody = BodyType; -export type ResetPasswordMutationError = ErrorType; +export type ForgotPasswordMutationBody = BodyType; +export type ForgotPasswordMutationError = ErrorType; /** - * @summary Reset password + * @summary Forgot password */ -export const useResetPassword = < +export const useForgotPassword = < TError = ErrorType, TContext = unknown >(options?: { mutation?: UseMutationOptions< - Awaited>, + Awaited>, TError, - { data: BodyType }, + { data: BodyType }, TContext >; }): UseMutationResult< - Awaited>, + Awaited>, TError, - { data: BodyType }, + { data: BodyType }, TContext > => { - const mutationOptions = getResetPasswordMutationOptions(options); + const mutationOptions = getForgotPasswordMutationOptions(options); return useMutation(mutationOptions); }; /** - * This endpoint lists all users - * @summary List users + * This endpoint returns the users having the role by role id + * @summary Get users by role id + */ +export const getUsersByRoleID = ( + { id }: GetUsersByRoleIDPathParameters, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v2/roles/${id}/users`, + method: 'GET', + signal, + }); +}; + +export const getGetUsersByRoleIDQueryKey = ({ + id, +}: GetUsersByRoleIDPathParameters) => { + return [`/api/v2/roles/${id}/users`] as const; +}; + +export const getGetUsersByRoleIDQueryOptions = < + TData = Awaited>, + TError = ErrorType +>( + { id }: GetUsersByRoleIDPathParameters, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetUsersByRoleIDQueryKey({ id }); + + const queryFn: QueryFunction>> = ({ + signal, + }) => getUsersByRoleID({ id }, signal); + + return { + queryKey, + queryFn, + enabled: !!id, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; +}; + +export type GetUsersByRoleIDQueryResult = NonNullable< + Awaited> +>; +export type GetUsersByRoleIDQueryError = ErrorType; + +/** + * @summary Get users by role id + */ + +export function useGetUsersByRoleID< + TData = Awaited>, + TError = ErrorType +>( + { id }: GetUsersByRoleIDPathParameters, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + }, +): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getGetUsersByRoleIDQueryOptions({ id }, options); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Get users by role id + */ +export const invalidateGetUsersByRoleID = async ( + queryClient: QueryClient, + { id }: GetUsersByRoleIDPathParameters, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getGetUsersByRoleIDQueryKey({ id }) }, + options, + ); + + return queryClient; +}; + +/** + * This endpoint lists all users for the organization + * @summary List users v2 */ export const listUsers = (signal?: AbortSignal) => { return GeneratedAPIInstance({ - url: `/api/v1/user`, + url: `/api/v2/users`, method: 'GET', signal, }); }; export const getListUsersQueryKey = () => { - return [`/api/v1/user`] as const; + return [`/api/v2/users`] as const; }; export const getListUsersQueryOptions = < @@ -878,22 +1178,112 @@ export const getListUsersQueryOptions = < > & { queryKey: QueryKey }; }; -export type ListUsersQueryResult = NonNullable< - Awaited> +export type ListUsersQueryResult = NonNullable< + Awaited> +>; +export type ListUsersQueryError = ErrorType; + +/** + * @summary List users v2 + */ + +export function useListUsers< + TData = Awaited>, + TError = ErrorType +>(options?: { + query?: UseQueryOptions>, TError, TData>; +}): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getListUsersQueryOptions(options); + + const query = useQuery(queryOptions) as UseQueryResult & { + queryKey: QueryKey; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary List users v2 + */ +export const invalidateListUsers = async ( + queryClient: QueryClient, + options?: InvalidateOptions, +): Promise => { + await queryClient.invalidateQueries( + { queryKey: getListUsersQueryKey() }, + options, + ); + + return queryClient; +}; + +/** + * This endpoint returns the user by id + * @summary Get user by user id + */ +export const getUser = ( + { id }: GetUserPathParameters, + signal?: AbortSignal, +) => { + return GeneratedAPIInstance({ + url: `/api/v2/users/${id}`, + method: 'GET', + signal, + }); +}; + +export const getGetUserQueryKey = ({ id }: GetUserPathParameters) => { + return [`/api/v2/users/${id}`] as const; +}; + +export const getGetUserQueryOptions = < + TData = Awaited>, + TError = ErrorType +>( + { id }: GetUserPathParameters, + options?: { + query?: UseQueryOptions>, TError, TData>; + }, +) => { + const { query: queryOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetUserQueryKey({ id }); + + const queryFn: QueryFunction>> = ({ + signal, + }) => getUser({ id }, signal); + + return { + queryKey, + queryFn, + enabled: !!id, + ...queryOptions, + } as UseQueryOptions>, TError, TData> & { + queryKey: QueryKey; + }; +}; + +export type GetUserQueryResult = NonNullable< + Awaited> >; -export type ListUsersQueryError = ErrorType; +export type GetUserQueryError = ErrorType; /** - * @summary List users + * @summary Get user by user id */ -export function useListUsers< - TData = Awaited>, +export function useGetUser< + TData = Awaited>, TError = ErrorType ->(options?: { - query?: UseQueryOptions>, TError, TData>; -}): UseQueryResult & { queryKey: QueryKey } { - const queryOptions = getListUsersQueryOptions(options); +>( + { id }: GetUserPathParameters, + options?: { + query?: UseQueryOptions>, TError, TData>; + }, +): UseQueryResult & { queryKey: QueryKey } { + const queryOptions = getGetUserQueryOptions({ id }, options); const query = useQuery(queryOptions) as UseQueryResult & { queryKey: QueryKey; @@ -905,14 +1295,15 @@ export function useListUsers< } /** - * @summary List users + * @summary Get user by user id */ -export const invalidateListUsers = async ( +export const invalidateGetUser = async ( queryClient: QueryClient, + { id }: GetUserPathParameters, options?: InvalidateOptions, ): Promise => { await queryClient.invalidateQueries( - { queryKey: getListUsersQueryKey() }, + { queryKey: getGetUserQueryKey({ id }) }, options, ); @@ -920,33 +1311,44 @@ export const invalidateListUsers = async ( }; /** - * This endpoint deletes the user by id - * @summary Delete user + * This endpoint updates the user by id + * @summary Update user v2 */ -export const deleteUser = ({ id }: DeleteUserPathParameters) => { +export const updateUser = ( + { id }: UpdateUserPathParameters, + typesUpdatableUserDTO: BodyType, +) => { return GeneratedAPIInstance({ - url: `/api/v1/user/${id}`, - method: 'DELETE', + url: `/api/v2/users/${id}`, + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + data: typesUpdatableUserDTO, }); }; -export const getDeleteUserMutationOptions = < +export const getUpdateUserMutationOptions = < TError = ErrorType, TContext = unknown >(options?: { mutation?: UseMutationOptions< - Awaited>, + Awaited>, TError, - { pathParams: DeleteUserPathParameters }, + { + pathParams: UpdateUserPathParameters; + data: BodyType; + }, TContext >; }): UseMutationOptions< - Awaited>, + Awaited>, TError, - { pathParams: DeleteUserPathParameters }, + { + pathParams: UpdateUserPathParameters; + data: BodyType; + }, TContext > => { - const mutationKey = ['deleteUser']; + const mutationKey = ['updateUser']; const { mutation: mutationOptions } = options ? options.mutation && 'mutationKey' in options.mutation && @@ -956,111 +1358,132 @@ export const getDeleteUserMutationOptions = < : { mutation: { mutationKey } }; const mutationFn: MutationFunction< - Awaited>, - { pathParams: DeleteUserPathParameters } + Awaited>, + { + pathParams: UpdateUserPathParameters; + data: BodyType; + } > = (props) => { - const { pathParams } = props ?? {}; + const { pathParams, data } = props ?? {}; - return deleteUser(pathParams); + return updateUser(pathParams, data); }; return { mutationFn, ...mutationOptions }; }; -export type DeleteUserMutationResult = NonNullable< - Awaited> +export type UpdateUserMutationResult = NonNullable< + Awaited> >; - -export type DeleteUserMutationError = ErrorType; +export type UpdateUserMutationBody = BodyType; +export type UpdateUserMutationError = ErrorType; /** - * @summary Delete user + * @summary Update user v2 */ -export const useDeleteUser = < +export const useUpdateUser = < TError = ErrorType, TContext = unknown >(options?: { mutation?: UseMutationOptions< - Awaited>, + Awaited>, TError, - { pathParams: DeleteUserPathParameters }, + { + pathParams: UpdateUserPathParameters; + data: BodyType; + }, TContext >; }): UseMutationResult< - Awaited>, + Awaited>, TError, - { pathParams: DeleteUserPathParameters }, + { + pathParams: UpdateUserPathParameters; + data: BodyType; + }, TContext > => { - const mutationOptions = getDeleteUserMutationOptions(options); + const mutationOptions = getUpdateUserMutationOptions(options); return useMutation(mutationOptions); }; /** - * This endpoint returns the user by id - * @summary Get user + * This endpoint returns the user roles by user id + * @summary Get user roles */ -export const getUser = ( - { id }: GetUserPathParameters, +export const getRolesByUserID = ( + { id }: GetRolesByUserIDPathParameters, signal?: AbortSignal, ) => { - return GeneratedAPIInstance({ - url: `/api/v1/user/${id}`, + return GeneratedAPIInstance({ + url: `/api/v2/users/${id}/roles`, method: 'GET', signal, }); }; -export const getGetUserQueryKey = ({ id }: GetUserPathParameters) => { - return [`/api/v1/user/${id}`] as const; +export const getGetRolesByUserIDQueryKey = ({ + id, +}: GetRolesByUserIDPathParameters) => { + return [`/api/v2/users/${id}/roles`] as const; }; -export const getGetUserQueryOptions = < - TData = Awaited>, +export const getGetRolesByUserIDQueryOptions = < + TData = Awaited>, TError = ErrorType >( - { id }: GetUserPathParameters, + { id }: GetRolesByUserIDPathParameters, options?: { - query?: UseQueryOptions>, TError, TData>; + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; }, ) => { const { query: queryOptions } = options ?? {}; - const queryKey = queryOptions?.queryKey ?? getGetUserQueryKey({ id }); + const queryKey = queryOptions?.queryKey ?? getGetRolesByUserIDQueryKey({ id }); - const queryFn: QueryFunction>> = ({ + const queryFn: QueryFunction>> = ({ signal, - }) => getUser({ id }, signal); + }) => getRolesByUserID({ id }, signal); return { queryKey, queryFn, enabled: !!id, ...queryOptions, - } as UseQueryOptions>, TError, TData> & { - queryKey: QueryKey; - }; + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; }; -export type GetUserQueryResult = NonNullable< - Awaited> +export type GetRolesByUserIDQueryResult = NonNullable< + Awaited> >; -export type GetUserQueryError = ErrorType; +export type GetRolesByUserIDQueryError = ErrorType; /** - * @summary Get user + * @summary Get user roles */ -export function useGetUser< - TData = Awaited>, +export function useGetRolesByUserID< + TData = Awaited>, TError = ErrorType >( - { id }: GetUserPathParameters, + { id }: GetRolesByUserIDPathParameters, options?: { - query?: UseQueryOptions>, TError, TData>; + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; }, ): UseQueryResult & { queryKey: QueryKey } { - const queryOptions = getGetUserQueryOptions({ id }, options); + const queryOptions = getGetRolesByUserIDQueryOptions({ id }, options); const query = useQuery(queryOptions) as UseQueryResult & { queryKey: QueryKey; @@ -1072,15 +1495,15 @@ export function useGetUser< } /** - * @summary Get user + * @summary Get user roles */ -export const invalidateGetUser = async ( +export const invalidateGetRolesByUserID = async ( queryClient: QueryClient, - { id }: GetUserPathParameters, + { id }: GetRolesByUserIDPathParameters, options?: InvalidateOptions, ): Promise => { await queryClient.invalidateQueries( - { queryKey: getGetUserQueryKey({ id }) }, + { queryKey: getGetRolesByUserIDQueryKey({ id }) }, options, ); @@ -1088,38 +1511,46 @@ export const invalidateGetUser = async ( }; /** - * This endpoint updates the user by id - * @summary Update user + * This endpoint assigns the role to the user roles by user id + * @summary Set user roles */ -export const updateUser = ( - { id }: UpdateUserPathParameters, - typesUserDTO: BodyType, +export const setRoleByUserID = ( + { id }: SetRoleByUserIDPathParameters, + typesPostableRoleDTO: BodyType, + signal?: AbortSignal, ) => { - return GeneratedAPIInstance({ - url: `/api/v1/user/${id}`, - method: 'PUT', + return GeneratedAPIInstance({ + url: `/api/v2/users/${id}/roles`, + method: 'POST', headers: { 'Content-Type': 'application/json' }, - data: typesUserDTO, + data: typesPostableRoleDTO, + signal, }); }; -export const getUpdateUserMutationOptions = < +export const getSetRoleByUserIDMutationOptions = < TError = ErrorType, TContext = unknown >(options?: { mutation?: UseMutationOptions< - Awaited>, + Awaited>, TError, - { pathParams: UpdateUserPathParameters; data: BodyType }, + { + pathParams: SetRoleByUserIDPathParameters; + data: BodyType; + }, TContext >; }): UseMutationOptions< - Awaited>, + Awaited>, TError, - { pathParams: UpdateUserPathParameters; data: BodyType }, + { + pathParams: SetRoleByUserIDPathParameters; + data: BodyType; + }, TContext > => { - const mutationKey = ['updateUser']; + const mutationKey = ['setRoleByUserID']; const { mutation: mutationOptions } = options ? options.mutation && 'mutationKey' in options.mutation && @@ -1129,60 +1560,151 @@ export const getUpdateUserMutationOptions = < : { mutation: { mutationKey } }; const mutationFn: MutationFunction< - Awaited>, - { pathParams: UpdateUserPathParameters; data: BodyType } + Awaited>, + { + pathParams: SetRoleByUserIDPathParameters; + data: BodyType; + } > = (props) => { const { pathParams, data } = props ?? {}; - return updateUser(pathParams, data); + return setRoleByUserID(pathParams, data); }; return { mutationFn, ...mutationOptions }; }; -export type UpdateUserMutationResult = NonNullable< - Awaited> +export type SetRoleByUserIDMutationResult = NonNullable< + Awaited> >; -export type UpdateUserMutationBody = BodyType; -export type UpdateUserMutationError = ErrorType; +export type SetRoleByUserIDMutationBody = BodyType; +export type SetRoleByUserIDMutationError = ErrorType; /** - * @summary Update user + * @summary Set user roles */ -export const useUpdateUser = < +export const useSetRoleByUserID = < TError = ErrorType, TContext = unknown >(options?: { mutation?: UseMutationOptions< - Awaited>, + Awaited>, TError, - { pathParams: UpdateUserPathParameters; data: BodyType }, + { + pathParams: SetRoleByUserIDPathParameters; + data: BodyType; + }, TContext >; }): UseMutationResult< - Awaited>, + Awaited>, TError, - { pathParams: UpdateUserPathParameters; data: BodyType }, + { + pathParams: SetRoleByUserIDPathParameters; + data: BodyType; + }, TContext > => { - const mutationOptions = getUpdateUserMutationOptions(options); + const mutationOptions = getSetRoleByUserIDMutationOptions(options); + + return useMutation(mutationOptions); +}; +/** + * This endpoint removes a role from the user by user id and role id + * @summary Remove a role from user + */ +export const removeUserRoleByUserIDAndRoleID = ({ + id, + roleId, +}: RemoveUserRoleByUserIDAndRoleIDPathParameters) => { + return GeneratedAPIInstance({ + url: `/api/v2/users/${id}/roles/${roleId}`, + method: 'DELETE', + }); +}; + +export const getRemoveUserRoleByUserIDAndRoleIDMutationOptions = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { pathParams: RemoveUserRoleByUserIDAndRoleIDPathParameters }, + TContext + >; +}): UseMutationOptions< + Awaited>, + TError, + { pathParams: RemoveUserRoleByUserIDAndRoleIDPathParameters }, + TContext +> => { + const mutationKey = ['removeUserRoleByUserIDAndRoleID']; + 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>, + { pathParams: RemoveUserRoleByUserIDAndRoleIDPathParameters } + > = (props) => { + const { pathParams } = props ?? {}; + + return removeUserRoleByUserIDAndRoleID(pathParams); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type RemoveUserRoleByUserIDAndRoleIDMutationResult = NonNullable< + Awaited> +>; + +export type RemoveUserRoleByUserIDAndRoleIDMutationError = ErrorType; + +/** + * @summary Remove a role from user + */ +export const useRemoveUserRoleByUserIDAndRoleID = < + TError = ErrorType, + TContext = unknown +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { pathParams: RemoveUserRoleByUserIDAndRoleIDPathParameters }, + TContext + >; +}): UseMutationResult< + Awaited>, + TError, + { pathParams: RemoveUserRoleByUserIDAndRoleIDPathParameters }, + TContext +> => { + const mutationOptions = getRemoveUserRoleByUserIDAndRoleIDMutationOptions( + options, + ); return useMutation(mutationOptions); }; /** * This endpoint returns the user I belong to - * @summary Get my user + * @summary Get my user v2 */ export const getMyUser = (signal?: AbortSignal) => { return GeneratedAPIInstance({ - url: `/api/v1/user/me`, + url: `/api/v2/users/me`, method: 'GET', signal, }); }; export const getGetMyUserQueryKey = () => { - return [`/api/v1/user/me`] as const; + return [`/api/v2/users/me`] as const; }; export const getGetMyUserQueryOptions = < @@ -1212,7 +1734,7 @@ export type GetMyUserQueryResult = NonNullable< export type GetMyUserQueryError = ErrorType; /** - * @summary Get my user + * @summary Get my user v2 */ export function useGetMyUser< @@ -1233,7 +1755,7 @@ export function useGetMyUser< } /** - * @summary Get my user + * @summary Get my user v2 */ export const invalidateGetMyUser = async ( queryClient: QueryClient, @@ -1248,39 +1770,37 @@ export const invalidateGetMyUser = async ( }; /** - * This endpoint initiates the forgot password flow by sending a reset password email - * @summary Forgot password + * This endpoint updates the user I belong to + * @summary Update my user v2 */ -export const forgotPassword = ( - typesPostableForgotPasswordDTO: BodyType, - signal?: AbortSignal, +export const updateMyUserV2 = ( + typesUpdatableUserDTO: BodyType, ) => { return GeneratedAPIInstance({ - url: `/api/v2/factor_password/forgot`, - method: 'POST', + url: `/api/v2/users/me`, + method: 'PUT', headers: { 'Content-Type': 'application/json' }, - data: typesPostableForgotPasswordDTO, - signal, + data: typesUpdatableUserDTO, }); }; -export const getForgotPasswordMutationOptions = < +export const getUpdateMyUserV2MutationOptions = < TError = ErrorType, TContext = unknown >(options?: { mutation?: UseMutationOptions< - Awaited>, + Awaited>, TError, - { data: BodyType }, + { data: BodyType }, TContext >; }): UseMutationOptions< - Awaited>, + Awaited>, TError, - { data: BodyType }, + { data: BodyType }, TContext > => { - const mutationKey = ['forgotPassword']; + const mutationKey = ['updateMyUserV2']; const { mutation: mutationOptions } = options ? options.mutation && 'mutationKey' in options.mutation && @@ -1290,43 +1810,43 @@ export const getForgotPasswordMutationOptions = < : { mutation: { mutationKey } }; const mutationFn: MutationFunction< - Awaited>, - { data: BodyType } + Awaited>, + { data: BodyType } > = (props) => { const { data } = props ?? {}; - return forgotPassword(data); + return updateMyUserV2(data); }; return { mutationFn, ...mutationOptions }; }; -export type ForgotPasswordMutationResult = NonNullable< - Awaited> +export type UpdateMyUserV2MutationResult = NonNullable< + Awaited> >; -export type ForgotPasswordMutationBody = BodyType; -export type ForgotPasswordMutationError = ErrorType; +export type UpdateMyUserV2MutationBody = BodyType; +export type UpdateMyUserV2MutationError = ErrorType; /** - * @summary Forgot password + * @summary Update my user v2 */ -export const useForgotPassword = < +export const useUpdateMyUserV2 = < TError = ErrorType, TContext = unknown >(options?: { mutation?: UseMutationOptions< - Awaited>, + Awaited>, TError, - { data: BodyType }, + { data: BodyType }, TContext >; }): UseMutationResult< - Awaited>, + Awaited>, TError, - { data: BodyType }, + { data: BodyType }, TContext > => { - const mutationOptions = getForgotPasswordMutationOptions(options); + const mutationOptions = getUpdateMyUserV2MutationOptions(options); return useMutation(mutationOptions); }; diff --git a/frontend/src/api/metricsExplorer/getInspectMetricsDetails.ts b/frontend/src/api/metricsExplorer/getInspectMetricsDetails.ts deleted file mode 100644 index cf6eadea741..00000000000 --- a/frontend/src/api/metricsExplorer/getInspectMetricsDetails.ts +++ /dev/null @@ -1,54 +0,0 @@ -import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; -import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; -import { TagFilter } from 'types/api/queryBuilder/queryBuilderData'; - -export interface InspectMetricsRequest { - metricName: string; - start: number; - end: number; - filters: TagFilter; -} - -export interface InspectMetricsResponse { - status: string; - data: { - series: InspectMetricsSeries[]; - }; -} - -export interface InspectMetricsSeries { - title?: string; - strokeColor?: string; - labels: Record; - labelsArray: Array>; - values: InspectMetricsTimestampValue[]; -} - -interface InspectMetricsTimestampValue { - timestamp: number; - value: string; -} - -export const getInspectMetricsDetails = async ( - request: InspectMetricsRequest, - signal?: AbortSignal, - headers?: Record, -): Promise | ErrorResponse> => { - try { - const response = await axios.post(`/metrics/inspect`, request, { - signal, - headers, - }); - - return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data, - }; - } catch (error) { - return ErrorResponseHandler(error as AxiosError); - } -}; diff --git a/frontend/src/api/metricsExplorer/getMetricDetails.ts b/frontend/src/api/metricsExplorer/getMetricDetails.ts deleted file mode 100644 index 74bde3e6719..00000000000 --- a/frontend/src/api/metricsExplorer/getMetricDetails.ts +++ /dev/null @@ -1,75 +0,0 @@ -import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; -import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; - -import { MetricType } from './getMetricsList'; - -export interface MetricDetails { - name: string; - description: string; - type: string; - unit: string; - timeseries: number; - samples: number; - timeSeriesTotal: number; - timeSeriesActive: number; - lastReceived: string; - attributes: MetricDetailsAttribute[] | null; - metadata?: { - metric_type: MetricType; - description: string; - unit: string; - temporality?: Temporality; - }; - alerts: MetricDetailsAlert[] | null; - dashboards: MetricDetailsDashboard[] | null; -} - -export enum Temporality { - CUMULATIVE = 'Cumulative', - DELTA = 'Delta', -} - -export interface MetricDetailsAttribute { - key: string; - value: string[]; - valueCount: number; -} - -export interface MetricDetailsAlert { - alert_name: string; - alert_id: string; -} - -export interface MetricDetailsDashboard { - dashboard_name: string; - dashboard_id: string; -} - -export interface MetricDetailsResponse { - status: string; - data: MetricDetails; -} - -export const getMetricDetails = async ( - metricName: string, - signal?: AbortSignal, - headers?: Record, -): Promise | ErrorResponse> => { - try { - const response = await axios.get(`/metrics/${metricName}/metadata`, { - signal, - headers, - }); - - return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data, - }; - } catch (error) { - return ErrorResponseHandler(error as AxiosError); - } -}; diff --git a/frontend/src/api/metricsExplorer/getMetricsList.ts b/frontend/src/api/metricsExplorer/getMetricsList.ts deleted file mode 100644 index 63fd5e4da4e..00000000000 --- a/frontend/src/api/metricsExplorer/getMetricsList.ts +++ /dev/null @@ -1,67 +0,0 @@ -import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; -import { AxiosError } from 'axios'; -import { - OrderByPayload, - TreemapViewType, -} from 'container/MetricsExplorer/Summary/types'; -import { ErrorResponse, SuccessResponse } from 'types/api'; -import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; -import { TagFilter } from 'types/api/queryBuilder/queryBuilderData'; - -export interface MetricsListPayload { - filters: TagFilter; - groupBy?: BaseAutocompleteData[]; - offset?: number; - limit?: number; - orderBy?: OrderByPayload; -} - -export enum MetricType { - SUM = 'Sum', - GAUGE = 'Gauge', - HISTOGRAM = 'Histogram', - SUMMARY = 'Summary', - EXPONENTIAL_HISTOGRAM = 'ExponentialHistogram', -} - -export interface MetricsListItemData { - metric_name: string; - description: string; - type: MetricType; - unit: string; - [TreemapViewType.TIMESERIES]: number; - [TreemapViewType.SAMPLES]: number; - lastReceived: string; -} - -export interface MetricsListResponse { - status: string; - data: { - metrics: MetricsListItemData[]; - total?: number; - }; -} - -export const getMetricsList = async ( - props: MetricsListPayload, - signal?: AbortSignal, - headers?: Record, -): Promise | ErrorResponse> => { - try { - const response = await axios.post('/metrics', props, { - signal, - headers, - }); - - return { - statusCode: 200, - error: null, - message: response.data.status, - payload: response.data, - params: props, - }; - } catch (error) { - return ErrorResponseHandler(error as AxiosError); - } -}; diff --git a/frontend/src/api/metricsExplorer/getMetricsListFilterKeys.ts b/frontend/src/api/metricsExplorer/getMetricsListFilterKeys.ts deleted file mode 100644 index 4fb15ab65ce..00000000000 --- a/frontend/src/api/metricsExplorer/getMetricsListFilterKeys.ts +++ /dev/null @@ -1,44 +0,0 @@ -import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; -import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; -import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; - -export interface MetricsListFilterKeysResponse { - status: string; - data: { - metricColumns: string[]; - attributeKeys: BaseAutocompleteData[]; - }; -} - -export interface GetMetricsListFilterKeysParams { - searchText: string; - limit?: number; -} - -export const getMetricsListFilterKeys = async ( - params: GetMetricsListFilterKeysParams, - signal?: AbortSignal, - headers?: Record, -): Promise | ErrorResponse> => { - try { - const response = await axios.get('/metrics/filters/keys', { - params: { - searchText: params.searchText, - limit: params.limit, - }, - signal, - headers, - }); - - return { - statusCode: 200, - error: null, - message: response.data.status, - payload: response.data, - }; - } catch (error) { - return ErrorResponseHandler(error as AxiosError); - } -}; diff --git a/frontend/src/api/metricsExplorer/getMetricsListFilterValues.ts b/frontend/src/api/metricsExplorer/getMetricsListFilterValues.ts deleted file mode 100644 index 6b4cd6d3176..00000000000 --- a/frontend/src/api/metricsExplorer/getMetricsListFilterValues.ts +++ /dev/null @@ -1,43 +0,0 @@ -import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; -import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; - -export interface MetricsListFilterValuesPayload { - filterAttributeKeyDataType: string; - filterKey: string; - searchText: string; - limit: number; -} - -export interface MetricsListFilterValuesResponse { - status: string; - data: { - filterValues: string[]; - }; -} - -export const getMetricsListFilterValues = async ( - props: MetricsListFilterValuesPayload, - signal?: AbortSignal, - headers?: Record, -): Promise< - SuccessResponse | ErrorResponse -> => { - try { - const response = await axios.post('/metrics/filters/values', props, { - signal, - headers, - }); - - return { - statusCode: 200, - error: null, - message: response.data.status, - payload: response.data, - params: props, - }; - } catch (error) { - return ErrorResponseHandler(error as AxiosError); - } -}; diff --git a/frontend/src/api/metricsExplorer/getRelatedMetrics.ts b/frontend/src/api/metricsExplorer/getRelatedMetrics.ts deleted file mode 100644 index 2f4aa5dfa6a..00000000000 --- a/frontend/src/api/metricsExplorer/getRelatedMetrics.ts +++ /dev/null @@ -1,60 +0,0 @@ -import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; -import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; -import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData'; - -export interface RelatedMetricsPayload { - start: number; - end: number; - currentMetricName: string; -} - -export interface RelatedMetricDashboard { - dashboard_name: string; - dashboard_id: string; - widget_id: string; - widget_name: string; -} - -export interface RelatedMetricAlert { - alert_name: string; - alert_id: string; -} - -export interface RelatedMetric { - name: string; - query: IBuilderQuery; - dashboards: RelatedMetricDashboard[]; - alerts: RelatedMetricAlert[]; -} - -export interface RelatedMetricsResponse { - status: 'success'; - data: { - related_metrics: RelatedMetric[]; - }; -} - -export const getRelatedMetrics = async ( - props: RelatedMetricsPayload, - signal?: AbortSignal, - headers?: Record, -): Promise | ErrorResponse> => { - try { - const response = await axios.post('/metrics/related', props, { - signal, - headers, - }); - - return { - statusCode: 200, - error: null, - message: response.data.status, - payload: response.data, - params: props, - }; - } catch (error) { - return ErrorResponseHandler(error as AxiosError); - } -}; diff --git a/frontend/src/api/v1/download/downloadExportData.ts b/frontend/src/api/v1/download/downloadExportData.ts index 30bc7b25dd7..73004b5a0fd 100644 --- a/frontend/src/api/v1/download/downloadExportData.ts +++ b/frontend/src/api/v1/download/downloadExportData.ts @@ -8,42 +8,32 @@ export const downloadExportData = async ( props: ExportRawDataProps, ): Promise => { try { - const queryParams = new URLSearchParams(); - - queryParams.append('start', String(props.start)); - queryParams.append('end', String(props.end)); - queryParams.append('filter', props.filter); - props.columns.forEach((col) => { - queryParams.append('columns', col); - }); - queryParams.append('order_by', props.orderBy); - queryParams.append('limit', String(props.limit)); - queryParams.append('format', props.format); - - const response = await axios.get(`export_raw_data?${queryParams}`, { - responseType: 'blob', // Important: tell axios to handle response as blob - decompress: true, // Enable automatic decompression - headers: { - Accept: 'application/octet-stream', // Tell server we expect binary data + const response = await axios.post( + `export_raw_data?format=${encodeURIComponent(props.format)}`, + props.body, + { + responseType: 'blob', + decompress: true, + headers: { + Accept: 'application/octet-stream', + 'Content-Type': 'application/json', + }, + timeout: 0, }, - timeout: 0, - }); + ); - // Only proceed if the response status is 200 if (response.status !== 200) { throw new Error( `Failed to download data: server returned status ${response.status}`, ); } - // Create blob URL from response data + const blob = new Blob([response.data], { type: 'application/octet-stream' }); const url = window.URL.createObjectURL(blob); - // Create and configure download link const link = document.createElement('a'); link.href = url; - // Get filename from Content-Disposition header or generate timestamped default const filename = response.headers['content-disposition'] ?.split('filename=')[1] @@ -51,7 +41,6 @@ export const downloadExportData = async ( link.setAttribute('download', filename); - // Trigger download document.body.appendChild(link); link.click(); link.remove(); diff --git a/frontend/src/api/v1/pats/create.ts b/frontend/src/api/v1/pats/create.ts deleted file mode 100644 index c487a91bb34..00000000000 --- a/frontend/src/api/v1/pats/create.ts +++ /dev/null @@ -1,28 +0,0 @@ -import axios from 'api'; -import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; -import { AxiosError } from 'axios'; -import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; -import { - APIKeyProps, - CreateAPIKeyProps, - CreatePayloadProps, -} from 'types/api/pat/types'; - -const create = async ( - props: CreateAPIKeyProps, -): Promise> => { - try { - const response = await axios.post('/pats', { - ...props, - }); - - return { - httpStatusCode: response.status, - data: response.data.data, - }; - } catch (error) { - ErrorResponseHandlerV2(error as AxiosError); - } -}; - -export default create; diff --git a/frontend/src/api/v1/pats/delete.ts b/frontend/src/api/v1/pats/delete.ts deleted file mode 100644 index 716bdffc196..00000000000 --- a/frontend/src/api/v1/pats/delete.ts +++ /dev/null @@ -1,19 +0,0 @@ -import axios from 'api'; -import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; -import { AxiosError } from 'axios'; -import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; - -const deleteAPIKey = async (id: string): Promise> => { - try { - const response = await axios.delete(`/pats/${id}`); - - return { - httpStatusCode: response.status, - data: null, - }; - } catch (error) { - ErrorResponseHandlerV2(error as AxiosError); - } -}; - -export default deleteAPIKey; diff --git a/frontend/src/api/v1/pats/list.ts b/frontend/src/api/v1/pats/list.ts deleted file mode 100644 index bc4833af51d..00000000000 --- a/frontend/src/api/v1/pats/list.ts +++ /dev/null @@ -1,20 +0,0 @@ -import axios from 'api'; -import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; -import { AxiosError } from 'axios'; -import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; -import { AllAPIKeyProps, APIKeyProps } from 'types/api/pat/types'; - -const list = async (): Promise> => { - try { - const response = await axios.get('/pats'); - - return { - httpStatusCode: response.status, - data: response.data.data, - }; - } catch (error) { - ErrorResponseHandlerV2(error as AxiosError); - } -}; - -export default list; diff --git a/frontend/src/api/v1/pats/update.ts b/frontend/src/api/v1/pats/update.ts deleted file mode 100644 index b7c1e016c8f..00000000000 --- a/frontend/src/api/v1/pats/update.ts +++ /dev/null @@ -1,24 +0,0 @@ -import axios from 'api'; -import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; -import { AxiosError } from 'axios'; -import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; -import { UpdateAPIKeyProps } from 'types/api/pat/types'; - -const updateAPIKey = async ( - props: UpdateAPIKeyProps, -): Promise> => { - try { - const response = await axios.put(`/pats/${props.id}`, { - ...props.data, - }); - - return { - httpStatusCode: response.status, - data: null, - }; - } catch (error) { - ErrorResponseHandlerV2(error as AxiosError); - } -}; - -export default updateAPIKey; diff --git a/frontend/src/components/AnnouncementBanner/AnnouncementBanner.styles.scss b/frontend/src/components/AnnouncementBanner/AnnouncementBanner.styles.scss deleted file mode 100644 index a3f95c22345..00000000000 --- a/frontend/src/components/AnnouncementBanner/AnnouncementBanner.styles.scss +++ /dev/null @@ -1,97 +0,0 @@ -.announcement-banner { - display: flex; - align-items: center; - justify-content: space-between; - gap: var(--spacing-4); - padding: var(--padding-2) var(--padding-4); - height: 40px; - font-family: var(--font-sans), sans-serif; - font-size: var(--label-base-500-font-size); - line-height: var(--label-base-500-line-height); - font-weight: var(--label-base-500-font-weight); - letter-spacing: -0.065px; - - &--warning { - background-color: var(--callout-warning-background); - color: var(--callout-warning-description); - .announcement-banner__action, - .announcement-banner__dismiss { - background: var(--callout-warning-border); - } - } - - &--info { - background-color: var(--callout-primary-background); - color: var(--callout-primary-description); - .announcement-banner__action, - .announcement-banner__dismiss { - background: var(--callout-primary-border); - } - } - - &--error { - background-color: var(--callout-error-background); - color: var(--callout-error-description); - .announcement-banner__action, - .announcement-banner__dismiss { - background: var(--callout-error-border); - } - } - - &--success { - background-color: var(--callout-success-background); - color: var(--callout-success-description); - .announcement-banner__action, - .announcement-banner__dismiss { - background: var(--callout-success-border); - } - } - - &__body { - display: flex; - align-items: center; - gap: var(--spacing-4); - flex: 1; - min-width: 0; - } - - &__icon { - display: flex; - align-items: center; - flex-shrink: 0; - } - - &__message { - flex: 1; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - line-height: var(--line-height-normal); - - strong { - font-weight: var(--font-weight-semibold); - } - } - - &__action { - height: 24px; - font-size: var(--label-small-500-font-size); - color: currentColor; - - &:hover { - opacity: 0.8; - } - } - - &__dismiss { - width: 24px; - height: 24px; - padding: 0; - color: currentColor; - - &:hover { - opacity: 0.8; - } - } -} diff --git a/frontend/src/components/AnnouncementBanner/AnnouncementBanner.test.tsx b/frontend/src/components/AnnouncementBanner/AnnouncementBanner.test.tsx deleted file mode 100644 index f960a30cdbd..00000000000 --- a/frontend/src/components/AnnouncementBanner/AnnouncementBanner.test.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { render, screen, userEvent } from 'tests/test-utils'; - -import { - AnnouncementBanner, - AnnouncementBannerProps, - PersistedAnnouncementBanner, -} from './index'; - -const STORAGE_KEY = 'test-banner-dismissed'; - -function renderBanner(props: Partial = {}): void { - render(); -} - -afterEach(() => { - localStorage.removeItem(STORAGE_KEY); -}); - -describe('AnnouncementBanner', () => { - it('renders message and default warning variant', () => { - renderBanner({ message: Heads up }); - - const alert = screen.getByRole('alert'); - expect(alert).toHaveClass('announcement-banner--warning'); - expect(alert).toHaveTextContent('Heads up'); - }); - - it.each(['warning', 'info', 'success', 'error'] as const)( - 'renders %s variant correctly', - (type) => { - renderBanner({ type, message: 'Test message' }); - const alert = screen.getByRole('alert'); - expect(alert).toHaveClass(`announcement-banner--${type}`); - }, - ); - - it('calls action onClick when action button is clicked', async () => { - const onClick = jest.fn() as jest.MockedFunction<() => void>; - renderBanner({ action: { label: 'Go to Settings', onClick } }); - - const user = userEvent.setup({ pointerEventsCheck: 0 }); - await user.click(screen.getByRole('button', { name: /go to settings/i })); - - expect(onClick).toHaveBeenCalledTimes(1); - }); - - it('hides dismiss button when onClose is not provided and hides icon when icon is null', () => { - renderBanner({ onClose: undefined, icon: null }); - - expect( - screen.queryByRole('button', { name: /dismiss/i }), - ).not.toBeInTheDocument(); - expect( - screen.queryByRole('alert')?.querySelector('.announcement-banner__icon'), - ).not.toBeInTheDocument(); - }); -}); - -describe('PersistedAnnouncementBanner', () => { - it('dismisses on click, calls onDismiss, and persists to localStorage', async () => { - const onDismiss = jest.fn() as jest.MockedFunction<() => void>; - render( - , - ); - - const user = userEvent.setup({ pointerEventsCheck: 0 }); - await user.click(screen.getByRole('button', { name: /dismiss/i })); - - expect(screen.queryByRole('alert')).not.toBeInTheDocument(); - expect(onDismiss).toHaveBeenCalledTimes(1); - expect(localStorage.getItem(STORAGE_KEY)).toBe('true'); - }); - - it('does not render when storageKey is already set in localStorage', () => { - localStorage.setItem(STORAGE_KEY, 'true'); - render( - , - ); - - expect(screen.queryByRole('alert')).not.toBeInTheDocument(); - }); -}); diff --git a/frontend/src/components/AnnouncementBanner/AnnouncementBanner.tsx b/frontend/src/components/AnnouncementBanner/AnnouncementBanner.tsx deleted file mode 100644 index 50471161895..00000000000 --- a/frontend/src/components/AnnouncementBanner/AnnouncementBanner.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { ReactNode } from 'react'; -import { Button } from '@signozhq/button'; -import { - CircleAlert, - CircleCheckBig, - Info, - TriangleAlert, - X, -} from '@signozhq/icons'; -import cx from 'classnames'; - -import './AnnouncementBanner.styles.scss'; - -export type AnnouncementBannerType = 'warning' | 'info' | 'error' | 'success'; - -export interface AnnouncementBannerAction { - label: string; - onClick: () => void; -} - -export interface AnnouncementBannerProps { - message: ReactNode; - type?: AnnouncementBannerType; - icon?: ReactNode | null; - action?: AnnouncementBannerAction; - onClose?: () => void; - className?: string; -} - -const DEFAULT_ICONS: Record = { - warning: , - info: , - error: , - success: , -}; - -export default function AnnouncementBanner({ - message, - type = 'warning', - icon, - action, - onClose, - className, -}: AnnouncementBannerProps): JSX.Element { - const resolvedIcon = icon === null ? null : icon ?? DEFAULT_ICONS[type]; - - return ( -
-
- {resolvedIcon && ( - {resolvedIcon} - )} - {message} - {action && ( - - )} -
- - {onClose && ( - - )} -
- ); -} diff --git a/frontend/src/components/AnnouncementBanner/PersistedAnnouncementBanner.tsx b/frontend/src/components/AnnouncementBanner/PersistedAnnouncementBanner.tsx deleted file mode 100644 index d9302b0e9a8..00000000000 --- a/frontend/src/components/AnnouncementBanner/PersistedAnnouncementBanner.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { useState } from 'react'; - -import AnnouncementBanner, { - AnnouncementBannerProps, -} from './AnnouncementBanner'; - -interface PersistedAnnouncementBannerProps extends AnnouncementBannerProps { - storageKey: string; - onDismiss?: () => void; -} - -function isDismissed(storageKey: string): boolean { - return localStorage.getItem(storageKey) === 'true'; -} - -export default function PersistedAnnouncementBanner({ - storageKey, - onDismiss, - ...props -}: PersistedAnnouncementBannerProps): JSX.Element | null { - const [visible, setVisible] = useState(() => !isDismissed(storageKey)); - - if (!visible) { - return null; - } - - const handleClose = (): void => { - localStorage.setItem(storageKey, 'true'); - setVisible(false); - onDismiss?.(); - }; - - return ; -} diff --git a/frontend/src/components/AnnouncementBanner/index.ts b/frontend/src/components/AnnouncementBanner/index.ts deleted file mode 100644 index 94e19891882..00000000000 --- a/frontend/src/components/AnnouncementBanner/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import AnnouncementBanner from './AnnouncementBanner'; -import PersistedAnnouncementBanner from './PersistedAnnouncementBanner'; - -export type { - AnnouncementBannerAction, - AnnouncementBannerProps, - AnnouncementBannerType, -} from './AnnouncementBanner'; - -export { AnnouncementBanner, PersistedAnnouncementBanner }; - -export default AnnouncementBanner; diff --git a/frontend/src/components/CeleryTask/useNavigateToExplorer.ts b/frontend/src/components/CeleryTask/useNavigateToExplorer.ts index 43cee03ef3d..84da964b539 100644 --- a/frontend/src/components/CeleryTask/useNavigateToExplorer.ts +++ b/frontend/src/components/CeleryTask/useNavigateToExplorer.ts @@ -7,7 +7,7 @@ import ROUTES from 'constants/routes'; import useUpdatedQuery from 'container/GridCardLayout/useResolveQuery'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useNotifications } from 'hooks/useNotifications'; -import { useDashboard } from 'providers/Dashboard/Dashboard'; +import { useDashboardStore } from 'providers/Dashboard/store/useDashboardStore'; import { AppState } from 'store/reducers'; import { Query, TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; import { DataSource, MetricAggregateOperator } from 'types/common/queryBuilder'; @@ -79,7 +79,7 @@ export function useNavigateToExplorer(): ( ); const { getUpdatedQuery } = useUpdatedQuery(); - const { selectedDashboard } = useDashboard(); + const { selectedDashboard } = useDashboardStore(); const { notifications } = useNotifications(); return useCallback( diff --git a/frontend/src/components/CreateServiceAccountModal/CreateServiceAccountModal.tsx b/frontend/src/components/CreateServiceAccountModal/CreateServiceAccountModal.tsx index 1b8c6c78c46..617328d4e39 100644 --- a/frontend/src/components/CreateServiceAccountModal/CreateServiceAccountModal.tsx +++ b/frontend/src/components/CreateServiceAccountModal/CreateServiceAccountModal.tsx @@ -12,17 +12,13 @@ import { } from 'api/generated/services/serviceaccount'; import type { RenderErrorResponseDTO } from 'api/generated/services/sigNoz.schemas'; import { AxiosError } from 'axios'; -import RolesSelect, { useRoles } from 'components/RolesSelect'; import { SA_QUERY_PARAMS } from 'container/ServiceAccountsSettings/constants'; import { parseAsBoolean, useQueryState } from 'nuqs'; -import { EMAIL_REGEX } from 'utils/app'; import './CreateServiceAccountModal.styles.scss'; interface FormValues { name: string; - email: string; - roles: string[]; } function CreateServiceAccountModal(): JSX.Element { @@ -41,8 +37,6 @@ function CreateServiceAccountModal(): JSX.Element { mode: 'onChange', defaultValues: { name: '', - email: '', - roles: [], }, }); @@ -70,13 +64,6 @@ function CreateServiceAccountModal(): JSX.Element { }, }, }); - const { - roles, - isLoading: rolesLoading, - isError: rolesError, - error: rolesErrorObj, - refetch: refetchRoles, - } = useRoles(); function handleClose(): void { reset(); @@ -87,8 +74,6 @@ function CreateServiceAccountModal(): JSX.Element { createServiceAccount({ data: { name: values.name.trim(), - email: values.email.trim(), - roles: values.roles, }, }); } @@ -134,68 +119,6 @@ function CreateServiceAccountModal(): JSX.Element {

{errors.name.message}

)} - -
- - ( - - )} - /> - {errors.email && ( -

{errors.email.message}

- )} -
-

- Used only for notifications about this service account. It is not used for - authentication. -

- -
- - - value.length > 0 || 'At least one role is required', - }} - render={({ field }): JSX.Element => ( - - )} - /> - {errors.roles && ( -

{errors.roles.message}

- )} -
diff --git a/frontend/src/components/CreateServiceAccountModal/__tests__/CreateServiceAccountModal.test.tsx b/frontend/src/components/CreateServiceAccountModal/__tests__/CreateServiceAccountModal.test.tsx index 8dea4e2d3cf..a9d0ab94406 100644 --- a/frontend/src/components/CreateServiceAccountModal/__tests__/CreateServiceAccountModal.test.tsx +++ b/frontend/src/components/CreateServiceAccountModal/__tests__/CreateServiceAccountModal.test.tsx @@ -1,5 +1,4 @@ import { toast } from '@signozhq/sonner'; -import { listRolesSuccessResponse } from 'mocks-server/__mockdata__/roles'; import { rest, server } from 'mocks-server/server'; import { NuqsTestingAdapter } from 'nuqs/adapters/testing'; import { render, screen, userEvent, waitFor } from 'tests/test-utils'; @@ -12,7 +11,6 @@ jest.mock('@signozhq/sonner', () => ({ const mockToast = jest.mocked(toast); -const ROLES_ENDPOINT = '*/api/v1/roles'; const SERVICE_ACCOUNTS_ENDPOINT = '*/api/v1/service_accounts'; function renderModal(): ReturnType { @@ -27,9 +25,6 @@ describe('CreateServiceAccountModal', () => { beforeEach(() => { jest.clearAllMocks(); server.use( - rest.get(ROLES_ENDPOINT, (_, res, ctx) => - res(ctx.status(200), ctx.json(listRolesSuccessResponse)), - ), rest.post(SERVICE_ACCOUNTS_ENDPOINT, (_, res, ctx) => res(ctx.status(201), ctx.json({ status: 'success', data: {} })), ), @@ -48,38 +43,11 @@ describe('CreateServiceAccountModal', () => { ).toBeDisabled(); }); - it('submit button remains disabled when email is invalid', async () => { - const user = userEvent.setup({ pointerEventsCheck: 0 }); - renderModal(); - - await user.type(screen.getByPlaceholderText('Enter a name'), 'My Bot'); - await user.type( - screen.getByPlaceholderText('email@example.com'), - 'not-an-email', - ); - - await user.click(screen.getByText('Select roles')); - await user.click(await screen.findByTitle('signoz-admin')); - - await waitFor(() => - expect( - screen.getByRole('button', { name: /Create Service Account/i }), - ).toBeDisabled(), - ); - }); - it('successful submit shows toast.success and closes modal', async () => { const user = userEvent.setup({ pointerEventsCheck: 0 }); renderModal(); await user.type(screen.getByPlaceholderText('Enter a name'), 'Deploy Bot'); - await user.type( - screen.getByPlaceholderText('email@example.com'), - 'deploy@acme.io', - ); - - await user.click(screen.getByText('Select roles')); - await user.click(await screen.findByTitle('signoz-admin')); const submitBtn = screen.getByRole('button', { name: /Create Service Account/i, @@ -116,13 +84,6 @@ describe('CreateServiceAccountModal', () => { renderModal(); await user.type(screen.getByPlaceholderText('Enter a name'), 'Dupe Bot'); - await user.type( - screen.getByPlaceholderText('email@example.com'), - 'dupe@acme.io', - ); - - await user.click(screen.getByText('Select roles')); - await user.click(await screen.findByTitle('signoz-admin')); const submitBtn = screen.getByRole('button', { name: /Create Service Account/i, @@ -164,16 +125,4 @@ describe('CreateServiceAccountModal', () => { await screen.findByText('Name is required'); }); - - it('shows "Please enter a valid email address" for a malformed email', async () => { - const user = userEvent.setup({ pointerEventsCheck: 0 }); - renderModal(); - - await user.type( - screen.getByPlaceholderText('email@example.com'), - 'not-an-email', - ); - - await screen.findByText('Please enter a valid email address'); - }); }); diff --git a/frontend/src/components/LogsDownloadOptionsMenu/LogsDownloadOptionsMenu.styles.scss b/frontend/src/components/DownloadOptionsMenu/DownloadOptionsMenu.styles.scss similarity index 71% rename from frontend/src/components/LogsDownloadOptionsMenu/LogsDownloadOptionsMenu.styles.scss rename to frontend/src/components/DownloadOptionsMenu/DownloadOptionsMenu.styles.scss index e9f2d92df37..ca9f81e5c33 100644 --- a/frontend/src/components/LogsDownloadOptionsMenu/LogsDownloadOptionsMenu.styles.scss +++ b/frontend/src/components/DownloadOptionsMenu/DownloadOptionsMenu.styles.scss @@ -1,11 +1,11 @@ -.logs-download-popover { +.download-popover { .ant-popover-inner { border-radius: 4px; - border: 1px solid var(--bg-slate-400); + border: 1px solid var(--l3-border); background: linear-gradient( 139deg, - var(--bg-ink-400) 0%, - var(--bg-ink-500) 98.68% + var(--l2-background) 0%, + var(--l3-background) 98.68% ); box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2); backdrop-filter: blur(20px); @@ -19,7 +19,7 @@ .title { display: flex; - color: var(--bg-slate-50); + color: var(--l3-foreground); font-family: Inter; font-size: 11px; font-style: normal; @@ -38,7 +38,7 @@ flex-direction: column; :global(.ant-radio-wrapper) { - color: var(--bg-vanilla-400); + color: var(--foreground); font-family: Inter; font-size: 13px; } @@ -46,7 +46,7 @@ .horizontal-line { height: 1px; - background: var(--bg-slate-400); + background: var(--l3-border); } .export-button { @@ -59,27 +59,27 @@ } .lightMode { - .logs-download-popover { + .download-popover { .ant-popover-inner { - border: 1px solid var(--bg-vanilla-300); + border: 1px solid var(--l2-border); background: linear-gradient( 139deg, - var(--bg-vanilla-100) 0%, - var(--bg-vanilla-300) 98.68% + var(--background) 0%, + var(--l1-background) 98.68% ); box-shadow: 4px 10px 16px 2px rgba(255, 255, 255, 0.2); } .export-options-container { .title { - color: var(--bg-ink-200); + color: var(--l2-foreground); } :global(.ant-radio-wrapper) { - color: var(--bg-ink-400); + color: var(--foreground); } .horizontal-line { - background: var(--bg-vanilla-300); + background: var(--l2-border); } } } diff --git a/frontend/src/components/DownloadOptionsMenu/DownloadOptionsMenu.test.tsx b/frontend/src/components/DownloadOptionsMenu/DownloadOptionsMenu.test.tsx new file mode 100644 index 00000000000..fa5b2023623 --- /dev/null +++ b/frontend/src/components/DownloadOptionsMenu/DownloadOptionsMenu.test.tsx @@ -0,0 +1,357 @@ +// eslint-disable-next-line no-restricted-imports +import { Provider } from 'react-redux'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { message } from 'antd'; +import configureStore from 'redux-mock-store'; +import store from 'store'; +import { Query } from 'types/api/queryBuilder/queryBuilderData'; +import { EQueryType } from 'types/common/dashboard'; +import { DataSource, StringOperators } from 'types/common/queryBuilder'; + +import '@testing-library/jest-dom'; + +import { DownloadFormats, DownloadRowCounts } from './constants'; +import DownloadOptionsMenu from './DownloadOptionsMenu'; + +const mockDownloadExportData = jest.fn().mockResolvedValue(undefined); +jest.mock('api/v1/download/downloadExportData', () => ({ + downloadExportData: (...args: any[]): any => mockDownloadExportData(...args), + default: (...args: any[]): any => mockDownloadExportData(...args), +})); + +jest.mock('antd', () => { + const actual = jest.requireActual('antd'); + return { + ...actual, + message: { + success: jest.fn(), + error: jest.fn(), + }, + }; +}); + +const mockUseQueryBuilder = jest.fn(); +jest.mock('hooks/queryBuilder/useQueryBuilder', () => ({ + useQueryBuilder: (): any => mockUseQueryBuilder(), +})); + +const mockStore = configureStore([]); +const createMockReduxStore = (): any => + mockStore({ + ...store.getState(), + }); + +const createMockStagedQuery = (dataSource: DataSource): Query => ({ + id: 'test-query-id', + queryType: EQueryType.QUERY_BUILDER, + builder: { + queryData: [ + { + queryName: 'A', + dataSource, + aggregateOperator: StringOperators.NOOP, + aggregateAttribute: { + id: '', + dataType: '' as any, + key: '', + type: '', + }, + aggregations: [{ expression: 'count()' }], + functions: [], + filter: { expression: 'status = 200' }, + filters: { items: [], op: 'AND' }, + groupBy: [], + expression: 'A', + disabled: false, + having: { expression: '' } as any, + limit: null, + stepInterval: null, + orderBy: [{ columnName: 'timestamp', order: 'desc' }], + legend: '', + selectColumns: [], + }, + ], + queryFormulas: [], + queryTraceOperator: [], + }, + promql: [], + clickhouse_sql: [], +}); + +const renderWithStore = (dataSource: DataSource): void => { + const mockReduxStore = createMockReduxStore(); + render( + + + , + ); +}; + +describe.each([ + [DataSource.LOGS, 'logs'], + [DataSource.TRACES, 'traces'], +])('DownloadOptionsMenu for %s', (dataSource, signal) => { + const testId = `periscope-btn-download-${dataSource}`; + + beforeEach(() => { + mockDownloadExportData.mockReset().mockResolvedValue(undefined); + (message.success as jest.Mock).mockReset(); + (message.error as jest.Mock).mockReset(); + mockUseQueryBuilder.mockReturnValue({ + stagedQuery: createMockStagedQuery(dataSource), + }); + }); + + it('renders download button', () => { + renderWithStore(dataSource); + const button = screen.getByTestId(testId); + expect(button).toBeInTheDocument(); + expect(button).toHaveClass('periscope-btn', 'ghost'); + }); + + it('shows popover with export options when download button is clicked', () => { + renderWithStore(dataSource); + fireEvent.click(screen.getByTestId(testId)); + + expect(screen.getByRole('dialog')).toBeInTheDocument(); + expect(screen.getByText('FORMAT')).toBeInTheDocument(); + expect(screen.getByText('Number of Rows')).toBeInTheDocument(); + + if (dataSource === DataSource.TRACES) { + expect(screen.queryByText('Columns')).not.toBeInTheDocument(); + } else { + expect(screen.getByText('Columns')).toBeInTheDocument(); + } + }); + + it('allows changing export format', () => { + renderWithStore(dataSource); + fireEvent.click(screen.getByTestId(testId)); + + const csvRadio = screen.getByRole('radio', { name: 'csv' }); + const jsonlRadio = screen.getByRole('radio', { name: 'jsonl' }); + + expect(csvRadio).toBeChecked(); + fireEvent.click(jsonlRadio); + expect(jsonlRadio).toBeChecked(); + expect(csvRadio).not.toBeChecked(); + }); + + it('allows changing row limit', () => { + renderWithStore(dataSource); + fireEvent.click(screen.getByTestId(testId)); + + const tenKRadio = screen.getByRole('radio', { name: '10k' }); + const fiftyKRadio = screen.getByRole('radio', { name: '50k' }); + + expect(tenKRadio).toBeChecked(); + fireEvent.click(fiftyKRadio); + expect(fiftyKRadio).toBeChecked(); + expect(tenKRadio).not.toBeChecked(); + }); + + it('allows changing columns scope', () => { + if (dataSource === DataSource.TRACES) { + renderWithStore(dataSource); + fireEvent.click(screen.getByTestId(testId)); + + expect(screen.queryByRole('radio', { name: 'All' })).not.toBeInTheDocument(); + expect( + screen.queryByRole('radio', { name: 'Selected' }), + ).not.toBeInTheDocument(); + return; + } + + renderWithStore(dataSource); + fireEvent.click(screen.getByTestId(testId)); + + const allColumnsRadio = screen.getByRole('radio', { name: 'All' }); + const selectedColumnsRadio = screen.getByRole('radio', { name: 'Selected' }); + + expect(allColumnsRadio).toBeChecked(); + fireEvent.click(selectedColumnsRadio); + expect(selectedColumnsRadio).toBeChecked(); + expect(allColumnsRadio).not.toBeChecked(); + }); + + it('calls downloadExportData with correct format and POST body', async () => { + renderWithStore(dataSource); + fireEvent.click(screen.getByTestId(testId)); + fireEvent.click(screen.getByText('Export')); + + await waitFor(() => { + expect(mockDownloadExportData).toHaveBeenCalledTimes(1); + const callArgs = mockDownloadExportData.mock.calls[0][0]; + expect(callArgs.format).toBe(DownloadFormats.CSV); + expect(callArgs.body).toBeDefined(); + expect(callArgs.body.requestType).toBe('raw'); + expect(callArgs.body.compositeQuery.queries).toHaveLength(1); + + const query = callArgs.body.compositeQuery.queries[0]; + expect(query.type).toBe('builder_query'); + expect(query.spec.signal).toBe(signal); + expect(query.spec.limit).toBe(DownloadRowCounts.TEN_K); + }); + }); + + it('clears groupBy and having in the export payload', async () => { + const mockQuery = createMockStagedQuery(dataSource); + mockQuery.builder.queryData[0].groupBy = [ + { key: 'service', dataType: 'string' as any, type: '' }, + ]; + mockQuery.builder.queryData[0].having = { + expression: 'count() > 10', + } as any; + + mockUseQueryBuilder.mockReturnValue({ stagedQuery: mockQuery }); + renderWithStore(dataSource); + fireEvent.click(screen.getByTestId(testId)); + fireEvent.click(screen.getByText('Export')); + + await waitFor(() => { + expect(mockDownloadExportData).toHaveBeenCalledTimes(1); + const callArgs = mockDownloadExportData.mock.calls[0][0]; + const query = callArgs.body.compositeQuery.queries[0]; + expect(query.spec.groupBy).toBeUndefined(); + expect(query.spec.having).toEqual({ expression: '' }); + }); + }); + + it('keeps selectColumns when column scope is Selected', async () => { + const mockQuery = createMockStagedQuery(dataSource); + mockQuery.builder.queryData[0].selectColumns = [ + { name: 'http.status', fieldDataType: 'int64', fieldContext: 'attribute' }, + ] as any; + + mockUseQueryBuilder.mockReturnValue({ stagedQuery: mockQuery }); + renderWithStore(dataSource); + fireEvent.click(screen.getByTestId(testId)); + + // For traces, column scope is always Selected and the radio is hidden + if (dataSource !== DataSource.TRACES) { + fireEvent.click(screen.getByRole('radio', { name: 'Selected' })); + } + + fireEvent.click(screen.getByText('Export')); + + await waitFor(() => { + expect(mockDownloadExportData).toHaveBeenCalledTimes(1); + const callArgs = mockDownloadExportData.mock.calls[0][0]; + const query = callArgs.body.compositeQuery.queries[0]; + expect(query.spec.selectFields).toEqual([ + expect.objectContaining({ + name: 'http.status', + fieldDataType: 'int64', + }), + ]); + }); + }); + + it('sends no selectFields when column scope is All', async () => { + // For traces, column scope is always Selected — this test only applies to other sources + if (dataSource === DataSource.TRACES) { + return; + } + + renderWithStore(dataSource); + fireEvent.click(screen.getByTestId(testId)); + fireEvent.click(screen.getByRole('radio', { name: 'All' })); + fireEvent.click(screen.getByText('Export')); + + await waitFor(() => { + expect(mockDownloadExportData).toHaveBeenCalledTimes(1); + const callArgs = mockDownloadExportData.mock.calls[0][0]; + const query = callArgs.body.compositeQuery.queries[0]; + expect(query.spec.selectFields).toBeUndefined(); + }); + }); + + it('handles successful export with success message', async () => { + renderWithStore(dataSource); + fireEvent.click(screen.getByTestId(testId)); + fireEvent.click(screen.getByText('Export')); + + await waitFor(() => { + expect(message.success).toHaveBeenCalledWith( + 'Export completed successfully', + ); + }); + }); + + it('handles export failure with error message', async () => { + mockDownloadExportData.mockRejectedValueOnce(new Error('Server error')); + renderWithStore(dataSource); + fireEvent.click(screen.getByTestId(testId)); + fireEvent.click(screen.getByText('Export')); + + await waitFor(() => { + expect(message.error).toHaveBeenCalledWith( + `Failed to export ${dataSource}. Please try again.`, + ); + }); + }); + + it('handles UI state correctly during export process', async () => { + let resolveDownload: () => void; + mockDownloadExportData.mockImplementationOnce( + () => + new Promise((resolve) => { + resolveDownload = resolve; + }), + ); + renderWithStore(dataSource); + + fireEvent.click(screen.getByTestId(testId)); + expect(screen.getByRole('dialog')).toBeInTheDocument(); + + fireEvent.click(screen.getByText('Export')); + + expect(screen.getByTestId(testId)).toBeDisabled(); + expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); + + resolveDownload!(); + + await waitFor(() => { + expect(screen.getByTestId(testId)).not.toBeDisabled(); + }); + }); +}); + +describe('DownloadOptionsMenu for traces with queryTraceOperator', () => { + const dataSource = DataSource.TRACES; + const testId = `periscope-btn-download-${dataSource}`; + + beforeEach(() => { + mockDownloadExportData.mockReset().mockResolvedValue(undefined); + (message.success as jest.Mock).mockReset(); + }); + + it('applies limit and clears groupBy on queryTraceOperator entries', async () => { + const query = createMockStagedQuery(dataSource); + query.builder.queryTraceOperator = [ + { + ...query.builder.queryData[0], + queryName: 'TraceOp1', + expression: 'TraceOp1', + groupBy: [{ key: 'service', dataType: 'string' as any, type: '' }], + }, + ]; + + mockUseQueryBuilder.mockReturnValue({ stagedQuery: query }); + renderWithStore(dataSource); + fireEvent.click(screen.getByTestId(testId)); + fireEvent.click(screen.getByRole('radio', { name: '50k' })); + fireEvent.click(screen.getByText('Export')); + + await waitFor(() => { + expect(mockDownloadExportData).toHaveBeenCalledTimes(1); + const callArgs = mockDownloadExportData.mock.calls[0][0]; + const queries = callArgs.body.compositeQuery.queries; + const traceOpQuery = queries.find((q: any) => q.spec.name === 'TraceOp1'); + if (traceOpQuery) { + expect(traceOpQuery.spec.limit).toBe(DownloadRowCounts.FIFTY_K); + expect(traceOpQuery.spec.groupBy).toBeUndefined(); + } + }); + }); +}); diff --git a/frontend/src/components/DownloadOptionsMenu/DownloadOptionsMenu.tsx b/frontend/src/components/DownloadOptionsMenu/DownloadOptionsMenu.tsx new file mode 100644 index 00000000000..04664b15f07 --- /dev/null +++ b/frontend/src/components/DownloadOptionsMenu/DownloadOptionsMenu.tsx @@ -0,0 +1,153 @@ +import { useCallback, useMemo, useState } from 'react'; +import { Button, Popover, Radio, Tooltip, Typography } from 'antd'; +import { TelemetryFieldKey } from 'api/v5/v5'; +import { useExportRawData } from 'hooks/useDownloadOptionsMenu/useDownloadOptionsMenu'; +import { Download, DownloadIcon, Loader2 } from 'lucide-react'; +import { DataSource } from 'types/common/queryBuilder'; + +import { + DownloadColumnsScopes, + DownloadFormats, + DownloadRowCounts, +} from './constants'; + +import './DownloadOptionsMenu.styles.scss'; + +interface DownloadOptionsMenuProps { + dataSource: DataSource; + selectedColumns?: TelemetryFieldKey[]; +} + +export default function DownloadOptionsMenu({ + dataSource, + selectedColumns, +}: DownloadOptionsMenuProps): JSX.Element { + const [exportFormat, setExportFormat] = useState(DownloadFormats.CSV); + const [rowLimit, setRowLimit] = useState(DownloadRowCounts.TEN_K); + const [columnsScope, setColumnsScope] = useState( + DownloadColumnsScopes.ALL, + ); + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + const { isDownloading, handleExportRawData } = useExportRawData({ + dataSource, + }); + + const handleExport = useCallback(async (): Promise => { + setIsPopoverOpen(false); + await handleExportRawData({ + format: exportFormat, + rowLimit, + clearSelectColumns: + dataSource !== DataSource.TRACES && + columnsScope === DownloadColumnsScopes.ALL, + selectedColumns, + }); + }, [ + exportFormat, + rowLimit, + columnsScope, + selectedColumns, + handleExportRawData, + dataSource, + ]); + + const popoverContent = useMemo( + () => ( +
+
+ FORMAT + setExportFormat(e.target.value)} + > + csv + jsonl + +
+ +
+ +
+ Number of Rows + setRowLimit(e.target.value)} + > + 10k + 30k + 50k + +
+ + {dataSource !== DataSource.TRACES && ( + <> +
+ +
+ Columns + setColumnsScope(e.target.value)} + > + All + Selected + +
+ + )} + + +
+ ), + [ + exportFormat, + rowLimit, + columnsScope, + isDownloading, + handleExport, + dataSource, + ], + ); + + return ( + + + -
- ), - [exportFormat, rowLimit, columnsScope, isDownloading, handleExportRawData], - ); - - return ( - - - ); } -export default DisableAccountModal; +export default DeleteAccountModal; diff --git a/frontend/src/components/ServiceAccountDrawer/EditKeyModal/EditKeyForm.tsx b/frontend/src/components/ServiceAccountDrawer/EditKeyModal/EditKeyForm.tsx index a05277b8de9..55510a36a13 100644 --- a/frontend/src/components/ServiceAccountDrawer/EditKeyModal/EditKeyForm.tsx +++ b/frontend/src/components/ServiceAccountDrawer/EditKeyModal/EditKeyForm.tsx @@ -6,7 +6,7 @@ import { LockKeyhole, Trash2, X } from '@signozhq/icons'; import { Input } from '@signozhq/input'; import { ToggleGroup, ToggleGroupItem } from '@signozhq/toggle-group'; import { DatePicker } from 'antd'; -import type { ServiceaccounttypesFactorAPIKeyDTO } from 'api/generated/services/sigNoz.schemas'; +import type { ServiceaccounttypesGettableFactorAPIKeyDTO } from 'api/generated/services/sigNoz.schemas'; import { popupContainer } from 'utils/selectPopupContainer'; import { disabledDate, formatLastObservedAt } from '../utils'; @@ -17,7 +17,7 @@ export interface EditKeyFormProps { register: UseFormRegister; control: Control; expiryMode: ExpiryMode; - keyItem: ServiceaccounttypesFactorAPIKeyDTO | null; + keyItem: ServiceaccounttypesGettableFactorAPIKeyDTO | null; isSaving: boolean; isDirty: boolean; onSubmit: () => void; diff --git a/frontend/src/components/ServiceAccountDrawer/EditKeyModal/index.tsx b/frontend/src/components/ServiceAccountDrawer/EditKeyModal/index.tsx index e03c6fb4219..c2d0c1b2eb0 100644 --- a/frontend/src/components/ServiceAccountDrawer/EditKeyModal/index.tsx +++ b/frontend/src/components/ServiceAccountDrawer/EditKeyModal/index.tsx @@ -11,7 +11,7 @@ import { } from 'api/generated/services/serviceaccount'; import type { RenderErrorResponseDTO, - ServiceaccounttypesFactorAPIKeyDTO, + ServiceaccounttypesGettableFactorAPIKeyDTO, } from 'api/generated/services/sigNoz.schemas'; import { AxiosError } from 'axios'; import { SA_QUERY_PARAMS } from 'container/ServiceAccountsSettings/constants'; @@ -27,7 +27,7 @@ import { DEFAULT_FORM_VALUES, ExpiryMode } from './types'; import './EditKeyModal.styles.scss'; export interface EditKeyModalProps { - keyItem: ServiceaccounttypesFactorAPIKeyDTO | null; + keyItem: ServiceaccounttypesGettableFactorAPIKeyDTO | null; } function EditKeyModal({ keyItem }: EditKeyModalProps): JSX.Element { diff --git a/frontend/src/components/ServiceAccountDrawer/KeysTab.tsx b/frontend/src/components/ServiceAccountDrawer/KeysTab.tsx index f33966f72b6..9a4a5ad6090 100644 --- a/frontend/src/components/ServiceAccountDrawer/KeysTab.tsx +++ b/frontend/src/components/ServiceAccountDrawer/KeysTab.tsx @@ -3,7 +3,7 @@ import { Button } from '@signozhq/button'; import { KeyRound, X } from '@signozhq/icons'; import { Skeleton, Table, Tooltip } from 'antd'; import type { ColumnsType } from 'antd/es/table/interface'; -import type { ServiceaccounttypesFactorAPIKeyDTO } from 'api/generated/services/sigNoz.schemas'; +import type { ServiceaccounttypesGettableFactorAPIKeyDTO } from 'api/generated/services/sigNoz.schemas'; import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats'; import dayjs from 'dayjs'; import { parseAsBoolean, parseAsString, useQueryState } from 'nuqs'; @@ -14,7 +14,7 @@ import RevokeKeyModal from './RevokeKeyModal'; import { formatLastObservedAt } from './utils'; interface KeysTabProps { - keys: ServiceaccounttypesFactorAPIKeyDTO[]; + keys: ServiceaccounttypesGettableFactorAPIKeyDTO[]; isLoading: boolean; isDisabled?: boolean; currentPage: number; @@ -44,7 +44,7 @@ function buildColumns({ isDisabled, onRevokeClick, handleformatLastObservedAt, -}: BuildColumnsParams): ColumnsType { +}: BuildColumnsParams): ColumnsType { return [ { title: 'Name', @@ -165,7 +165,17 @@ function KeysTab({ return (
-

No keys. Start by creating one.

+

+ No keys. Start by creating one.{' '} + + Learn more + +

+ + {saveErrors.length > 0 && ( +
+ {saveErrors.map(({ context, apiError, onRetry }) => ( + + ))} +
+ )} ); } diff --git a/frontend/src/components/ServiceAccountDrawer/RevokeKeyModal.tsx b/frontend/src/components/ServiceAccountDrawer/RevokeKeyModal.tsx index 40ad15bacab..690a020d6e6 100644 --- a/frontend/src/components/ServiceAccountDrawer/RevokeKeyModal.tsx +++ b/frontend/src/components/ServiceAccountDrawer/RevokeKeyModal.tsx @@ -11,7 +11,7 @@ import { } from 'api/generated/services/serviceaccount'; import type { RenderErrorResponseDTO, - ServiceaccounttypesFactorAPIKeyDTO, + ServiceaccounttypesGettableFactorAPIKeyDTO, } from 'api/generated/services/sigNoz.schemas'; import { AxiosError } from 'axios'; import { SA_QUERY_PARAMS } from 'container/ServiceAccountsSettings/constants'; @@ -64,9 +64,9 @@ function RevokeKeyModal(): JSX.Element { const open = !!revokeKeyId && !!accountId; const cachedKeys = accountId - ? queryClient.getQueryData<{ data: ServiceaccounttypesFactorAPIKeyDTO[] }>( - getListServiceAccountKeysQueryKey({ id: accountId }), - ) + ? queryClient.getQueryData<{ + data: ServiceaccounttypesGettableFactorAPIKeyDTO[]; + }>(getListServiceAccountKeysQueryKey({ id: accountId })) : null; const keyName = cachedKeys?.data?.find((k) => k.id === revokeKeyId)?.name; diff --git a/frontend/src/components/ServiceAccountDrawer/SaveErrorItem.tsx b/frontend/src/components/ServiceAccountDrawer/SaveErrorItem.tsx new file mode 100644 index 00000000000..bb7eb191753 --- /dev/null +++ b/frontend/src/components/ServiceAccountDrawer/SaveErrorItem.tsx @@ -0,0 +1,74 @@ +import { useState } from 'react'; +import { Button } from '@signozhq/button'; +import { Color } from '@signozhq/design-tokens'; +import { ChevronDown, ChevronUp, CircleAlert, RotateCw } from '@signozhq/icons'; +import ErrorContent from 'components/ErrorModal/components/ErrorContent'; +import APIError from 'types/api/error'; + +interface SaveErrorItemProps { + context: string; + apiError: APIError; + onRetry?: () => void | Promise; +} + +function SaveErrorItem({ + context, + apiError, + onRetry, +}: SaveErrorItemProps): JSX.Element { + const [expanded, setExpanded] = useState(false); + const [isRetrying, setIsRetrying] = useState(false); + + const ChevronIcon = expanded ? ChevronUp : ChevronDown; + + return ( +
+
{ + if (!isRetrying) { + setExpanded((prev) => !prev); + } + }} + > + + + {isRetrying ? 'Retrying...' : `${context}: ${apiError.getErrorMessage()}`} + + {onRetry && !isRetrying && ( + + )} + {!isRetrying && ( + + )} +
+ + {expanded && !isRetrying && ( +
+ +
+ )} +
+ ); +} + +export default SaveErrorItem; diff --git a/frontend/src/components/ServiceAccountDrawer/ServiceAccountDrawer.styles.scss b/frontend/src/components/ServiceAccountDrawer/ServiceAccountDrawer.styles.scss index a6804dffee0..9d12ff07470 100644 --- a/frontend/src/components/ServiceAccountDrawer/ServiceAccountDrawer.styles.scss +++ b/frontend/src/components/ServiceAccountDrawer/ServiceAccountDrawer.styles.scss @@ -92,6 +92,23 @@ display: flex; flex-direction: column; gap: var(--spacing-8); + + &::-webkit-scrollbar { + width: 0.25rem; + } + + &::-webkit-scrollbar-thumb { + background: rgba(136, 136, 136, 0.4); + border-radius: 0.125rem; + + &:hover { + background: rgba(136, 136, 136, 0.7); + } + } + + &::-webkit-scrollbar-track { + background: transparent; + } } &__footer { @@ -239,6 +256,113 @@ letter-spacing: 0.48px; text-transform: uppercase; } + + &__save-errors { + display: flex; + flex-direction: column; + gap: var(--spacing-2); + } +} + +.sa-error-item { + border: 1px solid var(--l1-border); + border-radius: 4px; + overflow: hidden; + + &__header { + display: flex; + align-items: center; + gap: var(--spacing-3); + width: 100%; + padding: var(--padding-2) var(--padding-4); + background: transparent; + border: none; + cursor: pointer; + text-align: left; + outline: none; + + &:hover { + background: rgba(229, 72, 77, 0.08); + } + + &:focus-visible { + outline: 2px solid var(--primary); + outline-offset: -2px; + } + + &[aria-disabled='true'] { + cursor: default; + pointer-events: none; + } + } + + &:hover { + border-color: var(--callout-error-border); + } + + &__icon { + flex-shrink: 0; + color: var(--bg-cherry-500); + } + + &__title { + flex: 1; + min-width: 0; + font-size: var(--font-size-xs); + font-weight: var(--font-weight-medium); + color: var(--bg-cherry-500); + line-height: var(--line-height-18); + letter-spacing: -0.06px; + text-align: left; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + &__chevron { + flex-shrink: 0; + color: var(--l2-foreground); + } + + &__body { + border-top: 1px solid var(--l1-border); + + .error-content { + &__summary { + padding: 10px 12px; + } + + &__summary-left { + gap: 6px; + } + + &__error-code { + font-size: 12px; + line-height: 18px; + } + + &__error-message { + font-size: 11px; + line-height: 16px; + } + + &__docs-button { + font-size: 11px; + padding: 5px 8px; + } + + &__message-badge { + padding: 0 12px 10px; + gap: 8px; + } + + &__message-item { + font-size: 11px; + padding: 2px 12px 2px 22px; + margin-bottom: 2px; + } + } + } } .keys-tab { @@ -429,7 +553,7 @@ } } -.sa-disable-dialog { +.sa-delete-dialog { background: var(--l2-background); border: 1px solid var(--l2-border); diff --git a/frontend/src/components/ServiceAccountDrawer/ServiceAccountDrawer.tsx b/frontend/src/components/ServiceAccountDrawer/ServiceAccountDrawer.tsx index a801e4df54c..afad688d427 100644 --- a/frontend/src/components/ServiceAccountDrawer/ServiceAccountDrawer.tsx +++ b/frontend/src/components/ServiceAccountDrawer/ServiceAccountDrawer.tsx @@ -1,25 +1,29 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useQueryClient } from 'react-query'; import { Button } from '@signozhq/button'; import { DrawerWrapper } from '@signozhq/drawer'; -import { Key, LayoutGrid, Plus, PowerOff, X } from '@signozhq/icons'; +import { Key, LayoutGrid, Plus, Trash2, X } from '@signozhq/icons'; import { toast } from '@signozhq/sonner'; import { ToggleGroup, ToggleGroupItem } from '@signozhq/toggle-group'; import { Pagination, Skeleton } from 'antd'; import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs'; import { + getListServiceAccountsQueryKey, useGetServiceAccount, useListServiceAccountKeys, useUpdateServiceAccount, } from 'api/generated/services/serviceaccount'; -import { RenderErrorResponseDTO } from 'api/generated/services/sigNoz.schemas'; +import type { RenderErrorResponseDTO } from 'api/generated/services/sigNoz.schemas'; import { AxiosError } from 'axios'; import ErrorInPlace from 'components/ErrorInPlace/ErrorInPlace'; import { useRoles } from 'components/RolesSelect'; import { SA_QUERY_PARAMS } from 'container/ServiceAccountsSettings/constants'; import { ServiceAccountRow, + ServiceAccountStatus, toServiceAccountRow, } from 'container/ServiceAccountsSettings/utils'; +import { useServiceAccountRoleManager } from 'hooks/serviceAccount/useServiceAccountRoleManager'; import { parseAsBoolean, parseAsInteger, @@ -27,12 +31,14 @@ import { parseAsStringEnum, useQueryState, } from 'nuqs'; +import APIError from 'types/api/error'; import { toAPIError } from 'utils/errorUtils'; import AddKeyModal from './AddKeyModal'; -import DisableAccountModal from './DisableAccountModal'; +import DeleteAccountModal from './DeleteAccountModal'; import KeysTab from './KeysTab'; import OverviewTab from './OverviewTab'; +import type { SaveError } from './utils'; import { ServiceAccountDrawerTab } from './utils'; import './ServiceAccountDrawer.styles.scss'; @@ -69,12 +75,16 @@ function ServiceAccountDrawer({ SA_QUERY_PARAMS.ADD_KEY, parseAsBoolean.withDefault(false), ); - const [, setIsDisableOpen] = useQueryState( - SA_QUERY_PARAMS.DISABLE_SA, + const [, setIsDeleteOpen] = useQueryState( + SA_QUERY_PARAMS.DELETE_SA, parseAsBoolean.withDefault(false), ); const [localName, setLocalName] = useState(''); const [localRoles, setLocalRoles] = useState([]); + const [isSaving, setIsSaving] = useState(false); + const [saveErrors, setSaveErrors] = useState([]); + + const queryClient = useQueryClient(); const { data: accountData, @@ -93,21 +103,30 @@ function ServiceAccountDrawer({ [accountData], ); + const { currentRoles, applyDiff } = useServiceAccountRoleManager( + selectedAccountId ?? '', + ); + useEffect(() => { - if (account) { - setLocalName(account.name ?? ''); - setLocalRoles(account.roles ?? []); + if (account?.id) { + setLocalName(account?.name ?? ''); setKeysPage(1); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [account?.id]); + setSaveErrors([]); + }, [account?.id, account?.name, setKeysPage]); + + useEffect(() => { + setLocalRoles(currentRoles.map((r) => r.id).filter(Boolean) as string[]); + }, [currentRoles]); - const isDisabled = account?.status?.toUpperCase() !== 'ACTIVE'; + const isDeleted = + account?.status?.toUpperCase() === ServiceAccountStatus.Deleted; const isDirty = account !== null && (localName !== (account.name ?? '') || - JSON.stringify(localRoles) !== JSON.stringify(account.roles ?? [])); + JSON.stringify([...localRoles].sort()) !== + JSON.stringify([...currentRoles.map((r) => r.id).filter(Boolean)].sort())); const { roles: availableRoles, @@ -133,51 +152,189 @@ function ServiceAccountDrawer({ } }, [keysLoading, keys.length, keysPage, setKeysPage]); - const { mutate: updateAccount, isLoading: isSaving } = useUpdateServiceAccount( - { - mutation: { - onSuccess: () => { - toast.success('Service account updated successfully', { - richColors: true, - }); - refetchAccount(); - onSuccess({ closeDrawer: false }); - }, - onError: (error) => { - const errMessage = - convertToApiError( - error as AxiosError | null, - )?.getErrorMessage() || 'Failed to update service account'; - toast.error(errMessage, { richColors: true }); - }, - }, + // the retry for this mutation is safe due to the api being idempotent on backend + const { mutateAsync: updateMutateAsync } = useUpdateServiceAccount(); + + const toSaveApiError = useCallback( + (err: unknown): APIError => + convertToApiError(err as AxiosError) ?? + toAPIError(err as AxiosError), + [], + ); + + const retryNameUpdate = useCallback(async (): Promise => { + if (!account) { + return; + } + try { + await updateMutateAsync({ + pathParams: { id: account.id }, + data: { name: localName }, + }); + setSaveErrors((prev) => prev.filter((e) => e.context !== 'Name update')); + refetchAccount(); + queryClient.invalidateQueries(getListServiceAccountsQueryKey()); + } catch (err) { + setSaveErrors((prev) => + prev.map((e) => + e.context === 'Name update' ? { ...e, apiError: toSaveApiError(err) } : e, + ), + ); + } + }, [ + account, + localName, + updateMutateAsync, + refetchAccount, + queryClient, + toSaveApiError, + ]); + + const handleNameChange = useCallback((name: string): void => { + setLocalName(name); + setSaveErrors((prev) => prev.filter((e) => e.context !== 'Name update')); + }, []); + + const makeRoleRetry = useCallback( + ( + context: string, + rawRetry: () => Promise, + ) => async (): Promise => { + try { + await rawRetry(); + setSaveErrors((prev) => prev.filter((e) => e.context !== context)); + } catch (err) { + setSaveErrors((prev) => + prev.map((e) => + e.context === context ? { ...e, apiError: toSaveApiError(err) } : e, + ), + ); + } }, + [toSaveApiError], ); - function handleSave(): void { + const retryRolesUpdate = useCallback(async (): Promise => { + try { + const failures = await applyDiff(localRoles, availableRoles); + if (failures.length === 0) { + setSaveErrors((prev) => prev.filter((e) => e.context !== 'Roles update')); + } else { + setSaveErrors((prev) => { + const rest = prev.filter((e) => e.context !== 'Roles update'); + const roleErrors = failures.map((f) => { + const ctx = `Role '${f.roleName}'`; + return { + context: ctx, + apiError: toSaveApiError(f.error), + onRetry: makeRoleRetry(ctx, f.onRetry), + }; + }); + return [...rest, ...roleErrors]; + }); + } + } catch (err) { + setSaveErrors((prev) => + prev.map((e) => + e.context === 'Roles update' ? { ...e, apiError: toSaveApiError(err) } : e, + ), + ); + } + }, [localRoles, availableRoles, applyDiff, toSaveApiError, makeRoleRetry]); + + const handleSave = useCallback(async (): Promise => { if (!account || !isDirty) { return; } - updateAccount({ - pathParams: { id: account.id }, - data: { name: localName, email: account.email, roles: localRoles }, - }); - } + setSaveErrors([]); + setIsSaving(true); + try { + const namePromise = + localName !== (account.name ?? '') + ? updateMutateAsync({ + pathParams: { id: account.id }, + data: { name: localName }, + }) + : Promise.resolve(); + + const [nameResult, rolesResult] = await Promise.allSettled([ + namePromise, + applyDiff(localRoles, availableRoles), + ]); + + const errors: SaveError[] = []; + + if (nameResult.status === 'rejected') { + errors.push({ + context: 'Name update', + apiError: toSaveApiError(nameResult.reason), + onRetry: retryNameUpdate, + }); + } + + if (rolesResult.status === 'rejected') { + errors.push({ + context: 'Roles update', + apiError: toSaveApiError(rolesResult.reason), + onRetry: retryRolesUpdate, + }); + } else { + for (const failure of rolesResult.value) { + const context = `Role '${failure.roleName}'`; + errors.push({ + context, + apiError: toSaveApiError(failure.error), + onRetry: makeRoleRetry(context, failure.onRetry), + }); + } + } + + if (errors.length > 0) { + setSaveErrors(errors); + } else { + toast.success('Service account updated successfully', { + richColors: true, + }); + onSuccess({ closeDrawer: false }); + } + + refetchAccount(); + queryClient.invalidateQueries(getListServiceAccountsQueryKey()); + } finally { + setIsSaving(false); + } + }, [ + account, + isDirty, + localName, + localRoles, + availableRoles, + updateMutateAsync, + applyDiff, + refetchAccount, + onSuccess, + queryClient, + toSaveApiError, + retryNameUpdate, + makeRoleRetry, + retryRolesUpdate, + ]); const handleClose = useCallback((): void => { - setIsDisableOpen(null); + setIsDeleteOpen(null); setIsAddKeyOpen(null); setSelectedAccountId(null); setActiveTab(null); setKeysPage(null); setEditKeyId(null); + setSaveErrors([]); }, [ setSelectedAccountId, setActiveTab, setKeysPage, setEditKeyId, setIsAddKeyOpen, - setIsDisableOpen, + setIsDeleteOpen, ]); const drawerContent = ( @@ -220,7 +377,7 @@ function ServiceAccountDrawer({ variant="outlined" size="sm" color="secondary" - disabled={isDisabled} + disabled={isDeleted} onClick={(): void => { setIsAddKeyOpen(true); }} @@ -251,22 +408,23 @@ function ServiceAccountDrawer({ )} {activeTab === ServiceAccountDrawerTab.Keys && ( @@ -298,20 +456,20 @@ function ServiceAccountDrawer({ /> ) : ( <> - {!isDisabled && ( + {!isDeleted && ( )} - {!isDisabled && ( + {!isDeleted && (
-
- ), - children: ( -
- {APIKey?.createdByUser && ( - - Creator - - - {APIKey?.createdByUser?.displayName?.substring(0, 1)} - - - - {APIKey.createdByUser?.displayName} - - -
{APIKey.createdByUser?.email}
- -
- )} - - Created on - - {createdOn} - - - {updatedOn && ( - - Updated on - - {updatedOn} - - - )} - - - Expires on - - {expiresOn} - - -
- ), - }, - ]; - - return ( -
- - -
-
- - Last used - {formattedDateAndTime} -
- - {!isExpired && expiresIn <= EXPIRATION_WITHIN_SEVEN_DAYS && ( -
- Expires {dayjs().to(expiresOn)} -
- )} - - {isExpired && ( -
- Expired -
- )} -
-
- ); - }, - }, - ]; - - return ( -
-
-
- API Keys - - Create and manage API keys for the SigNoz API - -
- -
- } - value={searchValue} - onChange={handleSearch} - /> - - -
- - - `${range[0]}-${range[1]} of ${total} keys`, - }} - /> - - - {/* Delete Key Modal */} - Delete Key} - open={isDeleteModalOpen} - closable - afterClose={handleModalClose} - onCancel={hideDeleteViewModal} - destroyOnClose - footer={[ - , - , - ]} - > - - {t('delete_confirm_message', { - keyName: activeAPIKey?.name, - })} - - - - {/* Edit Key Modal */} - } - > - Cancel - , - , - ]} - > -
- - - - - - - - -
- Admin -
-
- -
- Editor -
-
- -
- Viewer -
-
-
-
-
- -
- - {/* Create New Key Modal */} - } - > - Copy key and close - , - ] - : [ - , - , - ] - } - > - {!showNewAPIKeyDetails && ( -
- - - - - - - - -
- Admin -
-
- -
- Editor -
-
- -
- Viewer -
-
-
-
-
- -
+ + + + + +
cell
, + ); + + expect(screen.getByText('cell')).toBeInTheDocument(); + }); + + it('marks row active when activeLog matches item id', () => { + const { container } = render( + + + + + + +
x
, + ); + + const row = container.querySelector('tr'); + expect(row).toBeTruthy(); + }); + + it('uses logsById entry when present for indicator type', () => { + const logFromMap = { id: 'row-1', severity_text: 'error' } as never; + render( + + + + + + +
x
, + ); + + expect(screen.getByText('x')).toBeInTheDocument(); + }); +}); diff --git a/frontend/src/container/LogsExplorerList/TanStackTableView/__tests__/TanStackHeaderRow.test.tsx b/frontend/src/container/LogsExplorerList/TanStackTableView/__tests__/TanStackHeaderRow.test.tsx new file mode 100644 index 00000000000..070fdf69a9f --- /dev/null +++ b/frontend/src/container/LogsExplorerList/TanStackTableView/__tests__/TanStackHeaderRow.test.tsx @@ -0,0 +1,152 @@ +import type { Header } from '@tanstack/react-table'; +import { render, screen } from '@testing-library/react'; +import { FontSize } from 'container/OptionsMenu/types'; + +import TanStackHeaderRow from '../TanStackHeaderRow'; +import type { OrderedColumn, TanStackTableRowData } from '../types'; + +jest.mock('../../InfinityTableView/styles', () => ({ + TableHeaderCellStyled: 'th', +})); + +const mockUseSortable = jest.fn((_args?: unknown) => ({ + attributes: {}, + listeners: {}, + setNodeRef: jest.fn(), + setActivatorNodeRef: jest.fn(), + transform: null, + transition: undefined, + isDragging: false, +})); + +jest.mock('@dnd-kit/sortable', () => ({ + useSortable: (args: unknown): ReturnType => + mockUseSortable(args), +})); + +jest.mock('@tanstack/react-table', () => ({ + flexRender: (def: unknown, ctx: unknown): unknown => { + if (typeof def === 'string') { + return def; + } + if (typeof def === 'function') { + return (def as (c: unknown) => unknown)(ctx); + } + return def; + }, +})); + +const column = (key: string): OrderedColumn => + ({ key, title: key } as OrderedColumn); + +const mockHeader = ( + id: string, + canResize = true, +): Header => + (({ + id, + column: { + getCanResize: (): boolean => canResize, + getIsResizing: (): boolean => false, + columnDef: { header: id }, + }, + getContext: (): unknown => ({}), + getResizeHandler: (): (() => void) => jest.fn(), + flexRender: undefined, + } as unknown) as Header); + +describe('TanStackHeaderRow', () => { + beforeEach(() => { + mockUseSortable.mockClear(); + }); + + it('renders column title when header is undefined', () => { + render( + + + + + + +
, + ); + + expect(screen.getByText('Timestamp')).toBeInTheDocument(); + }); + + it('enables useSortable for draggable columns', () => { + render( + + + + + + +
, + ); + + expect(mockUseSortable).toHaveBeenCalledWith( + expect.objectContaining({ + id: 'body', + disabled: false, + }), + ); + }); + + it('disables sortable for expand column', () => { + render( + + + + + + +
, + ); + + expect(mockUseSortable).toHaveBeenCalledWith( + expect.objectContaining({ + disabled: true, + }), + ); + }); + + it('shows drag grip for draggable columns', () => { + render( + + + + + + +
, + ); + + expect( + screen.getByRole('button', { name: /Drag body column/i }), + ).toBeInTheDocument(); + }); +}); diff --git a/frontend/src/container/LogsExplorerList/TanStackTableView/__tests__/TanStackRow.test.tsx b/frontend/src/container/LogsExplorerList/TanStackTableView/__tests__/TanStackRow.test.tsx new file mode 100644 index 00000000000..c9ef63466e7 --- /dev/null +++ b/frontend/src/container/LogsExplorerList/TanStackTableView/__tests__/TanStackRow.test.tsx @@ -0,0 +1,193 @@ +import { fireEvent, render, screen } from '@testing-library/react'; +import RowHoverContext from 'container/LogsExplorerList/RowHoverContext'; +import { FontSize } from 'container/OptionsMenu/types'; + +import TanStackRowCells from '../TanStackRow'; +import type { TanStackTableRowData } from '../types'; + +jest.mock('../../InfinityTableView/styles', () => ({ + TableCellStyled: 'td', +})); + +jest.mock( + 'components/Logs/LogLinesActionButtons/LogLinesActionButtons', + () => ({ + __esModule: true, + default: ({ + onLogCopy, + }: { + onLogCopy: (e: React.MouseEvent) => void; + }): JSX.Element => ( + + ), + }), +); + +const flexRenderMock = jest.fn((def: unknown, _ctx?: unknown) => + typeof def === 'function' ? def({}) : def, +); + +jest.mock('@tanstack/react-table', () => ({ + flexRender: (def: unknown, ctx: unknown): unknown => flexRenderMock(def, ctx), +})); + +function buildMockRow( + visibleCells: Array<{ columnId: string }>, +): Parameters[0]['row'] { + return { + original: { + currentLog: { id: 'log-1' } as TanStackTableRowData['currentLog'], + log: {}, + rowIndex: 0, + }, + getVisibleCells: () => + visibleCells.map((cell, index) => ({ + id: `cell-${index}`, + column: { + id: cell.columnId, + columnDef: { + cell: (): string => `content-${cell.columnId}`, + }, + }, + getContext: (): Record => ({}), + })), + } as never; +} + +describe('TanStackRowCells', () => { + beforeEach(() => { + flexRenderMock.mockClear(); + }); + + it('renders a cell per visible column and calls flexRender', () => { + const row = buildMockRow([ + { columnId: 'state-indicator' }, + { columnId: 'body' }, + ]); + + render( + + + + + + +
, + ); + + expect(screen.getAllByRole('cell')).toHaveLength(2); + expect(flexRenderMock).toHaveBeenCalled(); + }); + + it('applies state-indicator styling class on the indicator cell', () => { + const row = buildMockRow([{ columnId: 'state-indicator' }]); + + const { container } = render( + + + + + + +
, + ); + + expect(container.querySelector('td.state-indicator')).toBeInTheDocument(); + }); + + it('renders row actions on logs explorer page after hover', () => { + const row = buildMockRow([{ columnId: 'body' }]); + + render( + + + + + + + +
+
, + ); + + expect(screen.getByTestId('copy-btn')).toBeInTheDocument(); + }); + + it('click on a data cell calls onSetActiveLog with current log', () => { + const onSetActiveLog = jest.fn(); + const row = buildMockRow([{ columnId: 'body' }]); + + render( + + + + + + +
, + ); + + fireEvent.click(screen.getAllByRole('cell')[0]); + + expect(onSetActiveLog).toHaveBeenCalledWith( + expect.objectContaining({ id: 'log-1' }), + ); + }); + + it('when row is active log, click on cell clears active log', () => { + const onSetActiveLog = jest.fn(); + const onClearActiveLog = jest.fn(); + const row = buildMockRow([{ columnId: 'body' }]); + + render( + + + + + + +
, + ); + + fireEvent.click(screen.getAllByRole('cell')[0]); + + expect(onClearActiveLog).toHaveBeenCalled(); + expect(onSetActiveLog).not.toHaveBeenCalled(); + }); +}); diff --git a/frontend/src/container/LogsExplorerList/TanStackTableView/__tests__/TanStackTableView.test.tsx b/frontend/src/container/LogsExplorerList/TanStackTableView/__tests__/TanStackTableView.test.tsx new file mode 100644 index 00000000000..4d3a630a15c --- /dev/null +++ b/frontend/src/container/LogsExplorerList/TanStackTableView/__tests__/TanStackTableView.test.tsx @@ -0,0 +1,104 @@ +import { forwardRef } from 'react'; +import { render, screen } from '@testing-library/react'; +import { FontSize } from 'container/OptionsMenu/types'; + +import type { InfinityTableProps } from '../../InfinityTableView/types'; +import TanStackTableView from '../index'; + +jest.mock('react-virtuoso', () => ({ + TableVirtuoso: forwardRef< + unknown, + { + fixedHeaderContent?: () => JSX.Element; + itemContent: (i: number) => JSX.Element; + } + >(function MockVirtuoso({ fixedHeaderContent, itemContent }, _ref) { + return ( +
+ {fixedHeaderContent?.()} + {itemContent(0)} +
+ ); + }), +})); + +jest.mock('components/Logs/TableView/useTableView', () => ({ + useTableView: (): { + dataSource: Record[]; + columns: unknown[]; + } => ({ + dataSource: [{ id: '1' }], + columns: [ + { key: 'body', title: 'body', render: (): string => 'x' }, + { key: 'state-indicator', title: 's', render: (): string => 'y' }, + ], + }), +})); + +jest.mock('hooks/useDragColumns', () => ({ + __esModule: true, + default: (): { + draggedColumns: unknown[]; + onColumnOrderChange: () => void; + } => ({ + draggedColumns: [], + onColumnOrderChange: jest.fn(), + }), +})); + +jest.mock('hooks/logs/useActiveLog', () => ({ + useActiveLog: (): { activeLog: null } => ({ activeLog: null }), +})); + +jest.mock('hooks/logs/useCopyLogLink', () => ({ + useCopyLogLink: (): { activeLogId: null } => ({ activeLogId: null }), +})); + +jest.mock('hooks/useDarkMode', () => ({ + useIsDarkMode: (): boolean => false, +})); + +jest.mock('react-router-dom', () => ({ + useLocation: (): { pathname: string } => ({ pathname: '/logs' }), +})); + +jest.mock('react-use', () => ({ + useCopyToClipboard: (): [unknown, () => void] => [null, jest.fn()], +})); + +jest.mock('@signozhq/sonner', () => ({ + toast: { success: jest.fn() }, +})); + +jest.mock('components/Spinner', () => ({ + __esModule: true, + default: ({ tip }: { tip: string }): JSX.Element => ( +
{tip}
+ ), +})); + +const baseProps: InfinityTableProps = { + isLoading: false, + tableViewProps: { + logs: [{ id: '1' } as never], + fields: [], + linesPerRow: 3, + fontSize: FontSize.SMALL, + appendTo: 'end', + activeLogIndex: 0, + }, +}; + +describe('TanStackTableView', () => { + it('shows spinner while loading', () => { + render(); + + expect(screen.getByTestId('spinner')).toHaveTextContent('Getting Logs'); + }); + + it('renders virtuoso when not loading', () => { + render(); + + expect(screen.getByTestId('virtuoso')).toBeInTheDocument(); + }); +}); diff --git a/frontend/src/container/LogsExplorerList/TanStackTableView/__tests__/useColumnSizingPersistence.test.tsx b/frontend/src/container/LogsExplorerList/TanStackTableView/__tests__/useColumnSizingPersistence.test.tsx new file mode 100644 index 00000000000..4826aea2626 --- /dev/null +++ b/frontend/src/container/LogsExplorerList/TanStackTableView/__tests__/useColumnSizingPersistence.test.tsx @@ -0,0 +1,173 @@ +import { act, renderHook } from '@testing-library/react'; +import { LOCALSTORAGE } from 'constants/localStorage'; + +import type { OrderedColumn } from '../types'; +import { useColumnSizingPersistence } from '../useColumnSizingPersistence'; + +const mockGet = jest.fn(); +const mockSet = jest.fn(); + +jest.mock('api/browser/localstorage/get', () => ({ + __esModule: true, + default: (key: string): string | null => mockGet(key), +})); + +jest.mock('api/browser/localstorage/set', () => ({ + __esModule: true, + default: (key: string, value: string): void => { + mockSet(key, value); + }, +})); + +const col = (key: string): OrderedColumn => + ({ key, title: key } as OrderedColumn); + +describe('useColumnSizingPersistence', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockGet.mockReturnValue(null); + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.runOnlyPendingTimers(); + jest.useRealTimers(); + }); + + it('initializes with empty sizing when localStorage is empty', () => { + const { result } = renderHook(() => + useColumnSizingPersistence([col('body'), col('timestamp')]), + ); + + expect(result.current.columnSizing).toEqual({}); + }); + + it('parses flat ColumnSizingState from localStorage', () => { + mockGet.mockReturnValue(JSON.stringify({ body: 400, timestamp: 180 })); + + const { result } = renderHook(() => + useColumnSizingPersistence([col('body'), col('timestamp')]), + ); + + expect(result.current.columnSizing).toEqual({ body: 400, timestamp: 180 }); + }); + + it('parses PersistedColumnSizing wrapper with sizing + columnIdsSignature', () => { + mockGet.mockReturnValue( + JSON.stringify({ + version: 1, + columnIdsSignature: 'body|timestamp', + sizing: { body: 300 }, + }), + ); + + const { result } = renderHook(() => + useColumnSizingPersistence([col('body'), col('timestamp')]), + ); + + expect(result.current.columnSizing).toEqual({ body: 300 }); + }); + + it('drops invalid numeric entries when reading from localStorage', () => { + mockGet.mockReturnValue( + JSON.stringify({ + body: 200, + bad: NaN, + zero: 0, + neg: -1, + str: 'wide', + }), + ); + + const { result } = renderHook(() => + useColumnSizingPersistence([col('body'), col('bad'), col('zero')]), + ); + + expect(result.current.columnSizing).toEqual({ body: 200 }); + }); + + it('returns empty sizing when JSON is invalid', () => { + const spy = jest.spyOn(console, 'error').mockImplementation(() => {}); + mockGet.mockReturnValue('not-json'); + + const { result } = renderHook(() => + useColumnSizingPersistence([col('body')]), + ); + + expect(result.current.columnSizing).toEqual({}); + spy.mockRestore(); + }); + + it('prunes sizing for columns not in orderedColumns and strips fixed columns', () => { + mockGet.mockReturnValue(JSON.stringify({ body: 400, expand: 32, gone: 100 })); + + const { result, rerender } = renderHook( + ({ columns }: { columns: OrderedColumn[] }) => + useColumnSizingPersistence(columns), + { + initialProps: { + columns: [ + col('body'), + col('expand'), + col('state-indicator'), + ] as OrderedColumn[], + }, + }, + ); + + expect(result.current.columnSizing).toEqual({ body: 400 }); + + act(() => { + rerender({ + columns: [col('body'), col('expand'), col('state-indicator')], + }); + }); + + expect(result.current.columnSizing).toEqual({ body: 400 }); + }); + + it('updates setColumnSizing manually', () => { + const { result } = renderHook(() => + useColumnSizingPersistence([col('body')]), + ); + + act(() => { + result.current.setColumnSizing({ body: 500 }); + }); + + expect(result.current.columnSizing).toEqual({ body: 500 }); + }); + + it('debounces writes to localStorage', () => { + const { result } = renderHook(() => + useColumnSizingPersistence([col('body')]), + ); + + act(() => { + result.current.setColumnSizing({ body: 600 }); + }); + + expect(mockSet).not.toHaveBeenCalled(); + + act(() => { + jest.advanceTimersByTime(250); + }); + + expect(mockSet).toHaveBeenCalledWith( + LOCALSTORAGE.LOGS_LIST_COLUMN_SIZING, + expect.stringContaining('"body":600'), + ); + }); + + it('does not persist when ordered columns signature effect runs with empty ids early — still debounces empty sizing', () => { + const { result } = renderHook(() => useColumnSizingPersistence([])); + + expect(result.current.columnSizing).toEqual({}); + + act(() => { + jest.advanceTimersByTime(250); + }); + + expect(mockSet).toHaveBeenCalled(); + }); +}); diff --git a/frontend/src/container/LogsExplorerList/TanStackTableView/__tests__/useOrderedColumns.test.tsx b/frontend/src/container/LogsExplorerList/TanStackTableView/__tests__/useOrderedColumns.test.tsx new file mode 100644 index 00000000000..9a986d397f9 --- /dev/null +++ b/frontend/src/container/LogsExplorerList/TanStackTableView/__tests__/useOrderedColumns.test.tsx @@ -0,0 +1,222 @@ +import { act, renderHook } from '@testing-library/react'; + +import type { OrderedColumn } from '../types'; +import { useOrderedColumns } from '../useOrderedColumns'; + +const mockGetDraggedColumns = jest.fn(); + +jest.mock('hooks/useDragColumns/utils', () => ({ + getDraggedColumns: (current: unknown[], dragged: unknown[]): T[] => + mockGetDraggedColumns(current, dragged) as T[], +})); + +const col = (key: string, title?: string): OrderedColumn => + ({ key, title: title ?? key } as OrderedColumn); + +describe('useOrderedColumns', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('returns columns from getDraggedColumns filtered to keys with string or number', () => { + mockGetDraggedColumns.mockReturnValue([ + col('body'), + col('timestamp'), + { title: 'no-key' }, + ]); + + const { result } = renderHook(() => + useOrderedColumns({ + columns: [], + draggedColumns: [], + onColumnOrderChange: jest.fn(), + }), + ); + + expect(result.current.orderedColumns).toEqual([ + col('body'), + col('timestamp'), + ]); + expect(result.current.orderedColumnIds).toEqual(['body', 'timestamp']); + }); + + it('hasSingleColumn is true when exactly one column is not state-indicator', () => { + mockGetDraggedColumns.mockReturnValue([col('state-indicator'), col('body')]); + + const { result } = renderHook(() => + useOrderedColumns({ + columns: [], + draggedColumns: [], + onColumnOrderChange: jest.fn(), + }), + ); + + expect(result.current.hasSingleColumn).toBe(true); + }); + + it('hasSingleColumn is false when more than one non-state-indicator column exists', () => { + mockGetDraggedColumns.mockReturnValue([ + col('state-indicator'), + col('body'), + col('timestamp'), + ]); + + const { result } = renderHook(() => + useOrderedColumns({ + columns: [], + draggedColumns: [], + onColumnOrderChange: jest.fn(), + }), + ); + + expect(result.current.hasSingleColumn).toBe(false); + }); + + it('handleDragEnd reorders columns and calls onColumnOrderChange', () => { + const onColumnOrderChange = jest.fn(); + mockGetDraggedColumns.mockReturnValue([col('a'), col('b'), col('c')]); + + const { result } = renderHook(() => + useOrderedColumns({ + columns: [], + draggedColumns: [], + onColumnOrderChange, + }), + ); + + act(() => { + result.current.handleDragEnd({ + active: { id: 'a' }, + over: { id: 'c' }, + } as never); + }); + + expect(onColumnOrderChange).toHaveBeenCalledWith([ + expect.objectContaining({ key: 'b' }), + expect.objectContaining({ key: 'c' }), + expect.objectContaining({ key: 'a' }), + ]); + + // Derived-only: orderedColumns should remain until draggedColumns (URL/localStorage) updates. + expect(result.current.orderedColumns.map((c) => c.key)).toEqual([ + 'a', + 'b', + 'c', + ]); + }); + + it('handleDragEnd no-ops when over is null', () => { + const onColumnOrderChange = jest.fn(); + mockGetDraggedColumns.mockReturnValue([col('a'), col('b')]); + + const { result } = renderHook(() => + useOrderedColumns({ + columns: [], + draggedColumns: [], + onColumnOrderChange, + }), + ); + + const before = result.current.orderedColumns; + + act(() => { + result.current.handleDragEnd({ + active: { id: 'a' }, + over: null, + } as never); + }); + + expect(result.current.orderedColumns).toBe(before); + expect(onColumnOrderChange).not.toHaveBeenCalled(); + }); + + it('handleDragEnd no-ops when active.id equals over.id', () => { + const onColumnOrderChange = jest.fn(); + mockGetDraggedColumns.mockReturnValue([col('a'), col('b')]); + + const { result } = renderHook(() => + useOrderedColumns({ + columns: [], + draggedColumns: [], + onColumnOrderChange, + }), + ); + + act(() => { + result.current.handleDragEnd({ + active: { id: 'a' }, + over: { id: 'a' }, + } as never); + }); + + expect(onColumnOrderChange).not.toHaveBeenCalled(); + }); + + it('handleDragEnd no-ops when indices cannot be resolved', () => { + const onColumnOrderChange = jest.fn(); + mockGetDraggedColumns.mockReturnValue([col('a'), col('b')]); + + const { result } = renderHook(() => + useOrderedColumns({ + columns: [], + draggedColumns: [], + onColumnOrderChange, + }), + ); + + act(() => { + result.current.handleDragEnd({ + active: { id: 'missing' }, + over: { id: 'a' }, + } as never); + }); + + expect(onColumnOrderChange).not.toHaveBeenCalled(); + }); + + it('exposes sensors from useSensors', () => { + mockGetDraggedColumns.mockReturnValue([col('a')]); + + const { result } = renderHook(() => + useOrderedColumns({ + columns: [], + draggedColumns: [], + onColumnOrderChange: jest.fn(), + }), + ); + + expect(result.current.sensors).toBeDefined(); + }); + + it('syncs ordered columns when base order changes externally (e.g. URL / localStorage)', () => { + mockGetDraggedColumns.mockReturnValue([col('a'), col('b'), col('c')]); + + const { result, rerender } = renderHook( + ({ draggedColumns }: { draggedColumns: unknown[] }) => + useOrderedColumns({ + columns: [], + draggedColumns, + onColumnOrderChange: jest.fn(), + }), + { initialProps: { draggedColumns: [] as unknown[] } }, + ); + + expect(result.current.orderedColumns.map((column) => column.key)).toEqual([ + 'a', + 'b', + 'c', + ]); + + mockGetDraggedColumns.mockReturnValue([col('c'), col('b'), col('a')]); + + act(() => { + rerender({ draggedColumns: [{ title: 'from-url' }] as unknown[] }); + }); + + expect(result.current.orderedColumns.map((column) => column.key)).toEqual([ + 'c', + 'b', + 'a', + ]); + }); +}); diff --git a/frontend/src/container/LogsExplorerList/TanStackTableView/index.tsx b/frontend/src/container/LogsExplorerList/TanStackTableView/index.tsx new file mode 100644 index 00000000000..4e0a58fd94f --- /dev/null +++ b/frontend/src/container/LogsExplorerList/TanStackTableView/index.tsx @@ -0,0 +1,433 @@ +import { + forwardRef, + memo, + MouseEvent as ReactMouseEvent, + ReactElement, + useCallback, + useEffect, + useImperativeHandle, + useMemo, + useRef, + useState, +} from 'react'; +import { useLocation } from 'react-router-dom'; +import { useCopyToClipboard } from 'react-use'; +import { TableVirtuoso, TableVirtuosoHandle } from 'react-virtuoso'; +import { DndContext, pointerWithin } from '@dnd-kit/core'; +import { + horizontalListSortingStrategy, + SortableContext, +} from '@dnd-kit/sortable'; +import { toast } from '@signozhq/sonner'; +import { + ColumnDef, + getCoreRowModel, + useReactTable, +} from '@tanstack/react-table'; +import { VIEW_TYPES } from 'components/LogDetail/constants'; +import { ColumnTypeRender } from 'components/Logs/TableView/types'; +import { useTableView } from 'components/Logs/TableView/useTableView'; +import Spinner from 'components/Spinner'; +import { LOCALSTORAGE } from 'constants/localStorage'; +import { QueryParams } from 'constants/query'; +import ROUTES from 'constants/routes'; +import { useActiveLog } from 'hooks/logs/useActiveLog'; +import { useIsDarkMode } from 'hooks/useDarkMode'; +import useDragColumns from 'hooks/useDragColumns'; + +import { infinityDefaultStyles } from '../InfinityTableView/config'; +import { TanStackTableStyled } from '../InfinityTableView/styles'; +import { InfinityTableProps } from '../InfinityTableView/types'; +import TanStackCustomTableRow from './TanStackCustomTableRow'; +import TanStackHeaderRow from './TanStackHeaderRow'; +import TanStackRowCells from './TanStackRow'; +import { TableRecord, TanStackTableRowData } from './types'; +import { useColumnSizingPersistence } from './useColumnSizingPersistence'; +import { useOrderedColumns } from './useOrderedColumns'; +import { + getColumnId, + getColumnMinWidthPx, + resolveColumnTypeRender, +} from './utils'; + +import '../logsTableVirtuosoScrollbar.scss'; +import './styles/TanStackTableView.styles.scss'; + +const COLUMN_DND_AUTO_SCROLL = { + layoutShiftCompensation: false as const, + threshold: { x: 0.2, y: 0 }, +}; + +const TanStackTableView = forwardRef( + function TanStackTableView( + { + isLoading, + isFetching, + onRemoveColumn, + tableViewProps, + infitiyTableProps, + onSetActiveLog, + onClearActiveLog, + activeLog, + }: InfinityTableProps, + forwardedRef, + ): JSX.Element { + const { pathname } = useLocation(); + const virtuosoRef = useRef(null); + // could avoid this if directly use forwardedRef in TableVirtuoso, but need to verify if it causes any issue with react-virtuoso internal ref handling + useImperativeHandle( + forwardedRef, + () => virtuosoRef.current as TableVirtuosoHandle, + [], + ); + const [, setCopy] = useCopyToClipboard(); + const isDarkMode = useIsDarkMode(); + const isLogsExplorerPage = pathname === ROUTES.LOGS_EXPLORER; + const { activeLog: activeContextLog } = useActiveLog(); + + // Column definitions (shared with existing logs table) + const { dataSource, columns } = useTableView({ + ...tableViewProps, + onClickExpand: onSetActiveLog, + onOpenLogsContext: (log): void => onSetActiveLog?.(log, VIEW_TYPES.CONTEXT), + }); + + // Column order (drag + persisted order) + const { draggedColumns, onColumnOrderChange } = useDragColumns( + LOCALSTORAGE.LOGS_LIST_COLUMNS, + ); + const { + orderedColumns, + orderedColumnIds, + hasSingleColumn, + handleDragEnd, + sensors, + } = useOrderedColumns({ + columns, + draggedColumns, + onColumnOrderChange: onColumnOrderChange as (columns: unknown[]) => void, + }); + + // Column sizing (persisted). stored to localStorage. + const { columnSizing, setColumnSizing } = useColumnSizingPersistence( + orderedColumns, + ); + + // don't allow "remove column" when only state-indicator + one data col remain + const isAtMinimumRemovableColumns = useMemo( + () => + orderedColumns.filter( + (column) => column.key !== 'state-indicator' && column.key !== 'expand', + ).length <= 1, + [orderedColumns], + ); + + // Table data (TanStack row data shape) + // useTableView sends flattened log data. this would not be needed once we move to new log details view + const tableData = useMemo( + () => + dataSource + .map((log, rowIndex) => { + const currentLog = tableViewProps.logs[rowIndex]; + if (!currentLog) { + return null; + } + return { log, currentLog, rowIndex }; + }) + .filter(Boolean) as TanStackTableRowData[], + [dataSource, tableViewProps.logs], + ); + + // TanStack columns + table instance + const tanstackColumns = useMemo[]>( + () => + orderedColumns.map((column, index) => { + const isStateIndicator = column.key === 'state-indicator'; + const isExpand = column.key === 'expand'; + const isFixedColumn = isStateIndicator || isExpand; + const fixedWidth = isFixedColumn ? 32 : undefined; + const minWidthPx = getColumnMinWidthPx(column, orderedColumns); + const headerTitle = String(column.title || ''); + + return { + id: getColumnId(column), + header: headerTitle.replace(/^\w/, (character) => + character.toUpperCase(), + ), + accessorFn: (row): unknown => row.log[column.key as keyof TableRecord], + enableResizing: !isFixedColumn && index !== orderedColumns.length - 1, + minSize: fixedWidth ?? minWidthPx, + size: fixedWidth, // last column gets remaining space, so don't set initial size to avoid conflict with resizing + maxSize: fixedWidth, + cell: ({ row, getValue }): ReactElement | string | number | null => { + if (!column.render) { + return null; + } + + return resolveColumnTypeRender( + column.render( + getValue(), + row.original.log, + row.original.rowIndex, + ) as ColumnTypeRender>, + ); + }, + }; + }), + [orderedColumns], + ); + const table = useReactTable({ + data: tableData, + columns: tanstackColumns, + enableColumnResizing: true, + getCoreRowModel: getCoreRowModel(), + columnResizeMode: 'onChange', + onColumnSizingChange: setColumnSizing, + state: { + columnSizing, + }, + }); + const tableRows = table.getRowModel().rows; + + // Infinite-scroll footer UI state + const [loadMoreState, setLoadMoreState] = useState<{ + active: boolean; + startCount: number; + }>({ + active: false, + startCount: 0, + }); + + // Map to resolve full log object by id (row highlighting + indicator) + const logsById = useMemo( + () => new Map(tableViewProps.logs.map((log) => [String(log.id), log])), + [tableViewProps.logs], + ); + + // this is already written in parent. Check if this is needed. + useEffect(() => { + const activeLogIndex = tableViewProps.activeLogIndex ?? -1; + if (activeLogIndex < 0 || activeLogIndex >= tableRows.length) { + return; + } + + virtuosoRef.current?.scrollToIndex({ + index: activeLogIndex, + align: 'center', + behavior: 'auto', + }); + }, [tableRows.length, tableViewProps.activeLogIndex]); + + useEffect(() => { + if (!loadMoreState.active) { + return; + } + + if (!isFetching || tableRows.length > loadMoreState.startCount) { + setLoadMoreState((prev) => + prev.active ? { active: false, startCount: prev.startCount } : prev, + ); + } + }, [isFetching, loadMoreState, tableRows.length]); + + const handleLogCopy = useCallback( + (logId: string, event: ReactMouseEvent): void => { + event.preventDefault(); + event.stopPropagation(); + + const urlQuery = new URLSearchParams(window.location.search); + urlQuery.delete(QueryParams.activeLogId); + urlQuery.delete(QueryParams.relativeTime); + urlQuery.set(QueryParams.activeLogId, `"${logId}"`); + const link = `${window.location.origin}${pathname}?${urlQuery.toString()}`; + + setCopy(link); + toast.success('Copied to clipboard', { position: 'top-right' }); + }, + [pathname, setCopy], + ); + + const itemContent = useCallback( + (index: number): JSX.Element | null => { + const row = tableRows[index]; + if (!row) { + return null; + } + + return ( + + ); + }, + [ + activeLog?.id, + handleLogCopy, + isDarkMode, + isLogsExplorerPage, + onClearActiveLog, + onSetActiveLog, + tableRows, + tableViewProps.fontSize, + ], + ); + + const flatHeaders = useMemo( + () => table.getFlatHeaders().filter((header) => !header.isPlaceholder), + // eslint-disable-next-line react-hooks/exhaustive-deps + [tanstackColumns], + ); + + const tableHeader = useCallback(() => { + const orderedColumnsById = new Map( + orderedColumns.map((column) => [getColumnId(column), column] as const), + ); + + return ( + + + + {flatHeaders.map((header) => { + const column = orderedColumnsById.get(header.id); + if (!column) { + return null; + } + + return ( + + ); + })} + + + + ); + }, [ + flatHeaders, + handleDragEnd, + hasSingleColumn, + isDarkMode, + orderedColumnIds, + orderedColumns, + onRemoveColumn, + isAtMinimumRemovableColumns, + sensors, + tableViewProps.fontSize, + ]); + + const handleEndReached = useCallback( + (index: number): void => { + if (!infitiyTableProps?.onEndReached) { + return; + } + + setLoadMoreState({ + active: true, + startCount: tableRows.length, + }); + infitiyTableProps.onEndReached(index); + }, + [infitiyTableProps, tableRows.length], + ); + + if (isLoading) { + return ; + } + + return ( +
+ ( + + + {orderedColumns.map((column, colIndex) => { + const columnId = getColumnId(column); + const isFixedColumn = + column.key === 'expand' || column.key === 'state-indicator'; + const minWidthPx = getColumnMinWidthPx(column, orderedColumns); + const persistedWidth = columnSizing[columnId]; + const computedWidth = table.getColumn(columnId)?.getSize(); + const effectiveWidth = persistedWidth ?? computedWidth; + if (isFixedColumn) { + return ; + } + // Last data column should stretch to fill remaining space + const isLastColumn = colIndex === orderedColumns.length - 1; + if (isLastColumn) { + return ( + + ); + } + const widthPx = + effectiveWidth != null + ? Math.max(effectiveWidth, minWidthPx) + : minWidthPx; + return ( + + ); + })} + + {children} + + ), + TableRow: TanStackCustomTableRow, + }} + {...(infitiyTableProps?.onEndReached + ? { endReached: handleEndReached } + : {})} + /> + {loadMoreState.active && ( +
+ +
+ )} +
+ ); + }, +); + +export default memo(TanStackTableView); diff --git a/frontend/src/container/LogsExplorerList/TanStackTableView/styles/TanStackHeaderRow.styles.scss b/frontend/src/container/LogsExplorerList/TanStackTableView/styles/TanStackHeaderRow.styles.scss new file mode 100644 index 00000000000..56cddfc9b94 --- /dev/null +++ b/frontend/src/container/LogsExplorerList/TanStackTableView/styles/TanStackHeaderRow.styles.scss @@ -0,0 +1,153 @@ +.tanstack-header-cell { + position: sticky; + top: 0; + z-index: 2; + padding: 0; + transform: translate3d( + var(--tanstack-header-translate-x, 0px), + var(--tanstack-header-translate-y, 0px), + 0 + ); + transition: var(--tanstack-header-transition, none); + + &.is-dragging { + opacity: 0.85; + } + + &.is-resizing { + background: var(--l2-background-hover); + } +} + +.tanstack-header-content { + display: flex; + align-items: center; + height: 100%; + min-width: 0; + width: 100%; + cursor: default; + max-width: 100%; + + &.has-resize-control { + max-width: calc(100% - 5px); + } + + &.has-action-control { + max-width: calc(100% - 5px); + } + + &.has-resize-control.has-action-control { + max-width: calc(100% - 10px); + } +} + +.tanstack-grip-slot { + display: inline-flex; + align-items: center; + justify-content: center; + width: 18px; + height: 18px; + margin-right: 4px; + flex-shrink: 0; +} + +.tanstack-grip-activator { + display: inline-flex; + align-items: center; + justify-content: center; + width: 12px; + height: 12px; + cursor: grab; + color: var(--l2-foreground); + opacity: 1; + touch-action: none; +} + +.tanstack-header-action-trigger { + display: inline-flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + cursor: pointer; + flex-shrink: 0; + color: var(--l2-foreground); +} + +.tanstack-column-actions-content { + width: 140px; + padding: 0; + background: var(--l2-background); + border: 1px solid var(--l2-border); + border-radius: 4px; + box-shadow: none; +} + +.tanstack-remove-column-action { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + min-height: 32px; + padding: 0 8px; + border: none; + border-radius: 0; + background: transparent; + justify-content: flex-start; + cursor: pointer; + color: var(--l2-foreground); + font-size: 12px; + line-height: 16px; + font-weight: 500; + transition: background-color 120ms ease, color 120ms ease; + + &:hover { + background: var(--l2-background-hover); + color: var(--l2-foreground); + + .tanstack-remove-column-action-icon { + color: var(--l2-foreground); + } + } +} + +.tanstack-remove-column-action-icon { + font-size: 11px; + color: var(--l2-foreground); + opacity: 0.95; +} + +.tanstack-header-cell .cursor-col-resize { + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: 5px; + cursor: col-resize; + z-index: 10; + touch-action: none; + background: transparent; +} + +.tanstack-header-cell.is-resizing .cursor-col-resize { + background: var(--bg-robin-300); +} + +.tanstack-resize-handle-line { + position: absolute; + top: 0; + bottom: 0; + left: 50%; + width: 4px; + transform: translateX(-50%); + background: var(--l2-border); + opacity: 1; + pointer-events: none; + transition: background 120ms ease, width 120ms ease; +} + +.tanstack-header-cell.is-resizing .tanstack-resize-handle-line { + width: 2px; + background: var(--bg-robin-500); + transition: none; +} diff --git a/frontend/src/container/LogsExplorerList/TanStackTableView/styles/TanStackTableView.styles.scss b/frontend/src/container/LogsExplorerList/TanStackTableView/styles/TanStackTableView.styles.scss new file mode 100644 index 00000000000..d47e17a8d77 --- /dev/null +++ b/frontend/src/container/LogsExplorerList/TanStackTableView/styles/TanStackTableView.styles.scss @@ -0,0 +1,55 @@ +.tanstack-table-view-wrapper { + display: flex; + flex-direction: column; + width: 100%; + min-height: 0; +} + +.tanstack-fixed-col { + width: 32px; + min-width: 32px; + max-width: 32px; +} + +.tanstack-filler-col { + width: 100%; + min-width: 0; +} + +.tanstack-actions-col { + width: 0; + min-width: 0; + max-width: 0; +} + +.tanstack-load-more-container { + width: 100%; + min-height: 56px; + display: flex; + align-items: center; + justify-content: center; + padding: 8px 0 12px; + flex-shrink: 0; +} + +.tanstack-table-virtuoso { + width: 100%; + overflow-x: scroll; +} + +.tanstack-fontSize-small { + font-size: 11px; +} + +.tanstack-fontSize-medium { + font-size: 13px; +} + +.tanstack-fontSize-large { + font-size: 14px; +} + +.tanstack-table-foot-loader-cell { + text-align: center; + padding: 8px 0; +} diff --git a/frontend/src/container/LogsExplorerList/TanStackTableView/types.ts b/frontend/src/container/LogsExplorerList/TanStackTableView/types.ts new file mode 100644 index 00000000000..f809a668bf6 --- /dev/null +++ b/frontend/src/container/LogsExplorerList/TanStackTableView/types.ts @@ -0,0 +1,29 @@ +import { ColumnSizingState } from '@tanstack/react-table'; +import { ColumnTypeRender } from 'components/Logs/TableView/types'; +import { ILog } from 'types/api/logs/log'; + +export type TableRecord = Record; + +export type LogsTableColumnDef = { + key?: string | number; + title?: string; + render?: ( + value: unknown, + record: TableRecord, + index: number, + ) => ColumnTypeRender>; +}; + +export type OrderedColumn = LogsTableColumnDef & { + key: string | number; +}; + +export type TanStackTableRowData = { + log: TableRecord; + currentLog: ILog; + rowIndex: number; +}; + +export type PersistedColumnSizing = { + sizing: ColumnSizingState; +}; diff --git a/frontend/src/container/LogsExplorerList/TanStackTableView/useColumnSizingPersistence.ts b/frontend/src/container/LogsExplorerList/TanStackTableView/useColumnSizingPersistence.ts new file mode 100644 index 00000000000..cda5ed1c351 --- /dev/null +++ b/frontend/src/container/LogsExplorerList/TanStackTableView/useColumnSizingPersistence.ts @@ -0,0 +1,111 @@ +import { Dispatch, SetStateAction, useEffect, useMemo, useState } from 'react'; +import { ColumnSizingState } from '@tanstack/react-table'; +import getFromLocalstorage from 'api/browser/localstorage/get'; +import setToLocalstorage from 'api/browser/localstorage/set'; +import { LOCALSTORAGE } from 'constants/localStorage'; + +import { OrderedColumn, PersistedColumnSizing } from './types'; +import { getColumnId } from './utils'; + +const COLUMN_SIZING_PERSIST_DEBOUNCE_MS = 250; + +const sanitizeSizing = (input: unknown): ColumnSizingState => { + if (!input || typeof input !== 'object') { + return {}; + } + return Object.entries( + input as Record, + ).reduce((acc, [key, value]) => { + if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) { + return acc; + } + acc[key] = value; + return acc; + }, {}); +}; + +const readPersistedColumnSizing = (): ColumnSizingState => { + const rawSizing = getFromLocalstorage(LOCALSTORAGE.LOGS_LIST_COLUMN_SIZING); + if (!rawSizing) { + return {}; + } + + try { + const parsed = JSON.parse(rawSizing) as + | PersistedColumnSizing + | ColumnSizingState; + const sizing = ('sizing' in parsed + ? parsed.sizing + : parsed) as ColumnSizingState; + return sanitizeSizing(sizing); + } catch (error) { + console.error('Failed to parse persisted log column sizing', error); + return {}; + } +}; + +type UseColumnSizingPersistenceResult = { + columnSizing: ColumnSizingState; + setColumnSizing: Dispatch>; +}; + +export const useColumnSizingPersistence = ( + orderedColumns: OrderedColumn[], +): UseColumnSizingPersistenceResult => { + const [columnSizing, setColumnSizing] = useState(() => + readPersistedColumnSizing(), + ); + const orderedColumnIds = useMemo( + () => orderedColumns.map((column) => getColumnId(column)), + [orderedColumns], + ); + + useEffect(() => { + if (orderedColumnIds.length === 0) { + return; + } + + const validColumnIds = new Set(orderedColumnIds); + const nonResizableColumnIds = new Set( + orderedColumns + .filter( + (column) => column.key === 'expand' || column.key === 'state-indicator', + ) + .map((column) => getColumnId(column)), + ); + + setColumnSizing((previousSizing) => { + const nextSizing = Object.entries(previousSizing).reduce( + (acc, [columnId, size]) => { + if (!validColumnIds.has(columnId) || nonResizableColumnIds.has(columnId)) { + return acc; + } + acc[columnId] = size; + return acc; + }, + {}, + ); + const hasChanged = + Object.keys(nextSizing).length !== Object.keys(previousSizing).length || + Object.entries(nextSizing).some( + ([columnId, size]) => previousSizing[columnId] !== size, + ); + + return hasChanged ? nextSizing : previousSizing; + }); + }, [orderedColumnIds, orderedColumns]); + + useEffect(() => { + const timeoutId = window.setTimeout(() => { + const persistedSizing = { sizing: columnSizing }; + setToLocalstorage( + LOCALSTORAGE.LOGS_LIST_COLUMN_SIZING, + JSON.stringify(persistedSizing), + ); + }, COLUMN_SIZING_PERSIST_DEBOUNCE_MS); + + return (): void => window.clearTimeout(timeoutId); + }, [columnSizing]); + + return { columnSizing, setColumnSizing }; +}; diff --git a/frontend/src/container/LogsExplorerList/TanStackTableView/useOrderedColumns.ts b/frontend/src/container/LogsExplorerList/TanStackTableView/useOrderedColumns.ts new file mode 100644 index 00000000000..67573bd195b --- /dev/null +++ b/frontend/src/container/LogsExplorerList/TanStackTableView/useOrderedColumns.ts @@ -0,0 +1,108 @@ +import { useCallback, useMemo } from 'react'; +import { + DragEndEvent, + PointerSensor, + useSensor, + useSensors, +} from '@dnd-kit/core'; +import { arrayMove } from '@dnd-kit/sortable'; +import { getDraggedColumns } from 'hooks/useDragColumns/utils'; + +import { OrderedColumn, TableRecord } from './types'; +import { getColumnId } from './utils'; + +type UseOrderedColumnsProps = { + columns: unknown[]; + draggedColumns: unknown[]; + onColumnOrderChange: (columns: unknown[]) => void; +}; + +type UseOrderedColumnsResult = { + orderedColumns: OrderedColumn[]; + orderedColumnIds: string[]; + hasSingleColumn: boolean; + handleDragEnd: (event: DragEndEvent) => void; + sensors: ReturnType; +}; + +export const useOrderedColumns = ({ + columns, + draggedColumns, + onColumnOrderChange, +}: UseOrderedColumnsProps): UseOrderedColumnsResult => { + const baseColumns = useMemo( + () => + getDraggedColumns( + columns as never[], + draggedColumns as never[], + ).filter( + (column): column is OrderedColumn => + typeof column.key === 'string' || typeof column.key === 'number', + ), + [columns, draggedColumns], + ); + + const orderedColumns = useMemo(() => { + const stateIndicatorIndex = baseColumns.findIndex( + (column) => column.key === 'state-indicator', + ); + if (stateIndicatorIndex <= 0) { + return baseColumns; + } + const pinned = baseColumns[stateIndicatorIndex]; + const rest = baseColumns.filter((_, i) => i !== stateIndicatorIndex); + return [pinned, ...rest]; + }, [baseColumns]); + + const handleDragEnd = useCallback( + (event: DragEndEvent): void => { + const { active, over } = event; + if (!over || active.id === over.id) { + return; + } + + // Don't allow moving the state-indicator column + if (String(active.id) === 'state-indicator') { + return; + } + + const oldIndex = orderedColumns.findIndex( + (column) => getColumnId(column) === String(active.id), + ); + const newIndex = orderedColumns.findIndex( + (column) => getColumnId(column) === String(over.id), + ); + if (oldIndex === -1 || newIndex === -1) { + return; + } + + const nextColumns = arrayMove(orderedColumns, oldIndex, newIndex); + onColumnOrderChange(nextColumns as unknown[]); + }, + [onColumnOrderChange, orderedColumns], + ); + + const orderedColumnIds = useMemo( + () => orderedColumns.map((column) => getColumnId(column)), + [orderedColumns], + ); + const hasSingleColumn = useMemo( + () => + orderedColumns.filter((column) => column.key !== 'state-indicator') + .length === 1, + [orderedColumns], + ); + const sensors = useSensors( + useSensor(PointerSensor, { + activationConstraint: { distance: 4 }, + }), + ); + + return { + orderedColumns, + orderedColumnIds, + hasSingleColumn, + handleDragEnd, + sensors, + }; +}; diff --git a/frontend/src/container/LogsExplorerList/TanStackTableView/utils.ts b/frontend/src/container/LogsExplorerList/TanStackTableView/utils.ts new file mode 100644 index 00000000000..54f225f331a --- /dev/null +++ b/frontend/src/container/LogsExplorerList/TanStackTableView/utils.ts @@ -0,0 +1,61 @@ +import { cloneElement, isValidElement, ReactElement } from 'react'; +import { ColumnTypeRender } from 'components/Logs/TableView/types'; + +import { OrderedColumn } from './types'; + +export const getColumnId = (column: OrderedColumn): string => + String(column.key); + +/** Browser default root font size; TanStack column sizing uses px. */ +const REM_PX = 16; +const MIN_WIDTH_OTHER_REM = 12; +const MIN_WIDTH_BODY_REM = 40; + +/** When total column count is below this, body column min width is doubled (more horizontal space for few columns). */ +export const FEW_COLUMNS_BODY_MIN_WIDTH_THRESHOLD = 4; + +/** + * Minimum width (px) for TanStack column defs + colgroup. + * Design: state/expand 32px; body min 40rem (doubled when fewer than + * {@link FEW_COLUMNS_BODY_MIN_WIDTH_THRESHOLD} total columns); other columns use rem→px (16px root). + */ +export const getColumnMinWidthPx = ( + column: OrderedColumn, + orderedColumns?: OrderedColumn[], +): number => { + const key = String(column.key); + if (key === 'state-indicator' || key === 'expand') { + return 32; + } + if (key === 'body') { + const base = MIN_WIDTH_BODY_REM * REM_PX; + const fewColumns = + orderedColumns != null && + orderedColumns.length < FEW_COLUMNS_BODY_MIN_WIDTH_THRESHOLD; + return fewColumns ? base * 1.5 : base; + } + return MIN_WIDTH_OTHER_REM * REM_PX; +}; + +export const resolveColumnTypeRender = ( + rendered: ColumnTypeRender>, +): ReactElement | string | number | null => { + if ( + rendered && + typeof rendered === 'object' && + 'children' in rendered && + isValidElement(rendered.children) + ) { + const { children, props } = rendered as { + children: ReactElement; + props?: Record; + }; + return cloneElement(children, props || {}); + } + if (rendered && typeof rendered === 'object' && isValidElement(rendered)) { + return rendered; + } + return typeof rendered === 'string' || typeof rendered === 'number' + ? rendered + : null; +}; diff --git a/frontend/src/container/LogsExplorerList/index.tsx b/frontend/src/container/LogsExplorerList/index.tsx index 743e89c668b..65744d92def 100644 --- a/frontend/src/container/LogsExplorerList/index.tsx +++ b/frontend/src/container/LogsExplorerList/index.tsx @@ -25,9 +25,9 @@ import { ILog } from 'types/api/logs/log'; import { DataSource, StringOperators } from 'types/common/queryBuilder'; import NoLogs from '../NoLogs/NoLogs'; -import InfinityTableView from './InfinityTableView'; import { LogsExplorerListProps } from './LogsExplorerList.interfaces'; import { InfinityWrapperStyled } from './styles'; +import TanStackTableView from './TanStackTableView'; import { convertKeysToColumnFields, getEmptyLogsListConfig, @@ -61,7 +61,7 @@ function LogsExplorerList({ handleCloseLogDetail, } = useLogDetailHandlers(); - const { options } = useOptionsMenu({ + const { options, config } = useOptionsMenu({ storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS, dataSource: DataSource.LOGS, aggregateOperator: @@ -155,9 +155,10 @@ function LogsExplorerList({ if (options.format === 'table') { return ( - ); } @@ -216,11 +218,13 @@ function LogsExplorerList({ logs, onEndReached, getItemContent, + isFetching, selectedFields, handleChangeSelectedView, handleSetActiveLog, handleCloseLogDetail, activeLog, + config.addColumn?.onRemove, ]); const isTraceToLogsNavigation = useMemo(() => { diff --git a/frontend/src/container/LogsExplorerList/logsTableVirtuosoScrollbar.scss b/frontend/src/container/LogsExplorerList/logsTableVirtuosoScrollbar.scss new file mode 100644 index 00000000000..c697d4877f1 --- /dev/null +++ b/frontend/src/container/LogsExplorerList/logsTableVirtuosoScrollbar.scss @@ -0,0 +1,38 @@ +.logs-table-virtuoso-scroll { + scrollbar-width: thin; + scrollbar-color: var(--bg-slate-300) transparent; + + &::-webkit-scrollbar { + width: 4px; + height: 4px; + } + + &::-webkit-scrollbar-corner { + background: transparent; + } + + &::-webkit-scrollbar-track { + background: transparent; + } + + &::-webkit-scrollbar-thumb { + background: var(--bg-slate-300); + border-radius: 9999px; + } + + &::-webkit-scrollbar-thumb:hover { + background: var(--bg-slate-200); + } +} + +.lightMode .logs-table-virtuoso-scroll { + scrollbar-color: var(--bg-vanilla-300) transparent; + + &::-webkit-scrollbar-thumb { + background: var(--bg-vanilla-300); + } + + &::-webkit-scrollbar-thumb:hover { + background: var(--bg-vanilla-100); + } +} diff --git a/frontend/src/container/LogsExplorerList/styles.ts b/frontend/src/container/LogsExplorerList/styles.ts index 8fbbf74733b..c07ffbe6194 100644 --- a/frontend/src/container/LogsExplorerList/styles.ts +++ b/frontend/src/container/LogsExplorerList/styles.ts @@ -2,7 +2,7 @@ import styled from 'styled-components'; export const InfinityWrapperStyled = styled.div` flex: 1; - height: 40rem !important; display: flex; height: 100%; + min-height: 0; `; diff --git a/frontend/src/container/LogsExplorerViews/LogsActionsContainer.tsx b/frontend/src/container/LogsExplorerViews/LogsActionsContainer.tsx index 28a9b3c6982..7cb3ee15855 100644 --- a/frontend/src/container/LogsExplorerViews/LogsActionsContainer.tsx +++ b/frontend/src/container/LogsExplorerViews/LogsActionsContainer.tsx @@ -1,5 +1,5 @@ import { Switch, Typography } from 'antd'; -import LogsDownloadOptionsMenu from 'components/LogsDownloadOptionsMenu/LogsDownloadOptionsMenu'; +import DownloadOptionsMenu from 'components/DownloadOptionsMenu/DownloadOptionsMenu'; import LogsFormatOptionsMenu from 'components/LogsFormatOptionsMenu/LogsFormatOptionsMenu'; import ListViewOrderBy from 'components/OrderBy/ListViewOrderBy'; import { LOCALSTORAGE } from 'constants/localStorage'; @@ -21,8 +21,6 @@ function LogsActionsContainer({ isLoading, isError, isSuccess, - minTime, - maxTime, }: { listQuery: any; selectedPanelType: PANEL_TYPES; @@ -34,8 +32,6 @@ function LogsActionsContainer({ isLoading: boolean; isError: boolean; isSuccess: boolean; - minTime: number; - maxTime: number; }): JSX.Element { const { options, config } = useOptionsMenu({ storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS, @@ -96,12 +92,9 @@ function LogsActionsContainer({ />
-
diff --git a/frontend/src/container/LogsExplorerViews/LogsExplorerViews.styles.scss b/frontend/src/container/LogsExplorerViews/LogsExplorerViews.styles.scss index c23b6672489..f6849dceacf 100644 --- a/frontend/src/container/LogsExplorerViews/LogsExplorerViews.styles.scss +++ b/frontend/src/container/LogsExplorerViews/LogsExplorerViews.styles.scss @@ -1,6 +1,7 @@ .logs-explorer-views-container { margin-bottom: 24px; flex: 1; + min-height: 0; display: flex; flex-direction: column; @@ -9,6 +10,7 @@ display: flex; flex-direction: column; flex: 1; + min-height: 0; padding-bottom: 10px; .views-tabs-container { @@ -195,6 +197,7 @@ .logs-explorer-views-type-content { flex: 1; + min-height: 0; display: flex; flex-direction: column; @@ -210,12 +213,32 @@ } } + .table-view-container { + flex: 1; + min-height: 0; + overflow-y: visible; + } + .time-series-view-container { + flex: 1; + display: flex; + flex-direction: column; + min-height: 0; + overflow-y: visible; + .time-series-view-container-header { display: flex; justify-content: flex-start; align-items: center; padding: 12px; + flex-shrink: 0; + } + + .time-series-view { + flex-shrink: 0; + height: 65vh; + min-height: 450px; + padding-bottom: 140px; } } } diff --git a/frontend/src/container/LogsExplorerViews/index.tsx b/frontend/src/container/LogsExplorerViews/index.tsx index 4e30a21fb67..4516213ac8e 100644 --- a/frontend/src/container/LogsExplorerViews/index.tsx +++ b/frontend/src/container/LogsExplorerViews/index.tsx @@ -444,8 +444,6 @@ function LogsExplorerViewsContainer({ isLoading={isLoading} isError={isError} isSuccess={isSuccess} - minTime={minTime} - maxTime={maxTime} /> )} @@ -501,16 +499,18 @@ function LogsExplorerViewsContainer({
)} {selectedPanelType === PANEL_TYPES.TABLE && !showLiveLogs && ( - +
+ +
)}
diff --git a/frontend/src/container/LogsExplorerViews/tests/LogsExplorerViews.test.tsx b/frontend/src/container/LogsExplorerViews/tests/LogsExplorerViews.test.tsx index 31b1468abc7..637c0b9a3f9 100644 --- a/frontend/src/container/LogsExplorerViews/tests/LogsExplorerViews.test.tsx +++ b/frontend/src/container/LogsExplorerViews/tests/LogsExplorerViews.test.tsx @@ -168,7 +168,7 @@ describe('LogsExplorerViews -', () => { lodsQueryServerRequest(); const { queryByTestId } = renderer(); - const periscopeDownloadButtonTestId = 'periscope-btn-download-options'; + const periscopeDownloadButtonTestId = 'periscope-btn-download-logs'; const periscopeFormatButtonTestId = 'periscope-btn-format-options'; // Test that the periscope button is present diff --git a/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx b/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx index a3ef74481e3..10ec768d14d 100644 --- a/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx +++ b/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx @@ -42,8 +42,15 @@ function LogsPanelComponent({ setPageSize(value); setOffset(0); setRequestData((prev) => { - const newQueryData = { ...prev.query }; - newQueryData.builder.queryData[0].pageSize = value; + const newQueryData = { + ...prev.query, + builder: { + ...prev.query.builder, + queryData: prev.query.builder.queryData.map((qd, i) => + i === 0 ? { ...qd, pageSize: value } : qd, + ), + }, + }; return { ...prev, query: newQueryData, diff --git a/frontend/src/container/MeterExplorer/Explorer/types.ts b/frontend/src/container/MeterExplorer/Explorer/types.ts deleted file mode 100644 index 2e618b8d706..00000000000 --- a/frontend/src/container/MeterExplorer/Explorer/types.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { UseQueryResult } from 'react-query'; -import { RelatedMetric } from 'api/metricsExplorer/getRelatedMetrics'; -import { SuccessResponse } from 'types/api'; -import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; - -export enum ExplorerTabs { - TIME_SERIES = 'time-series', - RELATED_METRICS = 'related-metrics', -} - -export interface TimeSeriesProps { - showOneChartPerQuery: boolean; -} - -export interface RelatedMetricsProps { - metricNames: string[]; -} - -export interface RelatedMetricsCardProps { - metric: RelatedMetricWithQueryResult; -} - -export interface UseGetRelatedMetricsGraphsProps { - selectedMetricName: string | null; - startMs: number; - endMs: number; -} - -export interface UseGetRelatedMetricsGraphsReturn { - relatedMetrics: RelatedMetricWithQueryResult[]; - isRelatedMetricsLoading: boolean; - isRelatedMetricsError: boolean; -} - -export interface RelatedMetricWithQueryResult extends RelatedMetric { - queryResult: UseQueryResult, unknown>; -} diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx index 04b8c84640d..6d8a7749708 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx @@ -32,6 +32,8 @@ import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { Query } from 'types/api/queryBuilder/queryBuilderData'; import { EQueryType } from 'types/common/dashboard'; import { GlobalReducer } from 'types/reducer/globalTime'; +import { isModifierKeyPressed } from 'utils/app'; +import { openInNewTab } from 'utils/navigation'; import { secondsToMilliseconds } from 'utils/timeUtils'; import { v4 as uuid } from 'uuid'; @@ -236,7 +238,7 @@ function Application(): JSX.Element { timestamp: number, apmToTraceQuery: Query, isViewLogsClicked?: boolean, - ): (() => void) => (): void => { + ): ((e: React.MouseEvent) => void) => (e: React.MouseEvent): void => { const endTime = secondsToMilliseconds(timestamp); const startTime = secondsToMilliseconds(timestamp - stepInterval); @@ -260,7 +262,11 @@ function Application(): JSX.Element { queryString, ); - history.push(newPath); + if (isModifierKeyPressed(e)) { + openInNewTab(newPath); + } else { + history.push(newPath); + } }, // eslint-disable-next-line react-hooks/exhaustive-deps [stepInterval], diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/GraphControlsPanel/GraphControlsPanel.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/GraphControlsPanel/GraphControlsPanel.tsx index 2b08b877f67..e6a1c34eab6 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview/GraphControlsPanel/GraphControlsPanel.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview/GraphControlsPanel/GraphControlsPanel.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { Color } from '@signozhq/design-tokens'; import { Button } from 'antd'; import { Binoculars, DraftingCompass, ScrollText } from 'lucide-react'; @@ -6,9 +7,9 @@ import './GraphControlsPanel.styles.scss'; interface GraphControlsPanelProps { id: string; - onViewLogsClick?: () => void; - onViewTracesClick: () => void; - onViewAPIMonitoringClick?: () => void; + onViewLogsClick?: (e: React.MouseEvent) => void; + onViewTracesClick: (e: React.MouseEvent) => void; + onViewAPIMonitoringClick?: (e: React.MouseEvent) => void; } function GraphControlsPanel({ diff --git a/frontend/src/container/MetricsApplication/Tabs/util.ts b/frontend/src/container/MetricsApplication/Tabs/util.ts index c78793514d9..ffb24fbb8e3 100644 --- a/frontend/src/container/MetricsApplication/Tabs/util.ts +++ b/frontend/src/container/MetricsApplication/Tabs/util.ts @@ -1,4 +1,4 @@ -import { Dispatch, SetStateAction, useMemo, useRef } from 'react'; +import React, { Dispatch, SetStateAction, useMemo, useRef } from 'react'; import { QueryParams } from 'constants/query'; import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder'; import ROUTES from 'constants/routes'; @@ -22,6 +22,7 @@ import { } from 'types/api/queryBuilder/queryBuilderData'; import { DataSource } from 'types/common/queryBuilder'; import { Tags } from 'types/reducer/trace'; +import { isModifierKeyPressed } from 'utils/app'; import { secondsToMilliseconds } from 'utils/timeUtils'; import { v4 as uuid } from 'uuid'; @@ -42,7 +43,7 @@ interface OnViewTracePopupClickProps { apmToTraceQuery: Query; isViewLogsClicked?: boolean; stepInterval?: number; - safeNavigate: (url: string) => void; + safeNavigate: (url: string, options?: { newTab?: boolean }) => void; } interface OnViewAPIMonitoringPopupClickProps { @@ -51,8 +52,7 @@ interface OnViewAPIMonitoringPopupClickProps { stepInterval?: number; domainName: string; isError: boolean; - - safeNavigate: (url: string) => void; + safeNavigate: (url: string, options?: { newTab?: boolean }) => void; } export function generateExplorerPath( @@ -93,8 +93,8 @@ export function onViewTracePopupClick({ isViewLogsClicked, stepInterval, safeNavigate, -}: OnViewTracePopupClickProps): VoidFunction { - return (): void => { +}: OnViewTracePopupClickProps): (e?: React.MouseEvent) => void { + return (e?: React.MouseEvent): void => { const endTime = secondsToMilliseconds(timestamp); const startTime = secondsToMilliseconds(timestamp - (stepInterval || 60)); @@ -118,7 +118,7 @@ export function onViewTracePopupClick({ queryString, ); - safeNavigate(newPath); + safeNavigate(newPath, { newTab: !!e && isModifierKeyPressed(e) }); }; } @@ -149,8 +149,8 @@ export function onViewAPIMonitoringPopupClick({ isError, stepInterval, safeNavigate, -}: OnViewAPIMonitoringPopupClickProps): VoidFunction { - return (): void => { +}: OnViewAPIMonitoringPopupClickProps): (e?: React.MouseEvent) => void { + return (e?: React.MouseEvent): void => { const endTime = timestamp + (stepInterval || 60); const startTime = timestamp - (stepInterval || 60); const filters = { @@ -190,7 +190,7 @@ export function onViewAPIMonitoringPopupClick({ filters, ); - safeNavigate(newPath); + safeNavigate(newPath, { newTab: !!e && isModifierKeyPressed(e) }); }; } diff --git a/frontend/src/container/MetricsExplorer/Explorer/Explorer.styles.scss b/frontend/src/container/MetricsExplorer/Explorer/Explorer.styles.scss index 6b5661eb90d..54c475739be 100644 --- a/frontend/src/container/MetricsExplorer/Explorer/Explorer.styles.scss +++ b/frontend/src/container/MetricsExplorer/Explorer/Explorer.styles.scss @@ -30,31 +30,6 @@ } } - .explore-tabs { - margin: 15px 0; - .tab { - background-color: var(--bg-slate-500); - border-color: var(--bg-ink-200); - width: 180px; - padding: 16px 0; - display: flex; - justify-content: center; - align-items: center; - } - - .tab:first-of-type { - border-top-left-radius: 2px; - } - - .tab:last-of-type { - border-top-right-radius: 2px; - } - - .selected-view { - background: var(--bg-ink-500); - } - } - .explore-content { padding: 0 8px; @@ -116,81 +91,6 @@ width: 100%; height: fit-content; } - - .related-metrics-container { - width: 100%; - min-height: 300px; - display: flex; - flex-direction: column; - gap: 10px; - - .related-metrics-header { - display: flex; - align-items: center; - justify-content: flex-start; - - .metric-name-select { - width: 20%; - margin-right: 10px; - } - - .related-metrics-input { - width: 40%; - - .ant-input-wrapper { - .ant-input-group-addon { - .related-metrics-select { - width: 250px; - border: 1px solid var(--bg-slate-500) !important; - - .ant-select-selector { - text-align: left; - color: var(--text-vanilla-500) !important; - } - } - } - } - } - } - - .related-metrics-body { - margin-top: 20px; - max-height: 650px; - overflow-y: scroll; - - .related-metrics-card-container { - margin-bottom: 20px; - min-height: 640px; - - .related-metrics-card { - display: flex; - flex-direction: column; - gap: 16px; - - .related-metrics-card-error { - padding-top: 10px; - height: fit-content; - width: fit-content; - } - } - } - } - } - } -} - -.lightMode { - .metrics-explorer-explore-container { - .explore-tabs { - .tab { - background-color: var(--bg-vanilla-100); - border-color: var(--bg-vanilla-400); - } - - .selected-view { - background: var(--bg-vanilla-500); - } - } } } diff --git a/frontend/src/container/MetricsExplorer/Explorer/Explorer.tsx b/frontend/src/container/MetricsExplorer/Explorer/Explorer.tsx index ce42ae4763b..eae56983d73 100644 --- a/frontend/src/container/MetricsExplorer/Explorer/Explorer.tsx +++ b/frontend/src/container/MetricsExplorer/Explorer/Explorer.tsx @@ -32,7 +32,6 @@ import { v4 as uuid } from 'uuid'; import { MetricsExplorerEventKeys, MetricsExplorerEvents } from '../events'; import MetricDetails from '../MetricDetails/MetricDetails'; import TimeSeries from './TimeSeries'; -import { ExplorerTabs } from './types'; import { getMetricUnits, splitQueryIntoOneChartPerQuery, @@ -95,7 +94,6 @@ function Explorer(): JSX.Element { const [disableOneChartPerQuery, toggleDisableOneChartPerQuery] = useState( false, ); - const [selectedTab] = useState(ExplorerTabs.TIME_SERIES); const [yAxisUnit, setYAxisUnit] = useState(); const unitsLength = useMemo(() => units.length, [units]); @@ -319,48 +317,21 @@ function Explorer(): JSX.Element { showFunctions={false} version="v3" /> - {/* TODO: Enable once we have resolved all related metrics issues */} - {/* - - - */}
- {selectedTab === ExplorerTabs.TIME_SERIES && ( - - )} - {/* TODO: Enable once we have resolved all related metrics issues */} - {/* {selectedTab === ExplorerTabs.RELATED_METRICS && ( - - )} */} +
(null); - const { maxTime, minTime } = useSelector( - (state) => state.globalTime, - ); - - const [selectedMetricName, setSelectedMetricName] = useState( - null, - ); - const [selectedRelatedMetric, setSelectedRelatedMetric] = useState('all'); - const [searchValue, setSearchValue] = useState(null); - - const startMs = useMemo(() => Math.floor(Number(minTime) / 1000000000), [ - minTime, - ]); - const endMs = useMemo(() => Math.floor(Number(maxTime) / 1000000000), [ - maxTime, - ]); - - useEffect(() => { - if (metricNames.length) { - setSelectedMetricName(metricNames[0]); - } - }, [metricNames]); - - const { - relatedMetrics, - isRelatedMetricsLoading, - isRelatedMetricsError, - } = useGetRelatedMetricsGraphs({ - selectedMetricName, - startMs, - endMs, - }); - - const metricNamesSelectOptions = useMemo( - () => - metricNames.map((name) => ({ - value: name, - label: name, - })), - [metricNames], - ); - - const relatedMetricsSelectOptions = useMemo(() => { - const options: { value: string; label: string }[] = [ - { - value: 'all', - label: 'All', - }, - ]; - relatedMetrics.forEach((metric) => { - options.push({ - value: metric.name, - label: metric.name, - }); - }); - return options; - }, [relatedMetrics]); - - const filteredRelatedMetrics = useMemo(() => { - let filteredMetrics: RelatedMetricWithQueryResult[] = []; - if (selectedRelatedMetric === 'all') { - filteredMetrics = [...relatedMetrics]; - } else { - filteredMetrics = relatedMetrics.filter( - (metric) => metric.name === selectedRelatedMetric, - ); - } - if (searchValue?.length) { - filteredMetrics = filteredMetrics.filter((metric) => - metric.name.toLowerCase().includes(searchValue?.toLowerCase() ?? ''), - ); - } - return filteredMetrics; - }, [relatedMetrics, selectedRelatedMetric, searchValue]); - - return ( -
-
- setSearchValue(e.target.value)} - bordered - addonBefore={ -