mirror of
https://github.com/SigNoz/signoz.git
synced 2026-04-01 10:00:20 +01:00
Compare commits
61 Commits
fix/array-
...
feat/cloud
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42300bd0a0 | ||
|
|
6e52f2c8f0 | ||
|
|
d9f8a4ae5a | ||
|
|
eefe3edffd | ||
|
|
2051861a03 | ||
|
|
4b01a40fb9 | ||
|
|
2d8a00bf18 | ||
|
|
f1b26b310f | ||
|
|
2c438b6c32 | ||
|
|
1814c2d13c | ||
|
|
e6cd771f11 | ||
|
|
6b94f87ca0 | ||
|
|
bf315253ae | ||
|
|
668ff7bc39 | ||
|
|
07f2aa52fd | ||
|
|
3416b3ad55 | ||
|
|
d6caa4f2c7 | ||
|
|
f86371566d | ||
|
|
9115803084 | ||
|
|
0c14d8f966 | ||
|
|
7afb461af8 | ||
|
|
a21fbb4ee0 | ||
|
|
0369842f3d | ||
|
|
59cd96562a | ||
|
|
cc4475cab7 | ||
|
|
ac8c648420 | ||
|
|
bede6be4b8 | ||
|
|
dd3d60e6df | ||
|
|
538ab686d2 | ||
|
|
936a325cb9 | ||
|
|
c6cdcd0143 | ||
|
|
cd9211d718 | ||
|
|
0601c28782 | ||
|
|
580610dbfa | ||
|
|
2d2aa02a81 | ||
|
|
dd9723ad13 | ||
|
|
3651469416 | ||
|
|
febce75734 | ||
|
|
e1616f3487 | ||
|
|
4b94287ac7 | ||
|
|
1575c7c54c | ||
|
|
8def3f835b | ||
|
|
11ed15f4c5 | ||
|
|
f47877cca9 | ||
|
|
bb2b9215ba | ||
|
|
3111904223 | ||
|
|
003e2c30d8 | ||
|
|
00fe516d10 | ||
|
|
0305f4f7db | ||
|
|
c60019a6dc | ||
|
|
acde2a37fa | ||
|
|
945241a52a | ||
|
|
e967f80c86 | ||
|
|
a09dc325de | ||
|
|
379b4f7fc4 | ||
|
|
5e536ae077 | ||
|
|
234585e642 | ||
|
|
2cc14f1ad4 | ||
|
|
dc4ed4d239 | ||
|
|
7281c36873 | ||
|
|
40288776e8 |
@@ -27,8 +27,8 @@ services:
|
|||||||
- ${PWD}/fs/tmp/var/lib/clickhouse/user_scripts/:/var/lib/clickhouse/user_scripts/
|
- ${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
|
- ${PWD}/../../../deploy/common/clickhouse/custom-function.xml:/etc/clickhouse-server/custom-function.xml
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:8123:8123"
|
- '127.0.0.1:8123:8123'
|
||||||
- "127.0.0.1:9000:9000"
|
- '127.0.0.1:9000:9000'
|
||||||
tty: true
|
tty: true
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test:
|
test:
|
||||||
@@ -47,16 +47,13 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
environment:
|
environment:
|
||||||
- CLICKHOUSE_SKIP_USER_SETUP=1
|
- CLICKHOUSE_SKIP_USER_SETUP=1
|
||||||
networks:
|
|
||||||
- default
|
|
||||||
- signoz-devenv
|
|
||||||
zookeeper:
|
zookeeper:
|
||||||
image: signoz/zookeeper:3.7.1
|
image: signoz/zookeeper:3.7.1
|
||||||
container_name: zookeeper
|
container_name: zookeeper
|
||||||
volumes:
|
volumes:
|
||||||
- ${PWD}/fs/tmp/zookeeper:/bitnami/zookeeper
|
- ${PWD}/fs/tmp/zookeeper:/bitnami/zookeeper
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:2181:2181"
|
- '127.0.0.1:2181:2181'
|
||||||
environment:
|
environment:
|
||||||
- ALLOW_ANONYMOUS_LOGIN=yes
|
- ALLOW_ANONYMOUS_LOGIN=yes
|
||||||
healthcheck:
|
healthcheck:
|
||||||
@@ -77,19 +74,12 @@ services:
|
|||||||
entrypoint:
|
entrypoint:
|
||||||
- /bin/sh
|
- /bin/sh
|
||||||
command:
|
command:
|
||||||
- -c
|
- -c
|
||||||
- |
|
- |
|
||||||
/signoz-otel-collector migrate bootstrap &&
|
/signoz-otel-collector migrate bootstrap &&
|
||||||
/signoz-otel-collector migrate sync up &&
|
/signoz-otel-collector migrate sync up &&
|
||||||
/signoz-otel-collector migrate async up
|
/signoz-otel-collector migrate async up
|
||||||
depends_on:
|
depends_on:
|
||||||
clickhouse:
|
clickhouse:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
networks:
|
|
||||||
- default
|
|
||||||
- signoz-devenv
|
|
||||||
|
|
||||||
networks:
|
|
||||||
signoz-devenv:
|
|
||||||
name: signoz-devenv
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ services:
|
|||||||
image: signoz/signoz-otel-collector:v0.142.0
|
image: signoz/signoz-otel-collector:v0.142.0
|
||||||
container_name: signoz-otel-collector-dev
|
container_name: signoz-otel-collector-dev
|
||||||
entrypoint:
|
entrypoint:
|
||||||
- /bin/sh
|
- /bin/sh
|
||||||
command:
|
command:
|
||||||
- -c
|
- -c
|
||||||
- |
|
- |
|
||||||
@@ -34,11 +34,4 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "host.docker.internal:host-gateway"
|
- "host.docker.internal:host-gateway"
|
||||||
networks:
|
|
||||||
- default
|
|
||||||
- signoz-devenv
|
|
||||||
|
|
||||||
networks:
|
|
||||||
signoz-devenv:
|
|
||||||
name: signoz-devenv
|
|
||||||
@@ -12,10 +12,10 @@ receivers:
|
|||||||
scrape_configs:
|
scrape_configs:
|
||||||
- job_name: otel-collector
|
- job_name: otel-collector
|
||||||
static_configs:
|
static_configs:
|
||||||
- targets:
|
- targets:
|
||||||
- localhost:8888
|
- localhost:8888
|
||||||
labels:
|
labels:
|
||||||
job_name: otel-collector
|
job_name: otel-collector
|
||||||
|
|
||||||
processors:
|
processors:
|
||||||
batch:
|
batch:
|
||||||
@@ -29,26 +29,7 @@ processors:
|
|||||||
signozspanmetrics/delta:
|
signozspanmetrics/delta:
|
||||||
metrics_exporter: signozclickhousemetrics
|
metrics_exporter: signozclickhousemetrics
|
||||||
metrics_flush_interval: 60s
|
metrics_flush_interval: 60s
|
||||||
latency_histogram_buckets:
|
latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ]
|
||||||
[
|
|
||||||
100us,
|
|
||||||
1ms,
|
|
||||||
2ms,
|
|
||||||
6ms,
|
|
||||||
10ms,
|
|
||||||
50ms,
|
|
||||||
100ms,
|
|
||||||
250ms,
|
|
||||||
500ms,
|
|
||||||
1000ms,
|
|
||||||
1400ms,
|
|
||||||
2000ms,
|
|
||||||
5s,
|
|
||||||
10s,
|
|
||||||
20s,
|
|
||||||
40s,
|
|
||||||
60s,
|
|
||||||
]
|
|
||||||
dimensions_cache_size: 100000
|
dimensions_cache_size: 100000
|
||||||
aggregation_temporality: AGGREGATION_TEMPORALITY_DELTA
|
aggregation_temporality: AGGREGATION_TEMPORALITY_DELTA
|
||||||
enable_exp_histogram: true
|
enable_exp_histogram: true
|
||||||
@@ -79,13 +60,13 @@ extensions:
|
|||||||
|
|
||||||
exporters:
|
exporters:
|
||||||
clickhousetraces:
|
clickhousetraces:
|
||||||
datasource: tcp://clickhouse:9000/signoz_traces
|
datasource: tcp://host.docker.internal:9000/signoz_traces
|
||||||
low_cardinal_exception_grouping: ${env:LOW_CARDINAL_EXCEPTION_GROUPING}
|
low_cardinal_exception_grouping: ${env:LOW_CARDINAL_EXCEPTION_GROUPING}
|
||||||
use_new_schema: true
|
use_new_schema: true
|
||||||
signozclickhousemetrics:
|
signozclickhousemetrics:
|
||||||
dsn: tcp://clickhouse:9000/signoz_metrics
|
dsn: tcp://host.docker.internal:9000/signoz_metrics
|
||||||
clickhouselogsexporter:
|
clickhouselogsexporter:
|
||||||
dsn: tcp://clickhouse:9000/signoz_logs
|
dsn: tcp://host.docker.internal:9000/signoz_logs
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
use_new_schema: true
|
use_new_schema: true
|
||||||
|
|
||||||
@@ -112,4 +93,4 @@ service:
|
|||||||
logs:
|
logs:
|
||||||
receivers: [otlp]
|
receivers: [otlp]
|
||||||
processors: [batch]
|
processors: [batch]
|
||||||
exporters: [clickhouselogsexporter]
|
exporters: [clickhouselogsexporter]
|
||||||
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@@ -86,8 +86,6 @@ go.mod @therealpandey
|
|||||||
/pkg/types/alertmanagertypes @srikanthccv
|
/pkg/types/alertmanagertypes @srikanthccv
|
||||||
/pkg/alertmanager/ @srikanthccv
|
/pkg/alertmanager/ @srikanthccv
|
||||||
/pkg/ruler/ @srikanthccv
|
/pkg/ruler/ @srikanthccv
|
||||||
/pkg/modules/rulestatehistory/ @srikanthccv
|
|
||||||
/pkg/types/rulestatehistorytypes/ @srikanthccv
|
|
||||||
|
|
||||||
# Correlation-adjacent
|
# Correlation-adjacent
|
||||||
|
|
||||||
@@ -107,7 +105,7 @@ go.mod @therealpandey
|
|||||||
/pkg/modules/authdomain/ @vikrantgupta25
|
/pkg/modules/authdomain/ @vikrantgupta25
|
||||||
/pkg/modules/role/ @vikrantgupta25
|
/pkg/modules/role/ @vikrantgupta25
|
||||||
|
|
||||||
# IdentN Owners
|
# IdentN Owners
|
||||||
/pkg/identn/ @vikrantgupta25
|
/pkg/identn/ @vikrantgupta25
|
||||||
/pkg/http/middleware/identn.go @vikrantgupta25
|
/pkg/http/middleware/identn.go @vikrantgupta25
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,12 @@ linters:
|
|||||||
- depguard
|
- depguard
|
||||||
- errcheck
|
- errcheck
|
||||||
- forbidigo
|
- forbidigo
|
||||||
- godot
|
|
||||||
- govet
|
- govet
|
||||||
- iface
|
- iface
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- misspell
|
- misspell
|
||||||
- nilnil
|
- nilnil
|
||||||
- sloglint
|
- sloglint
|
||||||
- staticcheck
|
|
||||||
- wastedassign
|
- wastedassign
|
||||||
- unparam
|
- unparam
|
||||||
- unused
|
- unused
|
||||||
|
|||||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -17,7 +17,5 @@
|
|||||||
},
|
},
|
||||||
"[html]": {
|
"[html]": {
|
||||||
"editor.defaultFormatter": "vscode.html-language-features"
|
"editor.defaultFormatter": "vscode.html-language-features"
|
||||||
},
|
}
|
||||||
"python-envs.defaultEnvManager": "ms-python.python:system",
|
|
||||||
"python-envs.pythonProjects": []
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/authz"
|
"github.com/SigNoz/signoz/pkg/authz"
|
||||||
"github.com/SigNoz/signoz/pkg/authz/openfgaauthz"
|
"github.com/SigNoz/signoz/pkg/authz/openfgaauthz"
|
||||||
"github.com/SigNoz/signoz/pkg/authz/openfgaschema"
|
"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/errors"
|
||||||
"github.com/SigNoz/signoz/pkg/factory"
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
"github.com/SigNoz/signoz/pkg/gateway"
|
"github.com/SigNoz/signoz/pkg/gateway"
|
||||||
@@ -79,13 +78,8 @@ 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) {
|
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)
|
return signoz.NewAuthNs(ctx, providerSettings, store, licensing)
|
||||||
},
|
},
|
||||||
func(ctx context.Context, sqlstore sqlstore.SQLStore, _ licensing.Licensing, _ dashboard.Module) (factory.ProviderFactory[authz.AuthZ, authz.Config], error) {
|
func(ctx context.Context, sqlstore sqlstore.SQLStore, _ licensing.Licensing, _ dashboard.Module) factory.ProviderFactory[authz.AuthZ, authz.Config] {
|
||||||
openfgaDataStore, err := openfgaserver.NewSQLStore(sqlstore)
|
return openfgaauthz.NewProviderFactory(sqlstore, openfgaschema.NewSchema().Get(ctx))
|
||||||
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 {
|
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)
|
return impldashboard.NewModule(impldashboard.NewStore(store), settings, analytics, orgGetter, queryParser)
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/SigNoz/signoz/ee/authn/callbackauthn/samlcallbackauthn"
|
"github.com/SigNoz/signoz/ee/authn/callbackauthn/samlcallbackauthn"
|
||||||
"github.com/SigNoz/signoz/ee/authz/openfgaauthz"
|
"github.com/SigNoz/signoz/ee/authz/openfgaauthz"
|
||||||
"github.com/SigNoz/signoz/ee/authz/openfgaschema"
|
"github.com/SigNoz/signoz/ee/authz/openfgaschema"
|
||||||
"github.com/SigNoz/signoz/ee/authz/openfgaserver"
|
|
||||||
"github.com/SigNoz/signoz/ee/gateway/httpgateway"
|
"github.com/SigNoz/signoz/ee/gateway/httpgateway"
|
||||||
enterpriselicensing "github.com/SigNoz/signoz/ee/licensing"
|
enterpriselicensing "github.com/SigNoz/signoz/ee/licensing"
|
||||||
"github.com/SigNoz/signoz/ee/licensing/httplicensing"
|
"github.com/SigNoz/signoz/ee/licensing/httplicensing"
|
||||||
@@ -119,13 +118,8 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
|||||||
|
|
||||||
return authNs, nil
|
return authNs, nil
|
||||||
},
|
},
|
||||||
func(ctx context.Context, sqlstore sqlstore.SQLStore, licensing licensing.Licensing, dashboardModule dashboard.Module) (factory.ProviderFactory[authz.AuthZ, authz.Config], error) {
|
func(ctx context.Context, sqlstore sqlstore.SQLStore, licensing licensing.Licensing, dashboardModule dashboard.Module) factory.ProviderFactory[authz.AuthZ, authz.Config] {
|
||||||
openfgaDataStore, err := openfgaserver.NewSQLStore(sqlstore)
|
return openfgaauthz.NewProviderFactory(sqlstore, openfgaschema.NewSchema().Get(ctx), licensing, dashboardModule)
|
||||||
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 {
|
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)
|
return impldashboard.NewModule(pkgimpldashboard.NewStore(store), settings, analytics, orgGetter, queryParser, querier, licensing)
|
||||||
|
|||||||
@@ -144,8 +144,6 @@ telemetrystore:
|
|||||||
|
|
||||||
##################### Prometheus #####################
|
##################### Prometheus #####################
|
||||||
prometheus:
|
prometheus:
|
||||||
# The maximum time a PromQL query is allowed to run before being aborted.
|
|
||||||
timeout: 2m
|
|
||||||
active_query_tracker:
|
active_query_tracker:
|
||||||
# Whether to enable the active query tracker.
|
# Whether to enable the active query tracker.
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ services:
|
|||||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||||
signoz:
|
signoz:
|
||||||
!!merge <<: *db-depend
|
!!merge <<: *db-depend
|
||||||
image: signoz/signoz:v0.117.1
|
image: signoz/signoz:v0.116.1
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080" # signoz port
|
- "8080:8080" # signoz port
|
||||||
# - "6060:6060" # pprof port
|
# - "6060:6060" # pprof port
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ services:
|
|||||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||||
signoz:
|
signoz:
|
||||||
!!merge <<: *db-depend
|
!!merge <<: *db-depend
|
||||||
image: signoz/signoz:v0.117.1
|
image: signoz/signoz:v0.116.1
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080" # signoz port
|
- "8080:8080" # signoz port
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ services:
|
|||||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||||
signoz:
|
signoz:
|
||||||
!!merge <<: *db-depend
|
!!merge <<: *db-depend
|
||||||
image: signoz/signoz:${VERSION:-v0.117.1}
|
image: signoz/signoz:${VERSION:-v0.116.1}
|
||||||
container_name: signoz
|
container_name: signoz
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080" # signoz port
|
- "8080:8080" # signoz port
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ services:
|
|||||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||||
signoz:
|
signoz:
|
||||||
!!merge <<: *db-depend
|
!!merge <<: *db-depend
|
||||||
image: signoz/signoz:${VERSION:-v0.117.1}
|
image: signoz/signoz:${VERSION:-v0.116.1}
|
||||||
container_name: signoz
|
container_name: signoz
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080" # signoz port
|
- "8080:8080" # signoz port
|
||||||
|
|||||||
1641
docs/api/openapi.yml
1641
docs/api/openapi.yml
File diff suppressed because it is too large
Load Diff
@@ -123,7 +123,6 @@ if err := router.Handle("/api/v1/things", handler.New(
|
|||||||
Description: "This endpoint creates a thing",
|
Description: "This endpoint creates a thing",
|
||||||
Request: new(types.PostableThing),
|
Request: new(types.PostableThing),
|
||||||
RequestContentType: "application/json",
|
RequestContentType: "application/json",
|
||||||
RequestQuery: new(types.QueryableThing),
|
|
||||||
Response: new(types.GettableThing),
|
Response: new(types.GettableThing),
|
||||||
ResponseContentType: "application/json",
|
ResponseContentType: "application/json",
|
||||||
SuccessStatusCode: http.StatusCreated,
|
SuccessStatusCode: http.StatusCreated,
|
||||||
@@ -156,8 +155,6 @@ The `handler.New` function ties the HTTP handler to OpenAPI metadata via `OpenAP
|
|||||||
- **Request / RequestContentType**:
|
- **Request / RequestContentType**:
|
||||||
- `Request` is a Go type that describes the request body or form.
|
- `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).
|
- `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.
|
- **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 / ResponseContentType**:
|
||||||
- `Response` is the Go type for the successful response payload.
|
- `Response` is the Go type for the successful response payload.
|
||||||
|
|||||||
@@ -273,7 +273,6 @@ Options can be simple (direct link) or nested (with another question):
|
|||||||
- Place logo files in `public/Logos/`
|
- Place logo files in `public/Logos/`
|
||||||
- Use SVG format
|
- Use SVG format
|
||||||
- Reference as `"/Logos/your-logo.svg"`
|
- 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=<TARGET_URL>`, where `<TARGET_URL>` 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.
|
- **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
|
### 4. Links
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func (hp *HourlyProvider) GetBaseSeasonalProvider() *BaseSeasonalProvider {
|
|||||||
return &hp.BaseSeasonalProvider
|
return &hp.BaseSeasonalProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHourlyProvider now uses the generic option type.
|
// NewHourlyProvider now uses the generic option type
|
||||||
func NewHourlyProvider(opts ...GenericProviderOption[*HourlyProvider]) *HourlyProvider {
|
func NewHourlyProvider(opts ...GenericProviderOption[*HourlyProvider]) *HourlyProvider {
|
||||||
hp := &HourlyProvider{
|
hp := &HourlyProvider{
|
||||||
BaseSeasonalProvider: BaseSeasonalProvider{},
|
BaseSeasonalProvider: BaseSeasonalProvider{},
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ type AnomaliesResponse struct {
|
|||||||
// | |
|
// | |
|
||||||
// (rounded value for past peiod) + (seasonal growth)
|
// (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 {
|
type anomalyQueryParams struct {
|
||||||
// CurrentPeriodQuery is the query range params for period user is looking at or eval window
|
// 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)
|
// Example: (now-5m, now), (now-30m, now), (now-1h, now)
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ var (
|
|||||||
movingAvgWindowSize = 7
|
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 {
|
type BaseProvider interface {
|
||||||
GetBaseSeasonalProvider() *BaseSeasonalProvider
|
GetBaseSeasonalProvider() *BaseSeasonalProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenericProviderOption is a generic type for provider options.
|
// GenericProviderOption is a generic type for provider options
|
||||||
type GenericProviderOption[T BaseProvider] func(T)
|
type GenericProviderOption[T BaseProvider] func(T)
|
||||||
|
|
||||||
func WithQuerier[T BaseProvider](querier querier.Querier) GenericProviderOption[T] {
|
func WithQuerier[T BaseProvider](querier querier.Querier) GenericProviderOption[T] {
|
||||||
@@ -121,7 +121,7 @@ func (p *BaseSeasonalProvider) getResults(ctx context.Context, orgID valuer.UUID
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getMatchingSeries gets the matching series from the query result
|
// 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 {
|
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 {
|
if queryResult == nil || len(queryResult.Aggregations) == 0 || len(queryResult.Aggregations[0].Series) == 0 {
|
||||||
return nil
|
return nil
|
||||||
@@ -155,14 +155,13 @@ func (p *BaseSeasonalProvider) getStdDev(series *qbtypes.TimeSeries) float64 {
|
|||||||
avg := p.getAvg(series)
|
avg := p.getAvg(series)
|
||||||
var sum float64
|
var sum float64
|
||||||
for _, smpl := range series.Values {
|
for _, smpl := range series.Values {
|
||||||
d := smpl.Value - avg
|
sum += math.Pow(smpl.Value-avg, 2)
|
||||||
sum += d * d
|
|
||||||
}
|
}
|
||||||
return math.Sqrt(sum / float64(len(series.Values)))
|
return math.Sqrt(sum / float64(len(series.Values)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// getMovingAvg gets the moving average for the given series
|
// 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 {
|
func (p *BaseSeasonalProvider) getMovingAvg(series *qbtypes.TimeSeries, movingAvgWindowSize, startIdx int) float64 {
|
||||||
if series == nil || len(series.Values) == 0 {
|
if series == nil || len(series.Values) == 0 {
|
||||||
return 0
|
return 0
|
||||||
@@ -237,7 +236,7 @@ func (p *BaseSeasonalProvider) getPredictedSeries(
|
|||||||
// getBounds gets the upper and lower bounds for the given series
|
// getBounds gets the upper and lower bounds for the given series
|
||||||
// for the given z score threshold
|
// 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.
|
// moving avg of the previous period series - z score threshold * std dev of the series
|
||||||
func (p *BaseSeasonalProvider) getBounds(
|
func (p *BaseSeasonalProvider) getBounds(
|
||||||
series, predictedSeries, weekSeries *qbtypes.TimeSeries,
|
series, predictedSeries, weekSeries *qbtypes.TimeSeries,
|
||||||
zScoreThreshold float64,
|
zScoreThreshold float64,
|
||||||
@@ -270,7 +269,7 @@ func (p *BaseSeasonalProvider) getBounds(
|
|||||||
|
|
||||||
// getExpectedValue gets the expected value for the given series
|
// getExpectedValue gets the expected value for the given series
|
||||||
// for the given index
|
// 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(
|
func (p *BaseSeasonalProvider) getExpectedValue(
|
||||||
_, prevSeries, currentSeasonSeries, pastSeasonSeries, past2SeasonSeries, past3SeasonSeries *qbtypes.TimeSeries, idx int,
|
_, prevSeries, currentSeasonSeries, pastSeasonSeries, past2SeasonSeries, past3SeasonSeries *qbtypes.TimeSeries, idx int,
|
||||||
) float64 {
|
) float64 {
|
||||||
@@ -284,7 +283,7 @@ func (p *BaseSeasonalProvider) getExpectedValue(
|
|||||||
|
|
||||||
// getScore gets the anomaly score for the given series
|
// getScore gets the anomaly score for the given series
|
||||||
// for the given index
|
// for the given index
|
||||||
// (value - expectedValue) / std dev of the series.
|
// (value - expectedValue) / std dev of the series
|
||||||
func (p *BaseSeasonalProvider) getScore(
|
func (p *BaseSeasonalProvider) getScore(
|
||||||
series, prevSeries, weekSeries, weekPrevSeries, past2SeasonSeries, past3SeasonSeries *qbtypes.TimeSeries, value float64, idx int,
|
series, prevSeries, weekSeries, weekPrevSeries, past2SeasonSeries, past3SeasonSeries *qbtypes.TimeSeries, value float64, idx int,
|
||||||
) float64 {
|
) float64 {
|
||||||
@@ -297,7 +296,7 @@ func (p *BaseSeasonalProvider) getScore(
|
|||||||
|
|
||||||
// getAnomalyScores gets the anomaly scores for the given series
|
// getAnomalyScores gets the anomaly scores for the given series
|
||||||
// for the given index
|
// for the given index
|
||||||
// (value - expectedValue) / std dev of the series.
|
// (value - expectedValue) / std dev of the series
|
||||||
func (p *BaseSeasonalProvider) getAnomalyScores(
|
func (p *BaseSeasonalProvider) getAnomalyScores(
|
||||||
series, prevSeries, currentSeasonSeries, pastSeasonSeries, past2SeasonSeries, past3SeasonSeries *qbtypes.TimeSeries,
|
series, prevSeries, currentSeasonSeries, pastSeasonSeries, past2SeasonSeries, past3SeasonSeries *qbtypes.TimeSeries,
|
||||||
) *qbtypes.TimeSeries {
|
) *qbtypes.TimeSeries {
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/valuer"
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
||||||
openfgapkgtransformer "github.com/openfga/language/pkg/go/transformer"
|
openfgapkgtransformer "github.com/openfga/language/pkg/go/transformer"
|
||||||
"github.com/openfga/openfga/pkg/storage"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type provider struct {
|
type provider struct {
|
||||||
@@ -27,14 +26,14 @@ type provider struct {
|
|||||||
registry []authz.RegisterTypeable
|
registry []authz.RegisterTypeable
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProviderFactory(sqlstore sqlstore.SQLStore, openfgaSchema []openfgapkgtransformer.ModuleFile, openfgaDataStore storage.OpenFGADatastore, licensing licensing.Licensing, registry ...authz.RegisterTypeable) factory.ProviderFactory[authz.AuthZ, authz.Config] {
|
func NewProviderFactory(sqlstore sqlstore.SQLStore, openfgaSchema []openfgapkgtransformer.ModuleFile, 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 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, openfgaDataStore, licensing, registry)
|
return newOpenfgaProvider(ctx, ps, config, sqlstore, openfgaSchema, licensing, registry)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
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, openfgaDataStore)
|
pkgOpenfgaAuthzProvider := pkgopenfgaauthz.NewProviderFactory(sqlstore, openfgaSchema)
|
||||||
pkgAuthzService, err := pkgOpenfgaAuthzProvider.New(ctx, settings, config)
|
pkgAuthzService, err := pkgOpenfgaAuthzProvider.New(ctx, settings, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -58,10 +57,6 @@ func (provider *provider) Start(ctx context.Context) error {
|
|||||||
return provider.openfgaServer.Start(ctx)
|
return provider.openfgaServer.Start(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) Healthy() <-chan struct{} {
|
|
||||||
return provider.openfgaServer.Healthy()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (provider *provider) Stop(ctx context.Context) error {
|
func (provider *provider) Stop(ctx context.Context) error {
|
||||||
return provider.openfgaServer.Stop(ctx)
|
return provider.openfgaServer.Stop(ctx)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ type Server struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewOpenfgaServer(ctx context.Context, pkgAuthzService authz.AuthZ) (*Server, error) {
|
func NewOpenfgaServer(ctx context.Context, pkgAuthzService authz.AuthZ) (*Server, error) {
|
||||||
|
|
||||||
return &Server{
|
return &Server{
|
||||||
pkgAuthzService: pkgAuthzService,
|
pkgAuthzService: pkgAuthzService,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -25,10 +26,6 @@ func (server *Server) Start(ctx context.Context) error {
|
|||||||
return server.pkgAuthzService.Start(ctx)
|
return server.pkgAuthzService.Start(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) Healthy() <-chan struct{} {
|
|
||||||
return server.pkgAuthzService.Healthy()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (server *Server) Stop(ctx context.Context) error {
|
func (server *Server) Stop(ctx context.Context) error {
|
||||||
return server.pkgAuthzService.Stop(ctx)
|
return server.pkgAuthzService.Stop(ctx)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
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())
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,7 @@ var (
|
|||||||
once sync.Once
|
once sync.Once
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config initializes the licensing configuration.
|
// initializes the licensing configuration
|
||||||
func Config(pollInterval time.Duration, failureThreshold int) licensing.Config {
|
func Config(pollInterval time.Duration, failureThreshold int) licensing.Config {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
config = licensing.Config{PollInterval: pollInterval, FailureThreshold: failureThreshold}
|
config = licensing.Config{PollInterval: pollInterval, FailureThreshold: failureThreshold}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ func (h *handler) QueryRange(rw http.ResponseWriter, req *http.Request) {
|
|||||||
// Build step intervals from the anomaly query
|
// Build step intervals from the anomaly query
|
||||||
stepIntervals := make(map[string]uint64)
|
stepIntervals := make(map[string]uint64)
|
||||||
if anomalyQuery.StepInterval.Duration > 0 {
|
if anomalyQuery.StepInterval.Duration > 0 {
|
||||||
stepIntervals[anomalyQuery.Name] = uint64(anomalyQuery.StepInterval.Seconds())
|
stepIntervals[anomalyQuery.Name] = uint64(anomalyQuery.StepInterval.Duration.Seconds())
|
||||||
}
|
}
|
||||||
|
|
||||||
finalResp := &qbtypes.QueryRangeResponse{
|
finalResp := &qbtypes.QueryRangeResponse{
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/cache"
|
"github.com/SigNoz/signoz/pkg/cache"
|
||||||
"github.com/SigNoz/signoz/pkg/http/middleware"
|
"github.com/SigNoz/signoz/pkg/http/middleware"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
"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/prometheus"
|
||||||
"github.com/SigNoz/signoz/pkg/querier"
|
"github.com/SigNoz/signoz/pkg/querier"
|
||||||
"github.com/SigNoz/signoz/pkg/signoz"
|
"github.com/SigNoz/signoz/pkg/signoz"
|
||||||
@@ -107,7 +106,6 @@ func NewServer(config signoz.Config, signoz *signoz.SigNoz) (*Server, error) {
|
|||||||
signoz.TelemetryMetadataStore,
|
signoz.TelemetryMetadataStore,
|
||||||
signoz.Prometheus,
|
signoz.Prometheus,
|
||||||
signoz.Modules.OrgGetter,
|
signoz.Modules.OrgGetter,
|
||||||
signoz.Modules.RuleStateHistory,
|
|
||||||
signoz.Querier,
|
signoz.Querier,
|
||||||
signoz.Instrumentation.ToProviderSettings(),
|
signoz.Instrumentation.ToProviderSettings(),
|
||||||
signoz.QueryParser,
|
signoz.QueryParser,
|
||||||
@@ -138,7 +136,6 @@ func NewServer(config signoz.Config, signoz *signoz.SigNoz) (*Server, error) {
|
|||||||
logParsingPipelineController, err := logparsingpipeline.NewLogParsingPipelinesController(
|
logParsingPipelineController, err := logparsingpipeline.NewLogParsingPipelinesController(
|
||||||
signoz.SQLStore,
|
signoz.SQLStore,
|
||||||
integrationsController.GetPipelinesForInstalledIntegrations,
|
integrationsController.GetPipelinesForInstalledIntegrations,
|
||||||
reader,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -242,6 +239,7 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*h
|
|||||||
apiHandler.RegisterWebSocketPaths(r, am)
|
apiHandler.RegisterWebSocketPaths(r, am)
|
||||||
apiHandler.RegisterMessagingQueuesRoutes(r, am)
|
apiHandler.RegisterMessagingQueuesRoutes(r, am)
|
||||||
apiHandler.RegisterThirdPartyApiRoutes(r, am)
|
apiHandler.RegisterThirdPartyApiRoutes(r, am)
|
||||||
|
apiHandler.MetricExplorerRoutes(r, am)
|
||||||
apiHandler.RegisterTraceFunnelsRoutes(r, am)
|
apiHandler.RegisterTraceFunnelsRoutes(r, am)
|
||||||
|
|
||||||
err := s.signoz.APIServer.AddToRouter(r)
|
err := s.signoz.APIServer.AddToRouter(r)
|
||||||
@@ -345,29 +343,28 @@ func (s *Server) Stop(ctx context.Context) error {
|
|||||||
return nil
|
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, ruleStateHistoryModule rulestatehistory.Module, querier querier.Querier, providerSettings factory.ProviderSettings, queryParser queryparser.QueryParser) (*baserules.Manager, error) {
|
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) {
|
||||||
ruleStore := sqlrulestore.NewRuleStore(sqlstore, queryParser, providerSettings)
|
ruleStore := sqlrulestore.NewRuleStore(sqlstore, queryParser, providerSettings)
|
||||||
maintenanceStore := sqlrulestore.NewMaintenanceStore(sqlstore)
|
maintenanceStore := sqlrulestore.NewMaintenanceStore(sqlstore)
|
||||||
// create manager opts
|
// create manager opts
|
||||||
managerOpts := &baserules.ManagerOptions{
|
managerOpts := &baserules.ManagerOptions{
|
||||||
TelemetryStore: telemetryStore,
|
TelemetryStore: telemetryStore,
|
||||||
MetadataStore: metadataStore,
|
MetadataStore: metadataStore,
|
||||||
Prometheus: prometheus,
|
Prometheus: prometheus,
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
Reader: ch,
|
Reader: ch,
|
||||||
Querier: querier,
|
Querier: querier,
|
||||||
Logger: providerSettings.Logger,
|
Logger: providerSettings.Logger,
|
||||||
Cache: cache,
|
Cache: cache,
|
||||||
EvalDelay: baseconst.GetEvalDelay(),
|
EvalDelay: baseconst.GetEvalDelay(),
|
||||||
PrepareTaskFunc: rules.PrepareTaskFunc,
|
PrepareTaskFunc: rules.PrepareTaskFunc,
|
||||||
PrepareTestRuleFunc: rules.TestNotification,
|
PrepareTestRuleFunc: rules.TestNotification,
|
||||||
Alertmanager: alertmanager,
|
Alertmanager: alertmanager,
|
||||||
OrgGetter: orgGetter,
|
OrgGetter: orgGetter,
|
||||||
RuleStore: ruleStore,
|
RuleStore: ruleStore,
|
||||||
MaintenanceStore: maintenanceStore,
|
MaintenanceStore: maintenanceStore,
|
||||||
SqlStore: sqlstore,
|
SqlStore: sqlstore,
|
||||||
QueryParser: queryParser,
|
QueryParser: queryParser,
|
||||||
RuleStateHistoryModule: ruleStateHistoryModule,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// create Manager
|
// create Manager
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "evaluation is invalid: %v", err)
|
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "evaluation is invalid: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Rule.RuleType == ruletypes.RuleTypeThreshold {
|
if opts.Rule.RuleType == ruletypes.RuleTypeThreshold {
|
||||||
// create a threshold rule
|
// create a threshold rule
|
||||||
tr, err := baserules.NewThresholdRule(
|
tr, err := baserules.NewThresholdRule(
|
||||||
@@ -42,7 +41,6 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error)
|
|||||||
baserules.WithSQLStore(opts.SQLStore),
|
baserules.WithSQLStore(opts.SQLStore),
|
||||||
baserules.WithQueryParser(opts.ManagerOpts.QueryParser),
|
baserules.WithQueryParser(opts.ManagerOpts.QueryParser),
|
||||||
baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore),
|
baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore),
|
||||||
baserules.WithRuleStateHistoryModule(opts.ManagerOpts.RuleStateHistoryModule),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -67,7 +65,6 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error)
|
|||||||
baserules.WithSQLStore(opts.SQLStore),
|
baserules.WithSQLStore(opts.SQLStore),
|
||||||
baserules.WithQueryParser(opts.ManagerOpts.QueryParser),
|
baserules.WithQueryParser(opts.ManagerOpts.QueryParser),
|
||||||
baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore),
|
baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore),
|
||||||
baserules.WithRuleStateHistoryModule(opts.ManagerOpts.RuleStateHistoryModule),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -93,7 +90,6 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error)
|
|||||||
baserules.WithSQLStore(opts.SQLStore),
|
baserules.WithSQLStore(opts.SQLStore),
|
||||||
baserules.WithQueryParser(opts.ManagerOpts.QueryParser),
|
baserules.WithQueryParser(opts.ManagerOpts.QueryParser),
|
||||||
baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore),
|
baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore),
|
||||||
baserules.WithRuleStateHistoryModule(opts.ManagerOpts.RuleStateHistoryModule),
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return task, err
|
return task, err
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ func TestManager_TestNotification_SendUnmatched_PromRule(t *testing.T) {
|
|||||||
WillReturnRows(samplesRows)
|
WillReturnRows(samplesRows)
|
||||||
|
|
||||||
// Create Prometheus provider for this test
|
// Create Prometheus provider for this test
|
||||||
promProvider = prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{Timeout: 2 * time.Minute}, store)
|
promProvider = prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, store)
|
||||||
},
|
},
|
||||||
ManagerOptionsHook: func(opts *rules.ManagerOptions) {
|
ManagerOptionsHook: func(opts *rules.ManagerOptions) {
|
||||||
// Set Prometheus provider for PromQL queries
|
// Set Prometheus provider for PromQL queries
|
||||||
|
|||||||
@@ -336,10 +336,9 @@ func (dialect *dialect) UpdatePrimaryKey(ctx context.Context, bun bun.IDB, oldMo
|
|||||||
}
|
}
|
||||||
|
|
||||||
fkReference := ""
|
fkReference := ""
|
||||||
switch reference {
|
if reference == Org {
|
||||||
case Org:
|
|
||||||
fkReference = OrgReference
|
fkReference = OrgReference
|
||||||
case User:
|
} else if reference == User {
|
||||||
fkReference = UserReference
|
fkReference = UserReference
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,10 +392,9 @@ func (dialect *dialect) AddPrimaryKey(ctx context.Context, bun bun.IDB, oldModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
fkReference := ""
|
fkReference := ""
|
||||||
switch reference {
|
if reference == Org {
|
||||||
case Org:
|
|
||||||
fkReference = OrgReference
|
fkReference = OrgReference
|
||||||
case User:
|
} else if reference == User {
|
||||||
fkReference = UserReference
|
fkReference = UserReference
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,21 +14,14 @@ import (
|
|||||||
"github.com/uptrace/bun/dialect/pgdialect"
|
"github.com/uptrace/bun/dialect/pgdialect"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Pooler = new(provider)
|
|
||||||
|
|
||||||
type provider struct {
|
type provider struct {
|
||||||
settings factory.ScopedProviderSettings
|
settings factory.ScopedProviderSettings
|
||||||
sqldb *sql.DB
|
sqldb *sql.DB
|
||||||
bundb *sqlstore.BunDB
|
bundb *sqlstore.BunDB
|
||||||
pgxPool *pgxpool.Pool
|
|
||||||
dialect *dialect
|
dialect *dialect
|
||||||
formatter sqlstore.SQLFormatter
|
formatter sqlstore.SQLFormatter
|
||||||
}
|
}
|
||||||
|
|
||||||
type Pooler interface {
|
|
||||||
Pool() *pgxpool.Pool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFactory(hookFactories ...factory.ProviderFactory[sqlstore.SQLStoreHook, sqlstore.Config]) factory.ProviderFactory[sqlstore.SQLStore, sqlstore.Config] {
|
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) {
|
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))
|
hooks := make([]sqlstore.SQLStoreHook, len(hookFactories))
|
||||||
@@ -69,7 +62,6 @@ func New(ctx context.Context, providerSettings factory.ProviderSettings, config
|
|||||||
settings: settings,
|
settings: settings,
|
||||||
sqldb: sqldb,
|
sqldb: sqldb,
|
||||||
bundb: bunDB,
|
bundb: bunDB,
|
||||||
pgxPool: pool,
|
|
||||||
dialect: new(dialect),
|
dialect: new(dialect),
|
||||||
formatter: newFormatter(bunDB.Dialect()),
|
formatter: newFormatter(bunDB.Dialect()),
|
||||||
}, nil
|
}, nil
|
||||||
@@ -83,10 +75,6 @@ func (provider *provider) SQLDB() *sql.DB {
|
|||||||
return provider.sqldb
|
return provider.sqldb
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) Pool() *pgxpool.Pool {
|
|
||||||
return provider.pgxPool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (provider *provider) Dialect() sqlstore.SQLDialect {
|
func (provider *provider) Dialect() sqlstore.SQLDialect {
|
||||||
return provider.dialect
|
return provider.dialect
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ var (
|
|||||||
once sync.Once
|
once sync.Once
|
||||||
)
|
)
|
||||||
|
|
||||||
// initializes the Zeus configuration.
|
// initializes the Zeus configuration
|
||||||
func Config() zeus.Config {
|
func Config() zeus.Config {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
parsedURL, err := neturl.Parse(url)
|
parsedURL, err := neturl.Parse(url)
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ func (provider *Provider) do(ctx context.Context, url *url.URL, method string, k
|
|||||||
return nil, provider.errFromStatusCode(response.StatusCode, errorMessage)
|
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 {
|
func (provider *Provider) errFromStatusCode(statusCode int, errorMessage string) error {
|
||||||
switch statusCode {
|
switch statusCode {
|
||||||
case http.StatusBadRequest:
|
case http.StatusBadRequest:
|
||||||
|
|||||||
@@ -205,25 +205,6 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
overrides: [
|
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: [
|
files: [
|
||||||
'**/*.test.{js,jsx,ts,tsx}',
|
'**/*.test.{js,jsx,ts,tsx}',
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
or
|
or
|
||||||
`docker build . -t tagname`
|
`docker build . -t tagname`
|
||||||
|
|
||||||
**Tag to remote url- Introduce versioning later on**
|
**Tag to remote url- Introduce versinoing later on**
|
||||||
|
|
||||||
```
|
```
|
||||||
docker tag signoz/frontend:latest 7296823551/signoz:latest
|
docker tag signoz/frontend:latest 7296823551/signoz:latest
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
interface SafeNavigateOptions {
|
interface SafeNavigateOptions {
|
||||||
replace?: boolean;
|
replace?: boolean;
|
||||||
state?: unknown;
|
state?: unknown;
|
||||||
newTab?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SafeNavigateTo {
|
interface SafeNavigateTo {
|
||||||
@@ -21,7 +20,9 @@ interface UseSafeNavigateReturn {
|
|||||||
|
|
||||||
export const useSafeNavigate = (): UseSafeNavigateReturn => ({
|
export const useSafeNavigate = (): UseSafeNavigateReturn => ({
|
||||||
safeNavigate: jest.fn(
|
safeNavigate: jest.fn(
|
||||||
(_to: SafeNavigateToType, _options?: SafeNavigateOptions) => {},
|
(to: SafeNavigateToType, options?: SafeNavigateOptions) => {
|
||||||
|
console.log(`Mock safeNavigate called with:`, to, options);
|
||||||
|
},
|
||||||
) as jest.MockedFunction<
|
) as jest.MockedFunction<
|
||||||
(to: SafeNavigateToType, options?: SafeNavigateOptions) => void
|
(to: SafeNavigateToType, options?: SafeNavigateOptions) => void
|
||||||
>,
|
>,
|
||||||
|
|||||||
@@ -164,7 +164,6 @@
|
|||||||
"vite-plugin-html": "3.2.2",
|
"vite-plugin-html": "3.2.2",
|
||||||
"web-vitals": "^0.2.4",
|
"web-vitals": "^0.2.4",
|
||||||
"xstate": "^4.31.0",
|
"xstate": "^4.31.0",
|
||||||
"zod": "4.3.6",
|
|
||||||
"zustand": "5.0.11"
|
"zustand": "5.0.11"
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
@@ -287,4 +286,4 @@
|
|||||||
"tmp": "0.2.4",
|
"tmp": "0.2.4",
|
||||||
"vite": "npm:rolldown-vite@7.3.1"
|
"vite": "npm:rolldown-vite@7.3.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="#fa520f" viewBox="0 0 24 24"><title>Mistral AI</title><path d="M17.143 3.429v3.428h-3.429v3.429h-3.428V6.857H6.857V3.43H3.43v13.714H0v3.428h10.286v-3.428H6.857v-3.429h3.429v3.429h3.429v-3.429h3.428v3.429h-3.428v3.428H24v-3.428h-3.43V3.429z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 294 B |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 120 120"><defs><linearGradient id="a" x1="0%" x2="100%" y1="0%" y2="100%"><stop offset="0%" stop-color="#ff4d4d"/><stop offset="100%" stop-color="#991b1b"/></linearGradient></defs><path fill="url(#a)" d="M60 10c-30 0-45 25-45 45s15 40 30 45v10h10v-10s5 2 10 0v10h10v-10c15-5 30-25 30-45S90 10 60 10"/><path fill="url(#a)" d="M20 45C5 40 0 50 5 60s15 5 20-5c3-7 0-10-5-10"/><path fill="url(#a)" d="M100 45c15-5 20 5 15 15s-15 5-20-5c-3-7 0-10 5-10"/><path stroke="#ff4d4d" stroke-linecap="round" stroke-width="3" d="M45 15Q35 5 30 8M75 15Q85 5 90 8"/><circle cx="45" cy="35" r="6" fill="#050810"/><circle cx="75" cy="35" r="6" fill="#050810"/><circle cx="46" cy="34" r="2.5" fill="#00e5cc"/><circle cx="76" cy="34" r="2.5" fill="#00e5cc"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 809 B |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>Render</title><path d="M18.263.007c-3.121-.147-5.744 2.109-6.192 5.082-.018.138-.045.272-.067.405-.696 3.703-3.936 6.507-7.827 6.507a7.9 7.9 0 0 1-3.825-.979.202.202 0 0 0-.302.178V24H12v-8.999c0-1.656 1.338-3 2.987-3h2.988c3.382 0 6.103-2.817 5.97-6.244-.12-3.084-2.61-5.603-5.682-5.75"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 362 B |
@@ -1,7 +1,6 @@
|
|||||||
import axios from 'api';
|
import axios from 'api';
|
||||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { DEFAULT_TIME_RANGE } from 'container/TopNav/DateTimeSelectionV2/constants';
|
|
||||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||||
import { CreatePublicDashboardProps } from 'types/api/dashboard/public/create';
|
import { CreatePublicDashboardProps } from 'types/api/dashboard/public/create';
|
||||||
|
|
||||||
@@ -9,7 +8,7 @@ const createPublicDashboard = async (
|
|||||||
props: CreatePublicDashboardProps,
|
props: CreatePublicDashboardProps,
|
||||||
): Promise<SuccessResponseV2<CreatePublicDashboardProps>> => {
|
): Promise<SuccessResponseV2<CreatePublicDashboardProps>> => {
|
||||||
|
|
||||||
const { dashboardId, timeRangeEnabled = false, defaultTimeRange = DEFAULT_TIME_RANGE } = props;
|
const { dashboardId, timeRangeEnabled = false, defaultTimeRange = '30m' } = props;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import axios from 'api';
|
import axios from 'api';
|
||||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { DEFAULT_TIME_RANGE } from 'container/TopNav/DateTimeSelectionV2/constants';
|
|
||||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||||
import { UpdatePublicDashboardProps } from 'types/api/dashboard/public/update';
|
import { UpdatePublicDashboardProps } from 'types/api/dashboard/public/update';
|
||||||
|
|
||||||
@@ -9,7 +8,7 @@ const updatePublicDashboard = async (
|
|||||||
props: UpdatePublicDashboardProps,
|
props: UpdatePublicDashboardProps,
|
||||||
): Promise<SuccessResponseV2<UpdatePublicDashboardProps>> => {
|
): Promise<SuccessResponseV2<UpdatePublicDashboardProps>> => {
|
||||||
|
|
||||||
const { dashboardId, timeRangeEnabled = false, defaultTimeRange = DEFAULT_TIME_RANGE } = props;
|
const { dashboardId, timeRangeEnabled = false, defaultTimeRange = '30m' } = props;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.put(
|
const response = await axios.put(
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,250 +0,0 @@
|
|||||||
/**
|
|
||||||
* ! 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<Healthz200>({
|
|
||||||
url: `/api/v2/healthz`,
|
|
||||||
method: 'GET',
|
|
||||||
signal,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getHealthzQueryKey = () => {
|
|
||||||
return [`/api/v2/healthz`] as const;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getHealthzQueryOptions = <
|
|
||||||
TData = Awaited<ReturnType<typeof healthz>>,
|
|
||||||
TError = ErrorType<Healthz503>
|
|
||||||
>(options?: {
|
|
||||||
query?: UseQueryOptions<Awaited<ReturnType<typeof healthz>>, TError, TData>;
|
|
||||||
}) => {
|
|
||||||
const { query: queryOptions } = options ?? {};
|
|
||||||
|
|
||||||
const queryKey = queryOptions?.queryKey ?? getHealthzQueryKey();
|
|
||||||
|
|
||||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof healthz>>> = ({
|
|
||||||
signal,
|
|
||||||
}) => healthz(signal);
|
|
||||||
|
|
||||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof healthz>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
> & { queryKey: QueryKey };
|
|
||||||
};
|
|
||||||
|
|
||||||
export type HealthzQueryResult = NonNullable<
|
|
||||||
Awaited<ReturnType<typeof healthz>>
|
|
||||||
>;
|
|
||||||
export type HealthzQueryError = ErrorType<Healthz503>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Health check
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function useHealthz<
|
|
||||||
TData = Awaited<ReturnType<typeof healthz>>,
|
|
||||||
TError = ErrorType<Healthz503>
|
|
||||||
>(options?: {
|
|
||||||
query?: UseQueryOptions<Awaited<ReturnType<typeof healthz>>, TError, TData>;
|
|
||||||
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
|
||||||
const queryOptions = getHealthzQueryOptions(options);
|
|
||||||
|
|
||||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
|
||||||
queryKey: QueryKey;
|
|
||||||
};
|
|
||||||
|
|
||||||
query.queryKey = queryOptions.queryKey;
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Health check
|
|
||||||
*/
|
|
||||||
export const invalidateHealthz = async (
|
|
||||||
queryClient: QueryClient,
|
|
||||||
options?: InvalidateOptions,
|
|
||||||
): Promise<QueryClient> => {
|
|
||||||
await queryClient.invalidateQueries(
|
|
||||||
{ queryKey: getHealthzQueryKey() },
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
return queryClient;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Liveness check
|
|
||||||
*/
|
|
||||||
export const livez = (signal?: AbortSignal) => {
|
|
||||||
return GeneratedAPIInstance<Livez200>({
|
|
||||||
url: `/api/v2/livez`,
|
|
||||||
method: 'GET',
|
|
||||||
signal,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getLivezQueryKey = () => {
|
|
||||||
return [`/api/v2/livez`] as const;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getLivezQueryOptions = <
|
|
||||||
TData = Awaited<ReturnType<typeof livez>>,
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>
|
|
||||||
>(options?: {
|
|
||||||
query?: UseQueryOptions<Awaited<ReturnType<typeof livez>>, TError, TData>;
|
|
||||||
}) => {
|
|
||||||
const { query: queryOptions } = options ?? {};
|
|
||||||
|
|
||||||
const queryKey = queryOptions?.queryKey ?? getLivezQueryKey();
|
|
||||||
|
|
||||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof livez>>> = ({
|
|
||||||
signal,
|
|
||||||
}) => livez(signal);
|
|
||||||
|
|
||||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof livez>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
> & { queryKey: QueryKey };
|
|
||||||
};
|
|
||||||
|
|
||||||
export type LivezQueryResult = NonNullable<Awaited<ReturnType<typeof livez>>>;
|
|
||||||
export type LivezQueryError = ErrorType<RenderErrorResponseDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Liveness check
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function useLivez<
|
|
||||||
TData = Awaited<ReturnType<typeof livez>>,
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>
|
|
||||||
>(options?: {
|
|
||||||
query?: UseQueryOptions<Awaited<ReturnType<typeof livez>>, TError, TData>;
|
|
||||||
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
|
||||||
const queryOptions = getLivezQueryOptions(options);
|
|
||||||
|
|
||||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
|
||||||
queryKey: QueryKey;
|
|
||||||
};
|
|
||||||
|
|
||||||
query.queryKey = queryOptions.queryKey;
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Liveness check
|
|
||||||
*/
|
|
||||||
export const invalidateLivez = async (
|
|
||||||
queryClient: QueryClient,
|
|
||||||
options?: InvalidateOptions,
|
|
||||||
): Promise<QueryClient> => {
|
|
||||||
await queryClient.invalidateQueries({ queryKey: getLivezQueryKey() }, options);
|
|
||||||
|
|
||||||
return queryClient;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Readiness check
|
|
||||||
*/
|
|
||||||
export const readyz = (signal?: AbortSignal) => {
|
|
||||||
return GeneratedAPIInstance<Readyz200>({
|
|
||||||
url: `/api/v2/readyz`,
|
|
||||||
method: 'GET',
|
|
||||||
signal,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getReadyzQueryKey = () => {
|
|
||||||
return [`/api/v2/readyz`] as const;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getReadyzQueryOptions = <
|
|
||||||
TData = Awaited<ReturnType<typeof readyz>>,
|
|
||||||
TError = ErrorType<Readyz503>
|
|
||||||
>(options?: {
|
|
||||||
query?: UseQueryOptions<Awaited<ReturnType<typeof readyz>>, TError, TData>;
|
|
||||||
}) => {
|
|
||||||
const { query: queryOptions } = options ?? {};
|
|
||||||
|
|
||||||
const queryKey = queryOptions?.queryKey ?? getReadyzQueryKey();
|
|
||||||
|
|
||||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof readyz>>> = ({
|
|
||||||
signal,
|
|
||||||
}) => readyz(signal);
|
|
||||||
|
|
||||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof readyz>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
> & { queryKey: QueryKey };
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ReadyzQueryResult = NonNullable<Awaited<ReturnType<typeof readyz>>>;
|
|
||||||
export type ReadyzQueryError = ErrorType<Readyz503>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Readiness check
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function useReadyz<
|
|
||||||
TData = Awaited<ReturnType<typeof readyz>>,
|
|
||||||
TError = ErrorType<Readyz503>
|
|
||||||
>(options?: {
|
|
||||||
query?: UseQueryOptions<Awaited<ReturnType<typeof readyz>>, TError, TData>;
|
|
||||||
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
|
||||||
const queryOptions = getReadyzQueryOptions(options);
|
|
||||||
|
|
||||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
|
||||||
queryKey: QueryKey;
|
|
||||||
};
|
|
||||||
|
|
||||||
query.queryKey = queryOptions.queryKey;
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Readiness check
|
|
||||||
*/
|
|
||||||
export const invalidateReadyz = async (
|
|
||||||
queryClient: QueryClient,
|
|
||||||
options?: InvalidateOptions,
|
|
||||||
): Promise<QueryClient> => {
|
|
||||||
await queryClient.invalidateQueries(
|
|
||||||
{ queryKey: getReadyzQueryKey() },
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
return queryClient;
|
|
||||||
};
|
|
||||||
@@ -20,113 +20,11 @@ import { useMutation, useQuery } from 'react-query';
|
|||||||
import type { BodyType, ErrorType } from '../../../generatedAPIInstance';
|
import type { BodyType, ErrorType } from '../../../generatedAPIInstance';
|
||||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||||
import type {
|
import type {
|
||||||
HandleExportRawDataPOSTParams,
|
|
||||||
ListPromotedAndIndexedPaths200,
|
ListPromotedAndIndexedPaths200,
|
||||||
PromotetypesPromotePathDTO,
|
PromotetypesPromotePathDTO,
|
||||||
Querybuildertypesv5QueryRangeRequestDTO,
|
|
||||||
RenderErrorResponseDTO,
|
RenderErrorResponseDTO,
|
||||||
} from '../sigNoz.schemas';
|
} from '../sigNoz.schemas';
|
||||||
|
|
||||||
/**
|
|
||||||
* This endpoints allows complex query exporting raw data for traces and logs
|
|
||||||
* @summary Export raw data
|
|
||||||
*/
|
|
||||||
export const handleExportRawDataPOST = (
|
|
||||||
querybuildertypesv5QueryRangeRequestDTO: BodyType<Querybuildertypesv5QueryRangeRequestDTO>,
|
|
||||||
params?: HandleExportRawDataPOSTParams,
|
|
||||||
signal?: AbortSignal,
|
|
||||||
) => {
|
|
||||||
return GeneratedAPIInstance<string>({
|
|
||||||
url: `/api/v1/export_raw_data`,
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
data: querybuildertypesv5QueryRangeRequestDTO,
|
|
||||||
params,
|
|
||||||
signal,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getHandleExportRawDataPOSTMutationOptions = <
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>,
|
|
||||||
TContext = unknown
|
|
||||||
>(options?: {
|
|
||||||
mutation?: UseMutationOptions<
|
|
||||||
Awaited<ReturnType<typeof handleExportRawDataPOST>>,
|
|
||||||
TError,
|
|
||||||
{
|
|
||||||
data: BodyType<Querybuildertypesv5QueryRangeRequestDTO>;
|
|
||||||
params?: HandleExportRawDataPOSTParams;
|
|
||||||
},
|
|
||||||
TContext
|
|
||||||
>;
|
|
||||||
}): UseMutationOptions<
|
|
||||||
Awaited<ReturnType<typeof handleExportRawDataPOST>>,
|
|
||||||
TError,
|
|
||||||
{
|
|
||||||
data: BodyType<Querybuildertypesv5QueryRangeRequestDTO>;
|
|
||||||
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<ReturnType<typeof handleExportRawDataPOST>>,
|
|
||||||
{
|
|
||||||
data: BodyType<Querybuildertypesv5QueryRangeRequestDTO>;
|
|
||||||
params?: HandleExportRawDataPOSTParams;
|
|
||||||
}
|
|
||||||
> = (props) => {
|
|
||||||
const { data, params } = props ?? {};
|
|
||||||
|
|
||||||
return handleExportRawDataPOST(data, params);
|
|
||||||
};
|
|
||||||
|
|
||||||
return { mutationFn, ...mutationOptions };
|
|
||||||
};
|
|
||||||
|
|
||||||
export type HandleExportRawDataPOSTMutationResult = NonNullable<
|
|
||||||
Awaited<ReturnType<typeof handleExportRawDataPOST>>
|
|
||||||
>;
|
|
||||||
export type HandleExportRawDataPOSTMutationBody = BodyType<Querybuildertypesv5QueryRangeRequestDTO>;
|
|
||||||
export type HandleExportRawDataPOSTMutationError = ErrorType<RenderErrorResponseDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Export raw data
|
|
||||||
*/
|
|
||||||
export const useHandleExportRawDataPOST = <
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>,
|
|
||||||
TContext = unknown
|
|
||||||
>(options?: {
|
|
||||||
mutation?: UseMutationOptions<
|
|
||||||
Awaited<ReturnType<typeof handleExportRawDataPOST>>,
|
|
||||||
TError,
|
|
||||||
{
|
|
||||||
data: BodyType<Querybuildertypesv5QueryRangeRequestDTO>;
|
|
||||||
params?: HandleExportRawDataPOSTParams;
|
|
||||||
},
|
|
||||||
TContext
|
|
||||||
>;
|
|
||||||
}): UseMutationResult<
|
|
||||||
Awaited<ReturnType<typeof handleExportRawDataPOST>>,
|
|
||||||
TError,
|
|
||||||
{
|
|
||||||
data: BodyType<Querybuildertypesv5QueryRangeRequestDTO>;
|
|
||||||
params?: HandleExportRawDataPOSTParams;
|
|
||||||
},
|
|
||||||
TContext
|
|
||||||
> => {
|
|
||||||
const mutationOptions = getHandleExportRawDataPOSTMutationOptions(options);
|
|
||||||
|
|
||||||
return useMutation(mutationOptions);
|
|
||||||
};
|
|
||||||
/**
|
/**
|
||||||
* This endpoints promotes and indexes paths
|
* This endpoints promotes and indexes paths
|
||||||
* @summary Promote and index paths
|
* @summary Promote and index paths
|
||||||
|
|||||||
@@ -31,13 +31,10 @@ import type {
|
|||||||
GetMetricHighlightsPathParameters,
|
GetMetricHighlightsPathParameters,
|
||||||
GetMetricMetadata200,
|
GetMetricMetadata200,
|
||||||
GetMetricMetadataPathParameters,
|
GetMetricMetadataPathParameters,
|
||||||
GetMetricsOnboardingStatus200,
|
|
||||||
GetMetricsStats200,
|
GetMetricsStats200,
|
||||||
GetMetricsTreemap200,
|
GetMetricsTreemap200,
|
||||||
InspectMetrics200,
|
|
||||||
ListMetrics200,
|
ListMetrics200,
|
||||||
ListMetricsParams,
|
ListMetricsParams,
|
||||||
MetricsexplorertypesInspectMetricsRequestDTO,
|
|
||||||
MetricsexplorertypesStatsRequestDTO,
|
MetricsexplorertypesStatsRequestDTO,
|
||||||
MetricsexplorertypesTreemapRequestDTO,
|
MetricsexplorertypesTreemapRequestDTO,
|
||||||
MetricsexplorertypesUpdateMetricMetadataRequestDTO,
|
MetricsexplorertypesUpdateMetricMetadataRequestDTO,
|
||||||
@@ -781,176 +778,6 @@ export const useUpdateMetricMetadata = <
|
|||||||
|
|
||||||
return useMutation(mutationOptions);
|
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<MetricsexplorertypesInspectMetricsRequestDTO>,
|
|
||||||
signal?: AbortSignal,
|
|
||||||
) => {
|
|
||||||
return GeneratedAPIInstance<InspectMetrics200>({
|
|
||||||
url: `/api/v2/metrics/inspect`,
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
data: metricsexplorertypesInspectMetricsRequestDTO,
|
|
||||||
signal,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getInspectMetricsMutationOptions = <
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>,
|
|
||||||
TContext = unknown
|
|
||||||
>(options?: {
|
|
||||||
mutation?: UseMutationOptions<
|
|
||||||
Awaited<ReturnType<typeof inspectMetrics>>,
|
|
||||||
TError,
|
|
||||||
{ data: BodyType<MetricsexplorertypesInspectMetricsRequestDTO> },
|
|
||||||
TContext
|
|
||||||
>;
|
|
||||||
}): UseMutationOptions<
|
|
||||||
Awaited<ReturnType<typeof inspectMetrics>>,
|
|
||||||
TError,
|
|
||||||
{ data: BodyType<MetricsexplorertypesInspectMetricsRequestDTO> },
|
|
||||||
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<ReturnType<typeof inspectMetrics>>,
|
|
||||||
{ data: BodyType<MetricsexplorertypesInspectMetricsRequestDTO> }
|
|
||||||
> = (props) => {
|
|
||||||
const { data } = props ?? {};
|
|
||||||
|
|
||||||
return inspectMetrics(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
return { mutationFn, ...mutationOptions };
|
|
||||||
};
|
|
||||||
|
|
||||||
export type InspectMetricsMutationResult = NonNullable<
|
|
||||||
Awaited<ReturnType<typeof inspectMetrics>>
|
|
||||||
>;
|
|
||||||
export type InspectMetricsMutationBody = BodyType<MetricsexplorertypesInspectMetricsRequestDTO>;
|
|
||||||
export type InspectMetricsMutationError = ErrorType<RenderErrorResponseDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Inspect raw metric data points
|
|
||||||
*/
|
|
||||||
export const useInspectMetrics = <
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>,
|
|
||||||
TContext = unknown
|
|
||||||
>(options?: {
|
|
||||||
mutation?: UseMutationOptions<
|
|
||||||
Awaited<ReturnType<typeof inspectMetrics>>,
|
|
||||||
TError,
|
|
||||||
{ data: BodyType<MetricsexplorertypesInspectMetricsRequestDTO> },
|
|
||||||
TContext
|
|
||||||
>;
|
|
||||||
}): UseMutationResult<
|
|
||||||
Awaited<ReturnType<typeof inspectMetrics>>,
|
|
||||||
TError,
|
|
||||||
{ data: BodyType<MetricsexplorertypesInspectMetricsRequestDTO> },
|
|
||||||
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<GetMetricsOnboardingStatus200>({
|
|
||||||
url: `/api/v2/metrics/onboarding`,
|
|
||||||
method: 'GET',
|
|
||||||
signal,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getGetMetricsOnboardingStatusQueryKey = () => {
|
|
||||||
return [`/api/v2/metrics/onboarding`] as const;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getGetMetricsOnboardingStatusQueryOptions = <
|
|
||||||
TData = Awaited<ReturnType<typeof getMetricsOnboardingStatus>>,
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>
|
|
||||||
>(options?: {
|
|
||||||
query?: UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getMetricsOnboardingStatus>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
>;
|
|
||||||
}) => {
|
|
||||||
const { query: queryOptions } = options ?? {};
|
|
||||||
|
|
||||||
const queryKey =
|
|
||||||
queryOptions?.queryKey ?? getGetMetricsOnboardingStatusQueryKey();
|
|
||||||
|
|
||||||
const queryFn: QueryFunction<
|
|
||||||
Awaited<ReturnType<typeof getMetricsOnboardingStatus>>
|
|
||||||
> = ({ signal }) => getMetricsOnboardingStatus(signal);
|
|
||||||
|
|
||||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getMetricsOnboardingStatus>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
> & { queryKey: QueryKey };
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GetMetricsOnboardingStatusQueryResult = NonNullable<
|
|
||||||
Awaited<ReturnType<typeof getMetricsOnboardingStatus>>
|
|
||||||
>;
|
|
||||||
export type GetMetricsOnboardingStatusQueryError = ErrorType<RenderErrorResponseDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Check if non-SigNoz metrics have been received
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function useGetMetricsOnboardingStatus<
|
|
||||||
TData = Awaited<ReturnType<typeof getMetricsOnboardingStatus>>,
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>
|
|
||||||
>(options?: {
|
|
||||||
query?: UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getMetricsOnboardingStatus>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
>;
|
|
||||||
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
|
||||||
const queryOptions = getGetMetricsOnboardingStatusQueryOptions(options);
|
|
||||||
|
|
||||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
|
||||||
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<QueryClient> => {
|
|
||||||
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
|
* This endpoint provides list of metrics with their number of samples and timeseries for the given time range
|
||||||
* @summary Get metrics statistics
|
* @summary Get metrics statistics
|
||||||
|
|||||||
@@ -1,744 +0,0 @@
|
|||||||
/**
|
|
||||||
* ! 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<GetRuleHistoryFilterKeys200>({
|
|
||||||
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<ReturnType<typeof getRuleHistoryFilterKeys>>,
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>
|
|
||||||
>(
|
|
||||||
{ id }: GetRuleHistoryFilterKeysPathParameters,
|
|
||||||
params?: GetRuleHistoryFilterKeysParams,
|
|
||||||
options?: {
|
|
||||||
query?: UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
>;
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
const { query: queryOptions } = options ?? {};
|
|
||||||
|
|
||||||
const queryKey =
|
|
||||||
queryOptions?.queryKey ?? getGetRuleHistoryFilterKeysQueryKey({ id }, params);
|
|
||||||
|
|
||||||
const queryFn: QueryFunction<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>
|
|
||||||
> = ({ signal }) => getRuleHistoryFilterKeys({ id }, params, signal);
|
|
||||||
|
|
||||||
return {
|
|
||||||
queryKey,
|
|
||||||
queryFn,
|
|
||||||
enabled: !!id,
|
|
||||||
...queryOptions,
|
|
||||||
} as UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
> & { queryKey: QueryKey };
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GetRuleHistoryFilterKeysQueryResult = NonNullable<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>
|
|
||||||
>;
|
|
||||||
export type GetRuleHistoryFilterKeysQueryError = ErrorType<RenderErrorResponseDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Get rule history filter keys
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function useGetRuleHistoryFilterKeys<
|
|
||||||
TData = Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>,
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>
|
|
||||||
>(
|
|
||||||
{ id }: GetRuleHistoryFilterKeysPathParameters,
|
|
||||||
params?: GetRuleHistoryFilterKeysParams,
|
|
||||||
options?: {
|
|
||||||
query?: UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
>;
|
|
||||||
},
|
|
||||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
|
||||||
const queryOptions = getGetRuleHistoryFilterKeysQueryOptions(
|
|
||||||
{ id },
|
|
||||||
params,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
|
||||||
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<QueryClient> => {
|
|
||||||
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<GetRuleHistoryFilterValues200>({
|
|
||||||
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<ReturnType<typeof getRuleHistoryFilterValues>>,
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>
|
|
||||||
>(
|
|
||||||
{ id }: GetRuleHistoryFilterValuesPathParameters,
|
|
||||||
params?: GetRuleHistoryFilterValuesParams,
|
|
||||||
options?: {
|
|
||||||
query?: UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryFilterValues>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
>;
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
const { query: queryOptions } = options ?? {};
|
|
||||||
|
|
||||||
const queryKey =
|
|
||||||
queryOptions?.queryKey ??
|
|
||||||
getGetRuleHistoryFilterValuesQueryKey({ id }, params);
|
|
||||||
|
|
||||||
const queryFn: QueryFunction<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryFilterValues>>
|
|
||||||
> = ({ signal }) => getRuleHistoryFilterValues({ id }, params, signal);
|
|
||||||
|
|
||||||
return {
|
|
||||||
queryKey,
|
|
||||||
queryFn,
|
|
||||||
enabled: !!id,
|
|
||||||
...queryOptions,
|
|
||||||
} as UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryFilterValues>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
> & { queryKey: QueryKey };
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GetRuleHistoryFilterValuesQueryResult = NonNullable<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryFilterValues>>
|
|
||||||
>;
|
|
||||||
export type GetRuleHistoryFilterValuesQueryError = ErrorType<RenderErrorResponseDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Get rule history filter values
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function useGetRuleHistoryFilterValues<
|
|
||||||
TData = Awaited<ReturnType<typeof getRuleHistoryFilterValues>>,
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>
|
|
||||||
>(
|
|
||||||
{ id }: GetRuleHistoryFilterValuesPathParameters,
|
|
||||||
params?: GetRuleHistoryFilterValuesParams,
|
|
||||||
options?: {
|
|
||||||
query?: UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryFilterValues>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
>;
|
|
||||||
},
|
|
||||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
|
||||||
const queryOptions = getGetRuleHistoryFilterValuesQueryOptions(
|
|
||||||
{ id },
|
|
||||||
params,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
|
||||||
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<QueryClient> => {
|
|
||||||
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<GetRuleHistoryOverallStatus200>({
|
|
||||||
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<ReturnType<typeof getRuleHistoryOverallStatus>>,
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>
|
|
||||||
>(
|
|
||||||
{ id }: GetRuleHistoryOverallStatusPathParameters,
|
|
||||||
params: GetRuleHistoryOverallStatusParams,
|
|
||||||
options?: {
|
|
||||||
query?: UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
>;
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
const { query: queryOptions } = options ?? {};
|
|
||||||
|
|
||||||
const queryKey =
|
|
||||||
queryOptions?.queryKey ??
|
|
||||||
getGetRuleHistoryOverallStatusQueryKey({ id }, params);
|
|
||||||
|
|
||||||
const queryFn: QueryFunction<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>
|
|
||||||
> = ({ signal }) => getRuleHistoryOverallStatus({ id }, params, signal);
|
|
||||||
|
|
||||||
return {
|
|
||||||
queryKey,
|
|
||||||
queryFn,
|
|
||||||
enabled: !!id,
|
|
||||||
...queryOptions,
|
|
||||||
} as UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
> & { queryKey: QueryKey };
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GetRuleHistoryOverallStatusQueryResult = NonNullable<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>
|
|
||||||
>;
|
|
||||||
export type GetRuleHistoryOverallStatusQueryError = ErrorType<RenderErrorResponseDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Get rule overall status timeline
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function useGetRuleHistoryOverallStatus<
|
|
||||||
TData = Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>,
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>
|
|
||||||
>(
|
|
||||||
{ id }: GetRuleHistoryOverallStatusPathParameters,
|
|
||||||
params: GetRuleHistoryOverallStatusParams,
|
|
||||||
options?: {
|
|
||||||
query?: UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
>;
|
|
||||||
},
|
|
||||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
|
||||||
const queryOptions = getGetRuleHistoryOverallStatusQueryOptions(
|
|
||||||
{ id },
|
|
||||||
params,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
|
||||||
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<QueryClient> => {
|
|
||||||
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<GetRuleHistoryStats200>({
|
|
||||||
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<ReturnType<typeof getRuleHistoryStats>>,
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>
|
|
||||||
>(
|
|
||||||
{ id }: GetRuleHistoryStatsPathParameters,
|
|
||||||
params: GetRuleHistoryStatsParams,
|
|
||||||
options?: {
|
|
||||||
query?: UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryStats>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
>;
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
const { query: queryOptions } = options ?? {};
|
|
||||||
|
|
||||||
const queryKey =
|
|
||||||
queryOptions?.queryKey ?? getGetRuleHistoryStatsQueryKey({ id }, params);
|
|
||||||
|
|
||||||
const queryFn: QueryFunction<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryStats>>
|
|
||||||
> = ({ signal }) => getRuleHistoryStats({ id }, params, signal);
|
|
||||||
|
|
||||||
return {
|
|
||||||
queryKey,
|
|
||||||
queryFn,
|
|
||||||
enabled: !!id,
|
|
||||||
...queryOptions,
|
|
||||||
} as UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryStats>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
> & { queryKey: QueryKey };
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GetRuleHistoryStatsQueryResult = NonNullable<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryStats>>
|
|
||||||
>;
|
|
||||||
export type GetRuleHistoryStatsQueryError = ErrorType<RenderErrorResponseDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Get rule history stats
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function useGetRuleHistoryStats<
|
|
||||||
TData = Awaited<ReturnType<typeof getRuleHistoryStats>>,
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>
|
|
||||||
>(
|
|
||||||
{ id }: GetRuleHistoryStatsPathParameters,
|
|
||||||
params: GetRuleHistoryStatsParams,
|
|
||||||
options?: {
|
|
||||||
query?: UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryStats>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
>;
|
|
||||||
},
|
|
||||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
|
||||||
const queryOptions = getGetRuleHistoryStatsQueryOptions(
|
|
||||||
{ id },
|
|
||||||
params,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
|
||||||
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<QueryClient> => {
|
|
||||||
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<GetRuleHistoryTimeline200>({
|
|
||||||
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<ReturnType<typeof getRuleHistoryTimeline>>,
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>
|
|
||||||
>(
|
|
||||||
{ id }: GetRuleHistoryTimelinePathParameters,
|
|
||||||
params: GetRuleHistoryTimelineParams,
|
|
||||||
options?: {
|
|
||||||
query?: UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryTimeline>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
>;
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
const { query: queryOptions } = options ?? {};
|
|
||||||
|
|
||||||
const queryKey =
|
|
||||||
queryOptions?.queryKey ?? getGetRuleHistoryTimelineQueryKey({ id }, params);
|
|
||||||
|
|
||||||
const queryFn: QueryFunction<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryTimeline>>
|
|
||||||
> = ({ signal }) => getRuleHistoryTimeline({ id }, params, signal);
|
|
||||||
|
|
||||||
return {
|
|
||||||
queryKey,
|
|
||||||
queryFn,
|
|
||||||
enabled: !!id,
|
|
||||||
...queryOptions,
|
|
||||||
} as UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryTimeline>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
> & { queryKey: QueryKey };
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GetRuleHistoryTimelineQueryResult = NonNullable<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryTimeline>>
|
|
||||||
>;
|
|
||||||
export type GetRuleHistoryTimelineQueryError = ErrorType<RenderErrorResponseDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Get rule history timeline
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function useGetRuleHistoryTimeline<
|
|
||||||
TData = Awaited<ReturnType<typeof getRuleHistoryTimeline>>,
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>
|
|
||||||
>(
|
|
||||||
{ id }: GetRuleHistoryTimelinePathParameters,
|
|
||||||
params: GetRuleHistoryTimelineParams,
|
|
||||||
options?: {
|
|
||||||
query?: UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryTimeline>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
>;
|
|
||||||
},
|
|
||||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
|
||||||
const queryOptions = getGetRuleHistoryTimelineQueryOptions(
|
|
||||||
{ id },
|
|
||||||
params,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
|
||||||
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<QueryClient> => {
|
|
||||||
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<GetRuleHistoryTopContributors200>({
|
|
||||||
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<ReturnType<typeof getRuleHistoryTopContributors>>,
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>
|
|
||||||
>(
|
|
||||||
{ id }: GetRuleHistoryTopContributorsPathParameters,
|
|
||||||
params: GetRuleHistoryTopContributorsParams,
|
|
||||||
options?: {
|
|
||||||
query?: UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryTopContributors>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
>;
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
const { query: queryOptions } = options ?? {};
|
|
||||||
|
|
||||||
const queryKey =
|
|
||||||
queryOptions?.queryKey ??
|
|
||||||
getGetRuleHistoryTopContributorsQueryKey({ id }, params);
|
|
||||||
|
|
||||||
const queryFn: QueryFunction<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryTopContributors>>
|
|
||||||
> = ({ signal }) => getRuleHistoryTopContributors({ id }, params, signal);
|
|
||||||
|
|
||||||
return {
|
|
||||||
queryKey,
|
|
||||||
queryFn,
|
|
||||||
enabled: !!id,
|
|
||||||
...queryOptions,
|
|
||||||
} as UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryTopContributors>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
> & { queryKey: QueryKey };
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GetRuleHistoryTopContributorsQueryResult = NonNullable<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryTopContributors>>
|
|
||||||
>;
|
|
||||||
export type GetRuleHistoryTopContributorsQueryError = ErrorType<RenderErrorResponseDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Get top contributors to rule firing
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function useGetRuleHistoryTopContributors<
|
|
||||||
TData = Awaited<ReturnType<typeof getRuleHistoryTopContributors>>,
|
|
||||||
TError = ErrorType<RenderErrorResponseDTO>
|
|
||||||
>(
|
|
||||||
{ id }: GetRuleHistoryTopContributorsPathParameters,
|
|
||||||
params: GetRuleHistoryTopContributorsParams,
|
|
||||||
options?: {
|
|
||||||
query?: UseQueryOptions<
|
|
||||||
Awaited<ReturnType<typeof getRuleHistoryTopContributors>>,
|
|
||||||
TError,
|
|
||||||
TData
|
|
||||||
>;
|
|
||||||
},
|
|
||||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
|
||||||
const queryOptions = getGetRuleHistoryTopContributorsQueryOptions(
|
|
||||||
{ id },
|
|
||||||
params,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
|
||||||
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<QueryClient> => {
|
|
||||||
await queryClient.invalidateQueries(
|
|
||||||
{ queryKey: getGetRuleHistoryTopContributorsQueryKey({ id }, params) },
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
return queryClient;
|
|
||||||
};
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
54
frontend/src/api/metricsExplorer/getInspectMetricsDetails.ts
Normal file
54
frontend/src/api/metricsExplorer/getInspectMetricsDetails.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
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<string, string>;
|
||||||
|
labelsArray: Array<Record<string, string>>;
|
||||||
|
values: InspectMetricsTimestampValue[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InspectMetricsTimestampValue {
|
||||||
|
timestamp: number;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getInspectMetricsDetails = async (
|
||||||
|
request: InspectMetricsRequest,
|
||||||
|
signal?: AbortSignal,
|
||||||
|
headers?: Record<string, string>,
|
||||||
|
): Promise<SuccessResponse<InspectMetricsResponse> | 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
75
frontend/src/api/metricsExplorer/getMetricDetails.ts
Normal file
75
frontend/src/api/metricsExplorer/getMetricDetails.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
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<string, string>,
|
||||||
|
): Promise<SuccessResponse<MetricDetailsResponse> | 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
67
frontend/src/api/metricsExplorer/getMetricsList.ts
Normal file
67
frontend/src/api/metricsExplorer/getMetricsList.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
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<string, string>,
|
||||||
|
): Promise<SuccessResponse<MetricsListResponse> | 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
44
frontend/src/api/metricsExplorer/getMetricsListFilterKeys.ts
Normal file
44
frontend/src/api/metricsExplorer/getMetricsListFilterKeys.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
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<string, string>,
|
||||||
|
): Promise<SuccessResponse<MetricsListFilterKeysResponse> | 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
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<string, string>,
|
||||||
|
): Promise<
|
||||||
|
SuccessResponse<MetricsListFilterValuesResponse> | 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
60
frontend/src/api/metricsExplorer/getRelatedMetrics.ts
Normal file
60
frontend/src/api/metricsExplorer/getRelatedMetrics.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
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<string, string>,
|
||||||
|
): Promise<SuccessResponse<RelatedMetricsResponse> | 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -7,7 +7,7 @@ import ROUTES from 'constants/routes';
|
|||||||
import useUpdatedQuery from 'container/GridCardLayout/useResolveQuery';
|
import useUpdatedQuery from 'container/GridCardLayout/useResolveQuery';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import { useDashboardStore } from 'providers/Dashboard/store/useDashboardStore';
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { Query, TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
import { Query, TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { DataSource, MetricAggregateOperator } from 'types/common/queryBuilder';
|
import { DataSource, MetricAggregateOperator } from 'types/common/queryBuilder';
|
||||||
@@ -79,7 +79,7 @@ export function useNavigateToExplorer(): (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { getUpdatedQuery } = useUpdatedQuery();
|
const { getUpdatedQuery } = useUpdatedQuery();
|
||||||
const { selectedDashboard } = useDashboardStore();
|
const { selectedDashboard } = useDashboard();
|
||||||
const { notifications } = useNotifications();
|
const { notifications } = useNotifications();
|
||||||
|
|
||||||
return useCallback(
|
return useCallback(
|
||||||
|
|||||||
@@ -116,12 +116,7 @@ describe.each([
|
|||||||
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
||||||
expect(screen.getByText('FORMAT')).toBeInTheDocument();
|
expect(screen.getByText('FORMAT')).toBeInTheDocument();
|
||||||
expect(screen.getByText('Number of Rows')).toBeInTheDocument();
|
expect(screen.getByText('Number of Rows')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Columns')).toBeInTheDocument();
|
||||||
if (dataSource === DataSource.TRACES) {
|
|
||||||
expect(screen.queryByText('Columns')).not.toBeInTheDocument();
|
|
||||||
} else {
|
|
||||||
expect(screen.getByText('Columns')).toBeInTheDocument();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows changing export format', () => {
|
it('allows changing export format', () => {
|
||||||
@@ -151,17 +146,6 @@ describe.each([
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('allows changing columns scope', () => {
|
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);
|
renderWithStore(dataSource);
|
||||||
fireEvent.click(screen.getByTestId(testId));
|
fireEvent.click(screen.getByTestId(testId));
|
||||||
|
|
||||||
@@ -226,12 +210,7 @@ describe.each([
|
|||||||
mockUseQueryBuilder.mockReturnValue({ stagedQuery: mockQuery });
|
mockUseQueryBuilder.mockReturnValue({ stagedQuery: mockQuery });
|
||||||
renderWithStore(dataSource);
|
renderWithStore(dataSource);
|
||||||
fireEvent.click(screen.getByTestId(testId));
|
fireEvent.click(screen.getByTestId(testId));
|
||||||
|
fireEvent.click(screen.getByRole('radio', { name: 'Selected' }));
|
||||||
// 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'));
|
fireEvent.click(screen.getByText('Export'));
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
@@ -248,11 +227,6 @@ describe.each([
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('sends no selectFields when column scope is All', async () => {
|
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);
|
renderWithStore(dataSource);
|
||||||
fireEvent.click(screen.getByTestId(testId));
|
fireEvent.click(screen.getByTestId(testId));
|
||||||
fireEvent.click(screen.getByRole('radio', { name: 'All' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'All' }));
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { Button, Popover, Radio, Tooltip, Typography } from 'antd';
|
import { Button, Popover, Radio, Tooltip, Typography } from 'antd';
|
||||||
import { TelemetryFieldKey } from 'api/v5/v5';
|
|
||||||
import { useExportRawData } from 'hooks/useDownloadOptionsMenu/useDownloadOptionsMenu';
|
import { useExportRawData } from 'hooks/useDownloadOptionsMenu/useDownloadOptionsMenu';
|
||||||
import { Download, DownloadIcon, Loader2 } from 'lucide-react';
|
import { Download, DownloadIcon, Loader2 } from 'lucide-react';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
@@ -15,12 +14,10 @@ import './DownloadOptionsMenu.styles.scss';
|
|||||||
|
|
||||||
interface DownloadOptionsMenuProps {
|
interface DownloadOptionsMenuProps {
|
||||||
dataSource: DataSource;
|
dataSource: DataSource;
|
||||||
selectedColumns?: TelemetryFieldKey[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DownloadOptionsMenu({
|
export default function DownloadOptionsMenu({
|
||||||
dataSource,
|
dataSource,
|
||||||
selectedColumns,
|
|
||||||
}: DownloadOptionsMenuProps): JSX.Element {
|
}: DownloadOptionsMenuProps): JSX.Element {
|
||||||
const [exportFormat, setExportFormat] = useState<string>(DownloadFormats.CSV);
|
const [exportFormat, setExportFormat] = useState<string>(DownloadFormats.CSV);
|
||||||
const [rowLimit, setRowLimit] = useState<number>(DownloadRowCounts.TEN_K);
|
const [rowLimit, setRowLimit] = useState<number>(DownloadRowCounts.TEN_K);
|
||||||
@@ -38,19 +35,9 @@ export default function DownloadOptionsMenu({
|
|||||||
await handleExportRawData({
|
await handleExportRawData({
|
||||||
format: exportFormat,
|
format: exportFormat,
|
||||||
rowLimit,
|
rowLimit,
|
||||||
clearSelectColumns:
|
clearSelectColumns: columnsScope === DownloadColumnsScopes.ALL,
|
||||||
dataSource !== DataSource.TRACES &&
|
|
||||||
columnsScope === DownloadColumnsScopes.ALL,
|
|
||||||
selectedColumns,
|
|
||||||
});
|
});
|
||||||
}, [
|
}, [exportFormat, rowLimit, columnsScope, handleExportRawData]);
|
||||||
exportFormat,
|
|
||||||
rowLimit,
|
|
||||||
columnsScope,
|
|
||||||
selectedColumns,
|
|
||||||
handleExportRawData,
|
|
||||||
dataSource,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const popoverContent = useMemo(
|
const popoverContent = useMemo(
|
||||||
() => (
|
() => (
|
||||||
@@ -85,22 +72,18 @@ export default function DownloadOptionsMenu({
|
|||||||
</Radio.Group>
|
</Radio.Group>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{dataSource !== DataSource.TRACES && (
|
<div className="horizontal-line" />
|
||||||
<>
|
|
||||||
<div className="horizontal-line" />
|
|
||||||
|
|
||||||
<div className="columns-scope">
|
<div className="columns-scope">
|
||||||
<Typography.Text className="title">Columns</Typography.Text>
|
<Typography.Text className="title">Columns</Typography.Text>
|
||||||
<Radio.Group
|
<Radio.Group
|
||||||
value={columnsScope}
|
value={columnsScope}
|
||||||
onChange={(e): void => setColumnsScope(e.target.value)}
|
onChange={(e): void => setColumnsScope(e.target.value)}
|
||||||
>
|
>
|
||||||
<Radio value={DownloadColumnsScopes.ALL}>All</Radio>
|
<Radio value={DownloadColumnsScopes.ALL}>All</Radio>
|
||||||
<Radio value={DownloadColumnsScopes.SELECTED}>Selected</Radio>
|
<Radio value={DownloadColumnsScopes.SELECTED}>Selected</Radio>
|
||||||
</Radio.Group>
|
</Radio.Group>
|
||||||
</div>
|
</div>
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
@@ -114,14 +97,7 @@ export default function DownloadOptionsMenu({
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
[
|
[exportFormat, rowLimit, columnsScope, isDownloading, handleExport],
|
||||||
exportFormat,
|
|
||||||
rowLimit,
|
|
||||||
columnsScope,
|
|
||||||
isDownloading,
|
|
||||||
handleExport,
|
|
||||||
dataSource,
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { useCopyToClipboard } from 'react-use';
|
|
||||||
import { Badge } from '@signozhq/badge';
|
import { Badge } from '@signozhq/badge';
|
||||||
import { Button } from '@signozhq/button';
|
import { Button } from '@signozhq/button';
|
||||||
import { DialogFooter, DialogWrapper } from '@signozhq/dialog';
|
import { DialogFooter, DialogWrapper } from '@signozhq/dialog';
|
||||||
@@ -21,7 +20,7 @@ import { RenderErrorResponseDTO } from 'api/generated/services/sigNoz.schemas';
|
|||||||
import {
|
import {
|
||||||
getResetPasswordToken,
|
getResetPasswordToken,
|
||||||
useDeleteUser,
|
useDeleteUser,
|
||||||
useUpdateUserDeprecated,
|
useUpdateUser,
|
||||||
} from 'api/generated/services/users';
|
} from 'api/generated/services/users';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { MemberRow } from 'components/MembersTable/MembersTable';
|
import { MemberRow } from 'components/MembersTable/MembersTable';
|
||||||
@@ -61,7 +60,7 @@ function EditMemberDrawer({
|
|||||||
|
|
||||||
const isInvited = member?.status === MemberStatus.Invited;
|
const isInvited = member?.status === MemberStatus.Invited;
|
||||||
|
|
||||||
const { mutate: updateUser, isLoading: isSaving } = useUpdateUserDeprecated({
|
const { mutate: updateUser, isLoading: isSaving } = useUpdateUser({
|
||||||
mutation: {
|
mutation: {
|
||||||
onSuccess: (): void => {
|
onSuccess: (): void => {
|
||||||
toast.success('Member details updated successfully', { richColors: true });
|
toast.success('Member details updated successfully', { richColors: true });
|
||||||
@@ -178,30 +177,26 @@ function EditMemberDrawer({
|
|||||||
}
|
}
|
||||||
}, [member, isInvited, setLinkType, onClose]);
|
}, [member, isInvited, setLinkType, onClose]);
|
||||||
|
|
||||||
const [copyState, copyToClipboard] = useCopyToClipboard();
|
|
||||||
const handleCopyResetLink = useCallback(async (): Promise<void> => {
|
const handleCopyResetLink = useCallback(async (): Promise<void> => {
|
||||||
if (!resetLink) {
|
if (!resetLink) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
copyToClipboard(resetLink);
|
try {
|
||||||
|
await navigator.clipboard.writeText(resetLink);
|
||||||
setHasCopiedResetLink(true);
|
setHasCopiedResetLink(true);
|
||||||
setTimeout(() => setHasCopiedResetLink(false), 2000);
|
setTimeout(() => setHasCopiedResetLink(false), 2000);
|
||||||
toast.success(
|
toast.success(
|
||||||
linkType === 'invite'
|
linkType === 'invite'
|
||||||
? 'Invite link copied to clipboard'
|
? 'Invite link copied to clipboard'
|
||||||
: 'Reset link copied to clipboard',
|
: 'Reset link copied to clipboard',
|
||||||
{ richColors: true },
|
{ richColors: true },
|
||||||
);
|
);
|
||||||
}, [resetLink, copyToClipboard, linkType]);
|
} catch {
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (copyState.error) {
|
|
||||||
toast.error('Failed to copy link', {
|
toast.error('Failed to copy link', {
|
||||||
richColors: true,
|
richColors: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [copyState.error]);
|
}, [resetLink, linkType]);
|
||||||
|
|
||||||
const handleClose = useCallback((): void => {
|
const handleClose = useCallback((): void => {
|
||||||
setShowDeleteConfirm(false);
|
setShowDeleteConfirm(false);
|
||||||
|
|||||||
@@ -4,10 +4,16 @@ import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
|||||||
import {
|
import {
|
||||||
getResetPasswordToken,
|
getResetPasswordToken,
|
||||||
useDeleteUser,
|
useDeleteUser,
|
||||||
useUpdateUserDeprecated,
|
useUpdateUser,
|
||||||
} from 'api/generated/services/users';
|
} from 'api/generated/services/users';
|
||||||
import { MemberStatus } from 'container/MembersSettings/utils';
|
import { MemberStatus } from 'container/MembersSettings/utils';
|
||||||
import { render, screen, userEvent, waitFor } from 'tests/test-utils';
|
import {
|
||||||
|
fireEvent,
|
||||||
|
render,
|
||||||
|
screen,
|
||||||
|
userEvent,
|
||||||
|
waitFor,
|
||||||
|
} from 'tests/test-utils';
|
||||||
import { ROLES } from 'types/roles';
|
import { ROLES } from 'types/roles';
|
||||||
|
|
||||||
import EditMemberDrawer, { EditMemberDrawerProps } from '../EditMemberDrawer';
|
import EditMemberDrawer, { EditMemberDrawerProps } from '../EditMemberDrawer';
|
||||||
@@ -44,7 +50,7 @@ jest.mock('@signozhq/dialog', () => ({
|
|||||||
|
|
||||||
jest.mock('api/generated/services/users', () => ({
|
jest.mock('api/generated/services/users', () => ({
|
||||||
useDeleteUser: jest.fn(),
|
useDeleteUser: jest.fn(),
|
||||||
useUpdateUserDeprecated: jest.fn(),
|
useUpdateUser: jest.fn(),
|
||||||
getResetPasswordToken: jest.fn(),
|
getResetPasswordToken: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -59,16 +65,6 @@ jest.mock('@signozhq/sonner', () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const mockCopyToClipboard = jest.fn();
|
|
||||||
const mockCopyState = { value: undefined, error: undefined };
|
|
||||||
|
|
||||||
jest.mock('react-use', () => ({
|
|
||||||
useCopyToClipboard: (): [typeof mockCopyState, typeof mockCopyToClipboard] => [
|
|
||||||
mockCopyState,
|
|
||||||
mockCopyToClipboard,
|
|
||||||
],
|
|
||||||
}));
|
|
||||||
|
|
||||||
const mockUpdateMutate = jest.fn();
|
const mockUpdateMutate = jest.fn();
|
||||||
const mockDeleteMutate = jest.fn();
|
const mockDeleteMutate = jest.fn();
|
||||||
const mockGetResetPasswordToken = jest.mocked(getResetPasswordToken);
|
const mockGetResetPasswordToken = jest.mocked(getResetPasswordToken);
|
||||||
@@ -109,7 +105,7 @@ function renderDrawer(
|
|||||||
describe('EditMemberDrawer', () => {
|
describe('EditMemberDrawer', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
(useUpdateUserDeprecated as jest.Mock).mockReturnValue({
|
(useUpdateUser as jest.Mock).mockReturnValue({
|
||||||
mutate: mockUpdateMutate,
|
mutate: mockUpdateMutate,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
});
|
});
|
||||||
@@ -134,7 +130,7 @@ describe('EditMemberDrawer', () => {
|
|||||||
const onComplete = jest.fn();
|
const onComplete = jest.fn();
|
||||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||||
|
|
||||||
(useUpdateUserDeprecated as jest.Mock).mockImplementation((options) => ({
|
(useUpdateUser as jest.Mock).mockImplementation((options) => ({
|
||||||
mutate: mockUpdateMutate.mockImplementation(() => {
|
mutate: mockUpdateMutate.mockImplementation(() => {
|
||||||
options?.mutation?.onSuccess?.();
|
options?.mutation?.onSuccess?.();
|
||||||
}),
|
}),
|
||||||
@@ -243,7 +239,7 @@ describe('EditMemberDrawer', () => {
|
|||||||
const onComplete = jest.fn();
|
const onComplete = jest.fn();
|
||||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||||
|
|
||||||
(useUpdateUserDeprecated as jest.Mock).mockImplementation((options) => ({
|
(useUpdateUser as jest.Mock).mockImplementation((options) => ({
|
||||||
mutate: mockUpdateMutate.mockImplementation(() => {
|
mutate: mockUpdateMutate.mockImplementation(() => {
|
||||||
options?.mutation?.onSuccess?.();
|
options?.mutation?.onSuccess?.();
|
||||||
}),
|
}),
|
||||||
@@ -284,7 +280,7 @@ describe('EditMemberDrawer', () => {
|
|||||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||||
const mockToast = jest.mocked(toast);
|
const mockToast = jest.mocked(toast);
|
||||||
|
|
||||||
(useUpdateUserDeprecated as jest.Mock).mockImplementation((options) => ({
|
(useUpdateUser as jest.Mock).mockImplementation((options) => ({
|
||||||
mutate: mockUpdateMutate.mockImplementation(() => {
|
mutate: mockUpdateMutate.mockImplementation(() => {
|
||||||
options?.mutation?.onError?.({});
|
options?.mutation?.onError?.({});
|
||||||
}),
|
}),
|
||||||
@@ -365,14 +361,32 @@ describe('EditMemberDrawer', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Generate Password Reset Link', () => {
|
describe('Generate Password Reset Link', () => {
|
||||||
|
const mockWriteText = jest.fn().mockResolvedValue(undefined);
|
||||||
|
let clipboardSpy: jest.SpyInstance | undefined;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
Object.defineProperty(navigator, 'clipboard', {
|
||||||
|
value: { writeText: (): Promise<void> => Promise.resolve() },
|
||||||
|
configurable: true,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockCopyToClipboard.mockClear();
|
mockWriteText.mockClear();
|
||||||
|
clipboardSpy = jest
|
||||||
|
.spyOn(navigator.clipboard, 'writeText')
|
||||||
|
.mockImplementation(mockWriteText);
|
||||||
mockGetResetPasswordToken.mockResolvedValue({
|
mockGetResetPasswordToken.mockResolvedValue({
|
||||||
status: 'success',
|
status: 'success',
|
||||||
data: { token: 'reset-tok-abc', id: 'user-1' },
|
data: { token: 'reset-tok-abc', id: 'user-1' },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
clipboardSpy?.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
it('calls getResetPasswordToken and opens the reset link dialog with the generated link', async () => {
|
it('calls getResetPasswordToken and opens the reset link dialog with the generated link', async () => {
|
||||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||||
|
|
||||||
@@ -407,7 +421,7 @@ describe('EditMemberDrawer', () => {
|
|||||||
});
|
});
|
||||||
expect(dialog).toHaveTextContent('reset-tok-abc');
|
expect(dialog).toHaveTextContent('reset-tok-abc');
|
||||||
|
|
||||||
await user.click(screen.getByRole('button', { name: /^copy$/i }));
|
fireEvent.click(screen.getByRole('button', { name: /^copy$/i }));
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(mockToast.success).toHaveBeenCalledWith(
|
expect(mockToast.success).toHaveBeenCalledWith(
|
||||||
@@ -416,7 +430,7 @@ describe('EditMemberDrawer', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(mockCopyToClipboard).toHaveBeenCalledWith(
|
expect(mockWriteText).toHaveBeenCalledWith(
|
||||||
expect.stringContaining('reset-tok-abc'),
|
expect.stringContaining('reset-tok-abc'),
|
||||||
);
|
);
|
||||||
expect(screen.getByRole('button', { name: /copied!/i })).toBeInTheDocument();
|
expect(screen.getByRole('button', { name: /copied!/i })).toBeInTheDocument();
|
||||||
|
|||||||
@@ -202,8 +202,19 @@ function InviteMembersModal({
|
|||||||
onComplete?.();
|
onComplete?.();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const apiErr = err as APIError;
|
const apiErr = err as APIError;
|
||||||
const errorMessage = apiErr?.getErrorMessage?.() ?? 'An error occurred';
|
if (apiErr?.getHttpStatusCode() === 409) {
|
||||||
toast.error(errorMessage, { richColors: true });
|
toast.error(
|
||||||
|
touchedRows.length === 1
|
||||||
|
? `${touchedRows[0].email} is already a member`
|
||||||
|
: 'Invite for one or more users already exists',
|
||||||
|
{ richColors: true },
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const errorMessage = apiErr?.getErrorMessage?.() ?? 'An error occurred';
|
||||||
|
toast.error(`Failed to send invites: ${errorMessage}`, {
|
||||||
|
richColors: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,9 @@
|
|||||||
import { toast } from '@signozhq/sonner';
|
|
||||||
import inviteUsers from 'api/v1/invite/bulk/create';
|
import inviteUsers from 'api/v1/invite/bulk/create';
|
||||||
import sendInvite from 'api/v1/invite/create';
|
import sendInvite from 'api/v1/invite/create';
|
||||||
import { StatusCodes } from 'http-status-codes';
|
|
||||||
import { render, screen, userEvent, waitFor } from 'tests/test-utils';
|
import { render, screen, userEvent, waitFor } from 'tests/test-utils';
|
||||||
import APIError from 'types/api/error';
|
|
||||||
|
|
||||||
import InviteMembersModal from '../InviteMembersModal';
|
import InviteMembersModal from '../InviteMembersModal';
|
||||||
|
|
||||||
const makeApiError = (message: string, code = StatusCodes.CONFLICT): APIError =>
|
|
||||||
new APIError({
|
|
||||||
httpStatusCode: code,
|
|
||||||
error: { code: 'already_exists', message, url: '', errors: [] },
|
|
||||||
});
|
|
||||||
|
|
||||||
jest.mock('api/v1/invite/create');
|
jest.mock('api/v1/invite/create');
|
||||||
jest.mock('api/v1/invite/bulk/create');
|
jest.mock('api/v1/invite/bulk/create');
|
||||||
jest.mock('@signozhq/sonner', () => ({
|
jest.mock('@signozhq/sonner', () => ({
|
||||||
@@ -151,90 +142,6 @@ describe('InviteMembersModal', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('error handling', () => {
|
|
||||||
it('shows BE message on single invite 409', async () => {
|
|
||||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
|
||||||
mockSendInvite.mockRejectedValue(
|
|
||||||
makeApiError('An invite already exists for this email: single@signoz.io'),
|
|
||||||
);
|
|
||||||
|
|
||||||
render(<InviteMembersModal {...defaultProps} />);
|
|
||||||
|
|
||||||
await user.type(
|
|
||||||
screen.getAllByPlaceholderText('john@signoz.io')[0],
|
|
||||||
'single@signoz.io',
|
|
||||||
);
|
|
||||||
await user.click(screen.getAllByText('Select roles')[0]);
|
|
||||||
await user.click(await screen.findByText('Viewer'));
|
|
||||||
await user.click(
|
|
||||||
screen.getByRole('button', { name: /invite team members/i }),
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(toast.error).toHaveBeenCalledWith(
|
|
||||||
'An invite already exists for this email: single@signoz.io',
|
|
||||||
expect.anything(),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('shows BE message on bulk invite 409', async () => {
|
|
||||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
|
||||||
mockInviteUsers.mockRejectedValue(
|
|
||||||
makeApiError('An invite already exists for this email: alice@signoz.io'),
|
|
||||||
);
|
|
||||||
|
|
||||||
render(<InviteMembersModal {...defaultProps} />);
|
|
||||||
|
|
||||||
const emailInputs = screen.getAllByPlaceholderText('john@signoz.io');
|
|
||||||
await user.type(emailInputs[0], 'alice@signoz.io');
|
|
||||||
await user.click(screen.getAllByText('Select roles')[0]);
|
|
||||||
await user.click(await screen.findByText('Viewer'));
|
|
||||||
|
|
||||||
await user.type(emailInputs[1], 'bob@signoz.io');
|
|
||||||
await user.click(screen.getAllByText('Select roles')[0]);
|
|
||||||
const editorOptions = await screen.findAllByText('Editor');
|
|
||||||
await user.click(editorOptions[editorOptions.length - 1]);
|
|
||||||
|
|
||||||
await user.click(
|
|
||||||
screen.getByRole('button', { name: /invite team members/i }),
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(toast.error).toHaveBeenCalledWith(
|
|
||||||
'An invite already exists for this email: alice@signoz.io',
|
|
||||||
expect.anything(),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('shows BE message on generic error', async () => {
|
|
||||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
|
||||||
mockSendInvite.mockRejectedValue(
|
|
||||||
makeApiError('Internal server error', StatusCodes.INTERNAL_SERVER_ERROR),
|
|
||||||
);
|
|
||||||
|
|
||||||
render(<InviteMembersModal {...defaultProps} />);
|
|
||||||
|
|
||||||
await user.type(
|
|
||||||
screen.getAllByPlaceholderText('john@signoz.io')[0],
|
|
||||||
'single@signoz.io',
|
|
||||||
);
|
|
||||||
await user.click(screen.getAllByText('Select roles')[0]);
|
|
||||||
await user.click(await screen.findByText('Viewer'));
|
|
||||||
await user.click(
|
|
||||||
screen.getByRole('button', { name: /invite team members/i }),
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(toast.error).toHaveBeenCalledWith(
|
|
||||||
'Internal server error',
|
|
||||||
expect.anything(),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('uses inviteUsers (bulk) when multiple rows are filled', async () => {
|
it('uses inviteUsers (bulk) when multiple rows are filled', async () => {
|
||||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||||
const onComplete = jest.fn();
|
const onComplete = jest.fn();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
// eslint-disable-next-line no-restricted-imports
|
// eslint-disable-next-line no-restricted-imports
|
||||||
import { useSelector } from 'react-redux'; // old code, TODO: fix this correctly
|
import { useSelector } from 'react-redux';
|
||||||
import { useCopyToClipboard, useLocation } from 'react-use';
|
import { useCopyToClipboard, useLocation } from 'react-use';
|
||||||
import { Color, Spacing } from '@signozhq/design-tokens';
|
import { Color, Spacing } from '@signozhq/design-tokens';
|
||||||
import { Button, Divider, Drawer, Radio, Tooltip, Typography } from 'antd';
|
import { Button, Divider, Drawer, Radio, Tooltip, Typography } from 'antd';
|
||||||
@@ -49,7 +49,6 @@ import { AppState } from 'store/reducers';
|
|||||||
import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData';
|
import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { DataSource, StringOperators } from 'types/common/queryBuilder';
|
import { DataSource, StringOperators } from 'types/common/queryBuilder';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import { isModifierKeyPressed } from 'utils/app';
|
|
||||||
|
|
||||||
import { RESOURCE_KEYS, VIEW_TYPES, VIEWS } from './constants';
|
import { RESOURCE_KEYS, VIEW_TYPES, VIEWS } from './constants';
|
||||||
import { LogDetailInnerProps, LogDetailProps } from './LogDetail.interfaces';
|
import { LogDetailInnerProps, LogDetailProps } from './LogDetail.interfaces';
|
||||||
@@ -222,7 +221,7 @@ function LogDetailInner({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Go to logs explorer page with the log data
|
// Go to logs explorer page with the log data
|
||||||
const handleOpenInExplorer = (e?: React.MouseEvent): void => {
|
const handleOpenInExplorer = (): void => {
|
||||||
const queryParams = {
|
const queryParams = {
|
||||||
[QueryParams.activeLogId]: `"${log?.id}"`,
|
[QueryParams.activeLogId]: `"${log?.id}"`,
|
||||||
[QueryParams.startTime]: minTime?.toString() || '',
|
[QueryParams.startTime]: minTime?.toString() || '',
|
||||||
@@ -235,9 +234,7 @@ function LogDetailInner({
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
safeNavigate(`${ROUTES.LOGS_EXPLORER}?${createQueryParams(queryParams)}`, {
|
safeNavigate(`${ROUTES.LOGS_EXPLORER}?${createQueryParams(queryParams)}`);
|
||||||
newTab: !!e && isModifierKeyPressed(e),
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleQueryExpressionChange = useCallback(
|
const handleQueryExpressionChange = useCallback(
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ function CodeCopyBtn({
|
|||||||
let copiedText = '';
|
let copiedText = '';
|
||||||
if (children && Array.isArray(children)) {
|
if (children && Array.isArray(children)) {
|
||||||
setIsSnippetCopied(true);
|
setIsSnippetCopied(true);
|
||||||
// eslint-disable-next-line no-restricted-properties
|
|
||||||
navigator.clipboard.writeText(children[0].props.children[0]).finally(() => {
|
navigator.clipboard.writeText(children[0].props.children[0]).finally(() => {
|
||||||
copiedText = (children[0].props.children[0] as string).slice(0, 200); // slicing is done due to the limitation in accepted char length in attributes
|
copiedText = (children[0].props.children[0] as string).slice(0, 200); // slicing is done due to the limitation in accepted char length in attributes
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
@@ -401,7 +401,6 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
|
|||||||
|
|
||||||
const textToCopy = selectedTexts.join(', ');
|
const textToCopy = selectedTexts.join(', ');
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-properties
|
|
||||||
navigator.clipboard.writeText(textToCopy).catch(console.error);
|
navigator.clipboard.writeText(textToCopy).catch(console.error);
|
||||||
}, [selectedChips, selectedValues]);
|
}, [selectedChips, selectedValues]);
|
||||||
|
|
||||||
|
|||||||
@@ -86,8 +86,8 @@ jest.mock('hooks/useDarkMode', () => ({
|
|||||||
useIsDarkMode: (): boolean => false,
|
useIsDarkMode: (): boolean => false,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('providers/Dashboard/store/useDashboardStore', () => ({
|
jest.mock('providers/Dashboard/Dashboard', () => ({
|
||||||
useDashboardStore: (): { selectedDashboard: undefined } => ({
|
useDashboard: (): { selectedDashboard: undefined } => ({
|
||||||
selectedDashboard: undefined,
|
selectedDashboard: undefined,
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { useQueryClient } from 'react-query';
|
import { useQueryClient } from 'react-query';
|
||||||
import { useCopyToClipboard } from 'react-use';
|
|
||||||
import { DialogWrapper } from '@signozhq/dialog';
|
import { DialogWrapper } from '@signozhq/dialog';
|
||||||
import { toast } from '@signozhq/sonner';
|
import { toast } from '@signozhq/sonner';
|
||||||
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
||||||
@@ -106,23 +105,19 @@ function AddKeyModal(): JSX.Element {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const [copyState, copyToClipboard] = useCopyToClipboard();
|
|
||||||
const handleCopy = useCallback(async (): Promise<void> => {
|
const handleCopy = useCallback(async (): Promise<void> => {
|
||||||
if (!createdKey?.key) {
|
if (!createdKey?.key) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
copyToClipboard(createdKey.key);
|
await navigator.clipboard.writeText(createdKey.key);
|
||||||
setHasCopied(true);
|
setHasCopied(true);
|
||||||
setTimeout(() => setHasCopied(false), 2000);
|
setTimeout(() => setHasCopied(false), 2000);
|
||||||
toast.success('Key copied to clipboard', { richColors: true });
|
toast.success('Key copied to clipboard', { richColors: true });
|
||||||
}, [copyToClipboard, createdKey?.key]);
|
} catch {
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (copyState.error) {
|
|
||||||
toast.error('Failed to copy key', { richColors: true });
|
toast.error('Failed to copy key', { richColors: true });
|
||||||
}
|
}
|
||||||
}, [copyState.error]);
|
}, [createdKey]);
|
||||||
|
|
||||||
const handleClose = useCallback((): void => {
|
const handleClose = useCallback((): void => {
|
||||||
setIsAddKeyOpen(null);
|
setIsAddKeyOpen(null);
|
||||||
|
|||||||
@@ -9,16 +9,6 @@ jest.mock('@signozhq/sonner', () => ({
|
|||||||
toast: { success: jest.fn(), error: jest.fn() },
|
toast: { success: jest.fn(), error: jest.fn() },
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const mockCopyToClipboard = jest.fn();
|
|
||||||
const mockCopyState = { value: undefined, error: undefined };
|
|
||||||
|
|
||||||
jest.mock('react-use', () => ({
|
|
||||||
useCopyToClipboard: (): [typeof mockCopyState, typeof mockCopyToClipboard] => [
|
|
||||||
mockCopyState,
|
|
||||||
mockCopyToClipboard,
|
|
||||||
],
|
|
||||||
}));
|
|
||||||
|
|
||||||
const mockToast = jest.mocked(toast);
|
const mockToast = jest.mocked(toast);
|
||||||
|
|
||||||
const SA_KEYS_ENDPOINT = '*/api/v1/service_accounts/sa-1/keys';
|
const SA_KEYS_ENDPOINT = '*/api/v1/service_accounts/sa-1/keys';
|
||||||
@@ -45,9 +35,16 @@ function renderModal(): ReturnType<typeof render> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('AddKeyModal', () => {
|
describe('AddKeyModal', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
Object.defineProperty(navigator, 'clipboard', {
|
||||||
|
value: { writeText: jest.fn().mockResolvedValue(undefined) },
|
||||||
|
configurable: true,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
mockCopyToClipboard.mockClear();
|
|
||||||
server.use(
|
server.use(
|
||||||
rest.post(SA_KEYS_ENDPOINT, (_, res, ctx) =>
|
rest.post(SA_KEYS_ENDPOINT, (_, res, ctx) =>
|
||||||
res(ctx.status(201), ctx.json(createdKeyResponse)),
|
res(ctx.status(201), ctx.json(createdKeyResponse)),
|
||||||
@@ -93,6 +90,9 @@ describe('AddKeyModal', () => {
|
|||||||
|
|
||||||
it('copy button writes key to clipboard and shows toast.success', async () => {
|
it('copy button writes key to clipboard and shows toast.success', async () => {
|
||||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||||
|
const writeTextSpy = jest
|
||||||
|
.spyOn(navigator.clipboard, 'writeText')
|
||||||
|
.mockResolvedValue(undefined);
|
||||||
|
|
||||||
renderModal();
|
renderModal();
|
||||||
|
|
||||||
@@ -115,12 +115,14 @@ describe('AddKeyModal', () => {
|
|||||||
await user.click(copyBtn);
|
await user.click(copyBtn);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(mockCopyToClipboard).toHaveBeenCalledWith('snz_abc123xyz456secret');
|
expect(writeTextSpy).toHaveBeenCalledWith('snz_abc123xyz456secret');
|
||||||
expect(mockToast.success).toHaveBeenCalledWith(
|
expect(mockToast.success).toHaveBeenCalledWith(
|
||||||
'Key copied to clipboard',
|
'Key copied to clipboard',
|
||||||
expect.anything(),
|
expect.anything(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
writeTextSpy.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Cancel button closes the modal', async () => {
|
it('Cancel button closes the modal', async () => {
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ function AverageResolutionCard({
|
|||||||
}: TotalTriggeredCardProps): JSX.Element {
|
}: TotalTriggeredCardProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<StatsCard
|
<StatsCard
|
||||||
displayValue={formatTime(+currentAvgResolutionTime)}
|
displayValue={formatTime(currentAvgResolutionTime)}
|
||||||
totalCurrentCount={+currentAvgResolutionTime}
|
totalCurrentCount={currentAvgResolutionTime}
|
||||||
totalPastCount={+pastAvgResolutionTime}
|
totalPastCount={pastAvgResolutionTime}
|
||||||
title="Avg. Resolution Time"
|
title="Avg. Resolution Time"
|
||||||
timeSeries={timeSeries}
|
timeSeries={timeSeries}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -800,10 +800,14 @@
|
|||||||
|
|
||||||
.ant-table-cell:has(.top-services-item-latency) {
|
.ant-table-cell:has(.top-services-item-latency) {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
opacity: 0.8;
|
||||||
|
background: rgba(171, 189, 255, 0.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-table-cell:has(.top-services-item-latency-title) {
|
.ant-table-cell:has(.top-services-item-latency-title) {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
opacity: 0.8;
|
||||||
|
background: rgba(171, 189, 255, 0.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-table-tbody > tr:hover > td {
|
.ant-table-tbody > tr:hover > td {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
.api-quick-filters-header {
|
.api-quick-filters-header {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border-bottom: 1px solid var(--bg-slate-400);
|
border-bottom: 1px solid var(--bg-slate-400);
|
||||||
|
border-right: 1px solid var(--bg-slate-400);
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -25,7 +26,6 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
border-top: 0px;
|
|
||||||
border-bottom: 1px solid var(--bg-slate-400);
|
border-bottom: 1px solid var(--bg-slate-400);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,18 +220,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.lightMode {
|
.lightMode {
|
||||||
.api-quick-filter-left-section {
|
|
||||||
.api-quick-filters-header {
|
|
||||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.api-module-right-section {
|
|
||||||
.toolbar {
|
|
||||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-filtered-domains-message-container {
|
.no-filtered-domains-message-container {
|
||||||
.no-filtered-domains-message-content {
|
.no-filtered-domains-message-content {
|
||||||
.no-filtered-domains-message {
|
.no-filtered-domains-message {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { ALERTS_DATA_SOURCE_MAP } from 'constants/alerts';
|
|||||||
import { FeatureKeys } from 'constants/features';
|
import { FeatureKeys } from 'constants/features';
|
||||||
import { useAppContext } from 'providers/App/App';
|
import { useAppContext } from 'providers/App/App';
|
||||||
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||||
import { isModifierKeyPressed } from 'utils/app';
|
|
||||||
|
|
||||||
import { getOptionList } from './config';
|
import { getOptionList } from './config';
|
||||||
import { AlertTypeCard, SelectTypeContainer } from './styles';
|
import { AlertTypeCard, SelectTypeContainer } from './styles';
|
||||||
@@ -71,8 +70,8 @@ function SelectAlertType({ onSelect }: SelectAlertTypeProps): JSX.Element {
|
|||||||
</Tag>
|
</Tag>
|
||||||
) : undefined
|
) : undefined
|
||||||
}
|
}
|
||||||
onClick={(e): void => {
|
onClick={(): void => {
|
||||||
onSelect(option.selection, isModifierKeyPressed(e));
|
onSelect(option.selection);
|
||||||
}}
|
}}
|
||||||
data-testid={`alert-type-card-${option.selection}`}
|
data-testid={`alert-type-card-${option.selection}`}
|
||||||
>
|
>
|
||||||
@@ -109,7 +108,7 @@ function SelectAlertType({ onSelect }: SelectAlertTypeProps): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface SelectAlertTypeProps {
|
interface SelectAlertTypeProps {
|
||||||
onSelect: (type: AlertTypes, newTab?: boolean) => void;
|
onSelect: (typ: AlertTypes) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectAlertType;
|
export default SelectAlertType;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { Button, Tooltip, Typography } from 'antd';
|
|||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import { useSafeNavigate } from 'hooks/useSafeNavigate';
|
import { useSafeNavigate } from 'hooks/useSafeNavigate';
|
||||||
import { Check, Loader, Send, X } from 'lucide-react';
|
import { Check, Loader, Send, X } from 'lucide-react';
|
||||||
import { isModifierKeyPressed } from 'utils/app';
|
|
||||||
|
|
||||||
import { useCreateAlertState } from '../context';
|
import { useCreateAlertState } from '../context';
|
||||||
import {
|
import {
|
||||||
@@ -34,9 +33,9 @@ function Footer(): JSX.Element {
|
|||||||
const { currentQuery } = useQueryBuilder();
|
const { currentQuery } = useQueryBuilder();
|
||||||
const { safeNavigate } = useSafeNavigate();
|
const { safeNavigate } = useSafeNavigate();
|
||||||
|
|
||||||
const handleDiscard = (e: React.MouseEvent): void => {
|
const handleDiscard = (): void => {
|
||||||
discardAlertRule();
|
discardAlertRule();
|
||||||
safeNavigate('/alerts', { newTab: isModifierKeyPressed(e) });
|
safeNavigate('/alerts');
|
||||||
};
|
};
|
||||||
|
|
||||||
const alertValidationMessage = useMemo(
|
const alertValidationMessage = useMemo(
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import { ReactNode } from 'react';
|
|
||||||
import { MemoryRouter, useLocation } from 'react-router-dom';
|
import { MemoryRouter, useLocation } from 'react-router-dom';
|
||||||
import { useDashboardBootstrap } from 'hooks/dashboard/useDashboardBootstrap';
|
|
||||||
import {
|
import {
|
||||||
getDashboardById,
|
getDashboardById,
|
||||||
getNonIntegrationDashboardById,
|
getNonIntegrationDashboardById,
|
||||||
@@ -8,9 +6,10 @@ import {
|
|||||||
import { server } from 'mocks-server/server';
|
import { server } from 'mocks-server/server';
|
||||||
import { rest } from 'msw';
|
import { rest } from 'msw';
|
||||||
import {
|
import {
|
||||||
resetDashboard,
|
DashboardContext,
|
||||||
useDashboardStore,
|
DashboardProvider,
|
||||||
} from 'providers/Dashboard/store/useDashboardStore';
|
} from 'providers/Dashboard/Dashboard';
|
||||||
|
import { IDashboardContext } from 'providers/Dashboard/types';
|
||||||
import {
|
import {
|
||||||
fireEvent,
|
fireEvent,
|
||||||
render,
|
render,
|
||||||
@@ -22,18 +21,6 @@ import { Dashboard } from 'types/api/dashboard/getAll';
|
|||||||
|
|
||||||
import DashboardDescription from '..';
|
import DashboardDescription from '..';
|
||||||
|
|
||||||
function DashboardBootstrapWrapper({
|
|
||||||
dashboardId,
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
dashboardId: string;
|
|
||||||
children: ReactNode;
|
|
||||||
}): JSX.Element {
|
|
||||||
useDashboardBootstrap(dashboardId);
|
|
||||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
|
||||||
return <>{children}</>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MockSafeNavigateReturn {
|
interface MockSafeNavigateReturn {
|
||||||
safeNavigate: jest.MockedFunction<(url: string) => void>;
|
safeNavigate: jest.MockedFunction<(url: string) => void>;
|
||||||
}
|
}
|
||||||
@@ -67,7 +54,6 @@ describe('Dashboard landing page actions header tests', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockSafeNavigate.mockClear();
|
mockSafeNavigate.mockClear();
|
||||||
sessionStorage.clear();
|
sessionStorage.clear();
|
||||||
resetDashboard();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('unlock dashboard should be disabled for integrations created dashboards', async () => {
|
it('unlock dashboard should be disabled for integrations created dashboards', async () => {
|
||||||
@@ -78,7 +64,7 @@ describe('Dashboard landing page actions header tests', () => {
|
|||||||
(useLocation as jest.Mock).mockReturnValue(mockLocation);
|
(useLocation as jest.Mock).mockReturnValue(mockLocation);
|
||||||
const { getByTestId } = render(
|
const { getByTestId } = render(
|
||||||
<MemoryRouter initialEntries={[DASHBOARD_PATH]}>
|
<MemoryRouter initialEntries={[DASHBOARD_PATH]}>
|
||||||
<DashboardBootstrapWrapper dashboardId="4">
|
<DashboardProvider dashboardId="4">
|
||||||
<DashboardDescription
|
<DashboardDescription
|
||||||
handle={{
|
handle={{
|
||||||
active: false,
|
active: false,
|
||||||
@@ -87,7 +73,7 @@ describe('Dashboard landing page actions header tests', () => {
|
|||||||
node: { current: null },
|
node: { current: null },
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</DashboardBootstrapWrapper>
|
</DashboardProvider>
|
||||||
</MemoryRouter>,
|
</MemoryRouter>,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -119,7 +105,7 @@ describe('Dashboard landing page actions header tests', () => {
|
|||||||
);
|
);
|
||||||
const { getByTestId } = render(
|
const { getByTestId } = render(
|
||||||
<MemoryRouter initialEntries={[DASHBOARD_PATH]}>
|
<MemoryRouter initialEntries={[DASHBOARD_PATH]}>
|
||||||
<DashboardBootstrapWrapper dashboardId="4">
|
<DashboardProvider dashboardId="4">
|
||||||
<DashboardDescription
|
<DashboardDescription
|
||||||
handle={{
|
handle={{
|
||||||
active: false,
|
active: false,
|
||||||
@@ -128,7 +114,7 @@ describe('Dashboard landing page actions header tests', () => {
|
|||||||
node: { current: null },
|
node: { current: null },
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</DashboardBootstrapWrapper>
|
</DashboardProvider>
|
||||||
</MemoryRouter>,
|
</MemoryRouter>,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -158,7 +144,7 @@ describe('Dashboard landing page actions header tests', () => {
|
|||||||
|
|
||||||
const { getByText } = render(
|
const { getByText } = render(
|
||||||
<MemoryRouter initialEntries={[DASHBOARD_PATH]}>
|
<MemoryRouter initialEntries={[DASHBOARD_PATH]}>
|
||||||
<DashboardBootstrapWrapper dashboardId="4">
|
<DashboardProvider dashboardId="4">
|
||||||
<DashboardDescription
|
<DashboardDescription
|
||||||
handle={{
|
handle={{
|
||||||
active: false,
|
active: false,
|
||||||
@@ -167,7 +153,7 @@ describe('Dashboard landing page actions header tests', () => {
|
|||||||
node: { current: null },
|
node: { current: null },
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</DashboardBootstrapWrapper>
|
</DashboardProvider>
|
||||||
</MemoryRouter>,
|
</MemoryRouter>,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -195,26 +181,37 @@ describe('Dashboard landing page actions header tests', () => {
|
|||||||
|
|
||||||
(useLocation as jest.Mock).mockReturnValue(mockLocation);
|
(useLocation as jest.Mock).mockReturnValue(mockLocation);
|
||||||
|
|
||||||
useDashboardStore.setState({
|
const mockContextValue: IDashboardContext = {
|
||||||
|
isDashboardLocked: false,
|
||||||
|
handleDashboardLockToggle: jest.fn(),
|
||||||
|
dashboardResponse: {} as IDashboardContext['dashboardResponse'],
|
||||||
selectedDashboard: (getDashboardById.data as unknown) as Dashboard,
|
selectedDashboard: (getDashboardById.data as unknown) as Dashboard,
|
||||||
layouts: [],
|
layouts: [],
|
||||||
panelMap: {},
|
panelMap: {},
|
||||||
setPanelMap: jest.fn(),
|
setPanelMap: jest.fn(),
|
||||||
setLayouts: jest.fn(),
|
setLayouts: jest.fn(),
|
||||||
setSelectedDashboard: jest.fn(),
|
setSelectedDashboard: jest.fn(),
|
||||||
|
updatedTimeRef: { current: null },
|
||||||
|
updateLocalStorageDashboardVariables: jest.fn(),
|
||||||
|
dashboardQueryRangeCalled: false,
|
||||||
|
setDashboardQueryRangeCalled: jest.fn(),
|
||||||
|
isDashboardFetching: false,
|
||||||
columnWidths: {},
|
columnWidths: {},
|
||||||
});
|
setColumnWidths: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
const { getByText } = render(
|
const { getByText } = render(
|
||||||
<MemoryRouter initialEntries={[DASHBOARD_PATH]}>
|
<MemoryRouter initialEntries={[DASHBOARD_PATH]}>
|
||||||
<DashboardDescription
|
<DashboardContext.Provider value={mockContextValue}>
|
||||||
handle={{
|
<DashboardDescription
|
||||||
active: false,
|
handle={{
|
||||||
enter: (): Promise<void> => Promise.resolve(),
|
active: false,
|
||||||
exit: (): Promise<void> => Promise.resolve(),
|
enter: (): Promise<void> => Promise.resolve(),
|
||||||
node: { current: null },
|
exit: (): Promise<void> => Promise.resolve(),
|
||||||
}}
|
node: { current: null },
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
</DashboardContext.Provider>
|
||||||
</MemoryRouter>,
|
</MemoryRouter>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import { DeleteButton } from 'container/ListOfDashboard/TableComponents/DeleteBu
|
|||||||
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
|
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
|
||||||
import { useDashboardVariables } from 'hooks/dashboard/useDashboardVariables';
|
import { useDashboardVariables } from 'hooks/dashboard/useDashboardVariables';
|
||||||
import { useGetPublicDashboardMeta } from 'hooks/dashboard/useGetPublicDashboardMeta';
|
import { useGetPublicDashboardMeta } from 'hooks/dashboard/useGetPublicDashboardMeta';
|
||||||
import { useLockDashboard } from 'hooks/dashboard/useLockDashboard';
|
|
||||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||||
import useComponentPermission from 'hooks/useComponentPermission';
|
import useComponentPermission from 'hooks/useComponentPermission';
|
||||||
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
|
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
|
||||||
@@ -40,11 +39,8 @@ import {
|
|||||||
X,
|
X,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useAppContext } from 'providers/App/App';
|
import { useAppContext } from 'providers/App/App';
|
||||||
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { usePanelTypeSelectionModalStore } from 'providers/Dashboard/helpers/panelTypeSelectionModalHelper';
|
import { usePanelTypeSelectionModalStore } from 'providers/Dashboard/helpers/panelTypeSelectionModalHelper';
|
||||||
import {
|
|
||||||
selectIsDashboardLocked,
|
|
||||||
useDashboardStore,
|
|
||||||
} from 'providers/Dashboard/store/useDashboardStore';
|
|
||||||
import { sortLayout } from 'providers/Dashboard/util';
|
import { sortLayout } from 'providers/Dashboard/util';
|
||||||
import { DashboardData } from 'types/api/dashboard/getAll';
|
import { DashboardData } from 'types/api/dashboard/getAll';
|
||||||
import { Props } from 'types/api/dashboard/update';
|
import { Props } from 'types/api/dashboard/update';
|
||||||
@@ -83,11 +79,10 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
|
|||||||
setPanelMap,
|
setPanelMap,
|
||||||
layouts,
|
layouts,
|
||||||
setLayouts,
|
setLayouts,
|
||||||
|
isDashboardLocked,
|
||||||
setSelectedDashboard,
|
setSelectedDashboard,
|
||||||
} = useDashboardStore();
|
handleDashboardLockToggle,
|
||||||
|
} = useDashboard();
|
||||||
const isDashboardLocked = useDashboardStore(selectIsDashboardLocked);
|
|
||||||
const handleDashboardLockToggle = useLockDashboard();
|
|
||||||
|
|
||||||
const variablesSettingsTabHandle = useRef<VariablesSettingsTab>(null);
|
const variablesSettingsTabHandle = useRef<VariablesSettingsTab>(null);
|
||||||
const [isSettingsDrawerOpen, setIsSettingsDrawerOpen] = useState<boolean>(
|
const [isSettingsDrawerOpen, setIsSettingsDrawerOpen] = useState<boolean>(
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import {
|
|||||||
Pyramid,
|
Pyramid,
|
||||||
X,
|
X,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useDashboardStore } from 'providers/Dashboard/store/useDashboardStore';
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import {
|
import {
|
||||||
IDashboardVariable,
|
IDashboardVariable,
|
||||||
@@ -239,7 +239,7 @@ function VariableItem({
|
|||||||
|
|
||||||
const [selectedWidgets, setSelectedWidgets] = useState<string[]>([]);
|
const [selectedWidgets, setSelectedWidgets] = useState<string[]>([]);
|
||||||
|
|
||||||
const { selectedDashboard } = useDashboardStore();
|
const { selectedDashboard } = useDashboard();
|
||||||
const widgetsByDynamicVariableId = useWidgetsByDynamicVariableId();
|
const widgetsByDynamicVariableId = useWidgetsByDynamicVariableId();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import { CustomMultiSelect } from 'components/NewSelect';
|
import { CustomMultiSelect } from 'components/NewSelect';
|
||||||
import { PANEL_GROUP_TYPES } from 'constants/queryBuilder';
|
import { PANEL_GROUP_TYPES } from 'constants/queryBuilder';
|
||||||
import { generateGridTitle } from 'container/GridPanelSwitch/utils';
|
import { generateGridTitle } from 'container/GridPanelSwitch/utils';
|
||||||
import { useDashboardStore } from 'providers/Dashboard/store/useDashboardStore';
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { WidgetRow, Widgets } from 'types/api/dashboard/getAll';
|
import { WidgetRow, Widgets } from 'types/api/dashboard/getAll';
|
||||||
|
|
||||||
export function WidgetSelector({
|
export function WidgetSelector({
|
||||||
@@ -12,7 +12,7 @@ export function WidgetSelector({
|
|||||||
selectedWidgets: string[];
|
selectedWidgets: string[];
|
||||||
setSelectedWidgets: (widgets: string[]) => void;
|
setSelectedWidgets: (widgets: string[]) => void;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const { selectedDashboard } = useDashboardStore();
|
const { selectedDashboard } = useDashboard();
|
||||||
|
|
||||||
// Get layout IDs for cross-referencing
|
// Get layout IDs for cross-referencing
|
||||||
const layoutIds = new Set(
|
const layoutIds = new Set(
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import { useDashboardVariables } from 'hooks/dashboard/useDashboardVariables';
|
|||||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import { PenLine, Trash2 } from 'lucide-react';
|
import { PenLine, Trash2 } from 'lucide-react';
|
||||||
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { IDashboardVariables } from 'providers/Dashboard/store/dashboardVariables/dashboardVariablesStoreTypes';
|
import { IDashboardVariables } from 'providers/Dashboard/store/dashboardVariables/dashboardVariablesStoreTypes';
|
||||||
import { useDashboardStore } from 'providers/Dashboard/store/useDashboardStore';
|
|
||||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||||
|
|
||||||
import { TVariableMode } from './types';
|
import { TVariableMode } from './types';
|
||||||
@@ -87,7 +87,7 @@ function VariablesSettings({
|
|||||||
|
|
||||||
const { t } = useTranslation(['dashboard']);
|
const { t } = useTranslation(['dashboard']);
|
||||||
|
|
||||||
const { selectedDashboard, setSelectedDashboard } = useDashboardStore();
|
const { selectedDashboard, setSelectedDashboard } = useDashboard();
|
||||||
const { dashboardVariables } = useDashboardVariables();
|
const { dashboardVariables } = useDashboardVariables();
|
||||||
|
|
||||||
const { notifications } = useNotifications();
|
const { notifications } = useNotifications();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import AddTags from 'container/DashboardContainer/DashboardSettings/General/AddT
|
|||||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
import { Check, X } from 'lucide-react';
|
import { Check, X } from 'lucide-react';
|
||||||
import { useDashboardStore } from 'providers/Dashboard/store/useDashboardStore';
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
|
|
||||||
import { Button } from './styles';
|
import { Button } from './styles';
|
||||||
import { Base64Icons } from './utils';
|
import { Base64Icons } from './utils';
|
||||||
@@ -15,7 +15,7 @@ import './GeneralSettings.styles.scss';
|
|||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
|
|
||||||
function GeneralDashboardSettings(): JSX.Element {
|
function GeneralDashboardSettings(): JSX.Element {
|
||||||
const { selectedDashboard, setSelectedDashboard } = useDashboardStore();
|
const { selectedDashboard, setSelectedDashboard } = useDashboard();
|
||||||
|
|
||||||
const updateDashboardMutation = useUpdateDashboard();
|
const updateDashboardMutation = useUpdateDashboard();
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
import { useCopyToClipboard } from 'react-use';
|
import { useCopyToClipboard } from 'react-use';
|
||||||
import { toast } from '@signozhq/sonner';
|
import { toast } from '@signozhq/sonner';
|
||||||
import { fireEvent, within } from '@testing-library/react';
|
import { fireEvent, within } from '@testing-library/react';
|
||||||
import { DEFAULT_TIME_RANGE } from 'container/TopNav/DateTimeSelectionV2/constants';
|
|
||||||
import { StatusCodes } from 'http-status-codes';
|
import { StatusCodes } from 'http-status-codes';
|
||||||
import {
|
import {
|
||||||
publishedPublicDashboardMeta,
|
publishedPublicDashboardMeta,
|
||||||
unpublishedPublicDashboardMeta,
|
unpublishedPublicDashboardMeta,
|
||||||
} from 'mocks-server/__mockdata__/publicDashboard';
|
} from 'mocks-server/__mockdata__/publicDashboard';
|
||||||
import { rest, server } from 'mocks-server/server';
|
import { rest, server } from 'mocks-server/server';
|
||||||
import { useDashboardStore } from 'providers/Dashboard/store/useDashboardStore';
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { render, screen, userEvent, waitFor } from 'tests/test-utils';
|
import { render, screen, userEvent, waitFor } from 'tests/test-utils';
|
||||||
import { USER_ROLES } from 'types/roles';
|
import { USER_ROLES } from 'types/roles';
|
||||||
|
|
||||||
import PublicDashboardSetting from '../index';
|
import PublicDashboardSetting from '../index';
|
||||||
|
|
||||||
// Mock dependencies
|
// Mock dependencies
|
||||||
jest.mock('providers/Dashboard/store/useDashboardStore');
|
jest.mock('providers/Dashboard/Dashboard');
|
||||||
jest.mock('react-use', () => ({
|
jest.mock('react-use', () => ({
|
||||||
...jest.requireActual('react-use'),
|
...jest.requireActual('react-use'),
|
||||||
useCopyToClipboard: jest.fn(),
|
useCopyToClipboard: jest.fn(),
|
||||||
@@ -27,13 +26,14 @@ jest.mock('@signozhq/sonner', () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const mockUseDashboard = jest.mocked(useDashboardStore);
|
const mockUseDashboard = jest.mocked(useDashboard);
|
||||||
const mockUseCopyToClipboard = jest.mocked(useCopyToClipboard);
|
const mockUseCopyToClipboard = jest.mocked(useCopyToClipboard);
|
||||||
const mockToast = jest.mocked(toast);
|
const mockToast = jest.mocked(toast);
|
||||||
|
|
||||||
// Test constants
|
// Test constants
|
||||||
const MOCK_DASHBOARD_ID = 'test-dashboard-id';
|
const MOCK_DASHBOARD_ID = 'test-dashboard-id';
|
||||||
const MOCK_PUBLIC_PATH = '/public/dashboard/test-dashboard-id';
|
const MOCK_PUBLIC_PATH = '/public/dashboard/test-dashboard-id';
|
||||||
|
const DEFAULT_TIME_RANGE = '30m';
|
||||||
const DASHBOARD_VARIABLES_WARNING =
|
const DASHBOARD_VARIABLES_WARNING =
|
||||||
"Dashboard variables won't work in public dashboards";
|
"Dashboard variables won't work in public dashboards";
|
||||||
|
|
||||||
@@ -67,10 +67,10 @@ beforeEach(() => {
|
|||||||
// Mock window.open
|
// Mock window.open
|
||||||
window.open = jest.fn();
|
window.open = jest.fn();
|
||||||
|
|
||||||
// Mock useDashboardStore
|
// Mock useDashboard
|
||||||
mockUseDashboard.mockReturnValue(({
|
mockUseDashboard.mockReturnValue(({
|
||||||
selectedDashboard: mockSelectedDashboard,
|
selectedDashboard: mockSelectedDashboard,
|
||||||
} as unknown) as ReturnType<typeof useDashboardStore>);
|
} as unknown) as ReturnType<typeof useDashboard>);
|
||||||
|
|
||||||
// Mock useCopyToClipboard
|
// Mock useCopyToClipboard
|
||||||
mockUseCopyToClipboard.mockReturnValue(([
|
mockUseCopyToClipboard.mockReturnValue(([
|
||||||
|
|||||||
@@ -7,12 +7,11 @@ import { Button, Select, Typography } from 'antd';
|
|||||||
import createPublicDashboardAPI from 'api/dashboard/public/createPublicDashboard';
|
import createPublicDashboardAPI from 'api/dashboard/public/createPublicDashboard';
|
||||||
import revokePublicDashboardAccessAPI from 'api/dashboard/public/revokePublicDashboardAccess';
|
import revokePublicDashboardAccessAPI from 'api/dashboard/public/revokePublicDashboardAccess';
|
||||||
import updatePublicDashboardAPI from 'api/dashboard/public/updatePublicDashboard';
|
import updatePublicDashboardAPI from 'api/dashboard/public/updatePublicDashboard';
|
||||||
import { DEFAULT_TIME_RANGE } from 'container/TopNav/DateTimeSelectionV2/constants';
|
|
||||||
import { useGetPublicDashboardMeta } from 'hooks/dashboard/useGetPublicDashboardMeta';
|
import { useGetPublicDashboardMeta } from 'hooks/dashboard/useGetPublicDashboardMeta';
|
||||||
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
|
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
|
||||||
import { Copy, ExternalLink, Globe, Info, Loader2, Trash } from 'lucide-react';
|
import { Copy, ExternalLink, Globe, Info, Loader2, Trash } from 'lucide-react';
|
||||||
import { useAppContext } from 'providers/App/App';
|
import { useAppContext } from 'providers/App/App';
|
||||||
import { useDashboardStore } from 'providers/Dashboard/store/useDashboardStore';
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { PublicDashboardMetaProps } from 'types/api/dashboard/public/getMeta';
|
import { PublicDashboardMetaProps } from 'types/api/dashboard/public/getMeta';
|
||||||
import APIError from 'types/api/error';
|
import APIError from 'types/api/error';
|
||||||
import { USER_ROLES } from 'types/roles';
|
import { USER_ROLES } from 'types/roles';
|
||||||
@@ -57,10 +56,10 @@ function PublicDashboardSetting(): JSX.Element {
|
|||||||
PublicDashboardMetaProps | undefined
|
PublicDashboardMetaProps | undefined
|
||||||
>(undefined);
|
>(undefined);
|
||||||
const [timeRangeEnabled, setTimeRangeEnabled] = useState(true);
|
const [timeRangeEnabled, setTimeRangeEnabled] = useState(true);
|
||||||
const [defaultTimeRange, setDefaultTimeRange] = useState(DEFAULT_TIME_RANGE);
|
const [defaultTimeRange, setDefaultTimeRange] = useState('30m');
|
||||||
const [, setCopyPublicDashboardURL] = useCopyToClipboard();
|
const [, setCopyPublicDashboardURL] = useCopyToClipboard();
|
||||||
|
|
||||||
const { selectedDashboard } = useDashboardStore();
|
const { selectedDashboard } = useDashboard();
|
||||||
|
|
||||||
const { isCloudUser, isEnterpriseSelfHostedUser } = useGetTenantLicense();
|
const { isCloudUser, isEnterpriseSelfHostedUser } = useGetTenantLicense();
|
||||||
|
|
||||||
@@ -100,7 +99,7 @@ function PublicDashboardSetting(): JSX.Element {
|
|||||||
console.error('Error getting public dashboard', errorPublicDashboard);
|
console.error('Error getting public dashboard', errorPublicDashboard);
|
||||||
setPublicDashboardData(undefined);
|
setPublicDashboardData(undefined);
|
||||||
setTimeRangeEnabled(true);
|
setTimeRangeEnabled(true);
|
||||||
setDefaultTimeRange(DEFAULT_TIME_RANGE);
|
setDefaultTimeRange('30m');
|
||||||
}
|
}
|
||||||
}, [publicDashboardResponse, errorPublicDashboard]);
|
}, [publicDashboardResponse, errorPublicDashboard]);
|
||||||
|
|
||||||
@@ -110,7 +109,7 @@ function PublicDashboardSetting(): JSX.Element {
|
|||||||
publicDashboardResponse?.data?.timeRangeEnabled || false,
|
publicDashboardResponse?.data?.timeRangeEnabled || false,
|
||||||
);
|
);
|
||||||
setDefaultTimeRange(
|
setDefaultTimeRange(
|
||||||
publicDashboardResponse?.data?.defaultTimeRange || DEFAULT_TIME_RANGE,
|
publicDashboardResponse?.data?.defaultTimeRange || '30m',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [publicDashboardResponse]);
|
}, [publicDashboardResponse]);
|
||||||
|
|||||||
@@ -3,14 +3,13 @@ import { memo, useCallback, useEffect, useMemo } from 'react';
|
|||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { Row } from 'antd';
|
import { Row } from 'antd';
|
||||||
import { ALL_SELECTED_VALUE } from 'components/NewSelect/utils';
|
import { ALL_SELECTED_VALUE } from 'components/NewSelect/utils';
|
||||||
import { updateLocalStorageDashboardVariable } from 'hooks/dashboard/useDashboardFromLocalStorage';
|
|
||||||
import {
|
import {
|
||||||
useDashboardVariables,
|
useDashboardVariables,
|
||||||
useDashboardVariablesSelector,
|
useDashboardVariablesSelector,
|
||||||
} from 'hooks/dashboard/useDashboardVariables';
|
} from 'hooks/dashboard/useDashboardVariables';
|
||||||
import useVariablesFromUrl from 'hooks/dashboard/useVariablesFromUrl';
|
import useVariablesFromUrl from 'hooks/dashboard/useVariablesFromUrl';
|
||||||
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { updateDashboardVariablesStore } from 'providers/Dashboard/store/dashboardVariables/dashboardVariablesStore';
|
import { updateDashboardVariablesStore } from 'providers/Dashboard/store/dashboardVariables/dashboardVariablesStore';
|
||||||
import { useDashboardStore } from 'providers/Dashboard/store/useDashboardStore';
|
|
||||||
import {
|
import {
|
||||||
enqueueDescendantsOfVariable,
|
enqueueDescendantsOfVariable,
|
||||||
enqueueFetchOfAllVariables,
|
enqueueFetchOfAllVariables,
|
||||||
@@ -19,23 +18,23 @@ import {
|
|||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
|
||||||
|
|
||||||
import VariableItem from './VariableItem';
|
import VariableItem from './VariableItem';
|
||||||
|
|
||||||
import './DashboardVariableSelection.styles.scss';
|
import './DashboardVariableSelection.styles.scss';
|
||||||
|
|
||||||
function DashboardVariableSelection(): JSX.Element | null {
|
function DashboardVariableSelection(): JSX.Element | null {
|
||||||
const { dashboardId, setSelectedDashboard } = useDashboardStore(
|
const {
|
||||||
useShallow((s) => ({
|
setSelectedDashboard,
|
||||||
dashboardId: s.selectedDashboard?.id ?? '',
|
updateLocalStorageDashboardVariables,
|
||||||
setSelectedDashboard: s.setSelectedDashboard,
|
} = useDashboard();
|
||||||
})),
|
|
||||||
);
|
|
||||||
|
|
||||||
const { updateUrlVariable } = useVariablesFromUrl();
|
const { updateUrlVariable } = useVariablesFromUrl();
|
||||||
|
|
||||||
const { dashboardVariables } = useDashboardVariables();
|
const { dashboardVariables } = useDashboardVariables();
|
||||||
|
const dashboardId = useDashboardVariablesSelector(
|
||||||
|
(state) => state.dashboardId,
|
||||||
|
);
|
||||||
const sortedVariablesArray = useDashboardVariablesSelector(
|
const sortedVariablesArray = useDashboardVariablesSelector(
|
||||||
(state) => state.sortedVariablesArray,
|
(state) => state.sortedVariablesArray,
|
||||||
);
|
);
|
||||||
@@ -83,13 +82,7 @@ function DashboardVariableSelection(): JSX.Element | null {
|
|||||||
// This makes localStorage much lighter by avoiding storing all individual values
|
// This makes localStorage much lighter by avoiding storing all individual values
|
||||||
const variable = dashboardVariables[id] || dashboardVariables[name];
|
const variable = dashboardVariables[id] || dashboardVariables[name];
|
||||||
const isDynamic = variable.type === 'DYNAMIC';
|
const isDynamic = variable.type === 'DYNAMIC';
|
||||||
updateLocalStorageDashboardVariable(
|
updateLocalStorageDashboardVariables(name, value, allSelected, isDynamic);
|
||||||
dashboardId,
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
allSelected,
|
|
||||||
isDynamic,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (allSelected) {
|
if (allSelected) {
|
||||||
updateUrlVariable(name || id, ALL_SELECTED_VALUE);
|
updateUrlVariable(name || id, ALL_SELECTED_VALUE);
|
||||||
@@ -157,7 +150,13 @@ function DashboardVariableSelection(): JSX.Element | null {
|
|||||||
// Safe to call synchronously now that the store already has the updated value.
|
// Safe to call synchronously now that the store already has the updated value.
|
||||||
enqueueDescendantsOfVariable(name);
|
enqueueDescendantsOfVariable(name);
|
||||||
},
|
},
|
||||||
[dashboardId, dashboardVariables, updateUrlVariable, setSelectedDashboard],
|
[
|
||||||
|
dashboardId,
|
||||||
|
dashboardVariables,
|
||||||
|
updateLocalStorageDashboardVariables,
|
||||||
|
updateUrlVariable,
|
||||||
|
setSelectedDashboard,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -32,22 +32,11 @@ const mockVariableItemCallbacks: {
|
|||||||
// Mock providers/Dashboard/Dashboard
|
// Mock providers/Dashboard/Dashboard
|
||||||
const mockSetSelectedDashboard = jest.fn();
|
const mockSetSelectedDashboard = jest.fn();
|
||||||
const mockUpdateLocalStorageDashboardVariables = jest.fn();
|
const mockUpdateLocalStorageDashboardVariables = jest.fn();
|
||||||
interface MockDashboardStoreState {
|
jest.mock('providers/Dashboard/Dashboard', () => ({
|
||||||
selectedDashboard?: { id: string };
|
useDashboard: (): Record<string, unknown> => ({
|
||||||
setSelectedDashboard: typeof mockSetSelectedDashboard;
|
setSelectedDashboard: mockSetSelectedDashboard,
|
||||||
updateLocalStorageDashboardVariables: typeof mockUpdateLocalStorageDashboardVariables;
|
updateLocalStorageDashboardVariables: mockUpdateLocalStorageDashboardVariables,
|
||||||
}
|
}),
|
||||||
jest.mock('providers/Dashboard/store/useDashboardStore', () => ({
|
|
||||||
useDashboardStore: (
|
|
||||||
selector?: (s: Record<string, unknown>) => MockDashboardStoreState,
|
|
||||||
): MockDashboardStoreState => {
|
|
||||||
const state = {
|
|
||||||
selectedDashboard: { id: 'dash-1' },
|
|
||||||
setSelectedDashboard: mockSetSelectedDashboard,
|
|
||||||
updateLocalStorageDashboardVariables: mockUpdateLocalStorageDashboardVariables,
|
|
||||||
};
|
|
||||||
return selector ? selector(state) : state;
|
|
||||||
},
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Mock hooks/dashboard/useVariablesFromUrl
|
// Mock hooks/dashboard/useVariablesFromUrl
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
/* eslint-disable sonarjs/cognitive-complexity */
|
/* eslint-disable sonarjs/cognitive-complexity */
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useAddDynamicVariableToPanels } from 'hooks/dashboard/useAddDynamicVariableToPanels';
|
import { useAddDynamicVariableToPanels } from 'hooks/dashboard/useAddDynamicVariableToPanels';
|
||||||
import { updateLocalStorageDashboardVariable } from 'hooks/dashboard/useDashboardFromLocalStorage';
|
|
||||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||||
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { IDashboardVariables } from 'providers/Dashboard/store/dashboardVariables/dashboardVariablesStoreTypes';
|
import { IDashboardVariables } from 'providers/Dashboard/store/dashboardVariables/dashboardVariablesStoreTypes';
|
||||||
import { useDashboardStore } from 'providers/Dashboard/store/useDashboardStore';
|
|
||||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
|
||||||
|
|
||||||
import { convertVariablesToDbFormat } from './util';
|
import { convertVariablesToDbFormat } from './util';
|
||||||
|
|
||||||
@@ -39,16 +37,11 @@ interface UseDashboardVariableUpdateReturn {
|
|||||||
|
|
||||||
export const useDashboardVariableUpdate = (): UseDashboardVariableUpdateReturn => {
|
export const useDashboardVariableUpdate = (): UseDashboardVariableUpdateReturn => {
|
||||||
const {
|
const {
|
||||||
dashboardId,
|
|
||||||
selectedDashboard,
|
selectedDashboard,
|
||||||
setSelectedDashboard,
|
setSelectedDashboard,
|
||||||
} = useDashboardStore(
|
updateLocalStorageDashboardVariables,
|
||||||
useShallow((s) => ({
|
} = useDashboard();
|
||||||
dashboardId: s.selectedDashboard?.id ?? '',
|
|
||||||
selectedDashboard: s.selectedDashboard,
|
|
||||||
setSelectedDashboard: s.setSelectedDashboard,
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
const addDynamicVariableToPanels = useAddDynamicVariableToPanels();
|
const addDynamicVariableToPanels = useAddDynamicVariableToPanels();
|
||||||
const updateMutation = useUpdateDashboard();
|
const updateMutation = useUpdateDashboard();
|
||||||
|
|
||||||
@@ -66,13 +59,7 @@ export const useDashboardVariableUpdate = (): UseDashboardVariableUpdateReturn =
|
|||||||
// This makes localStorage much lighter and more efficient.
|
// This makes localStorage much lighter and more efficient.
|
||||||
// currently all the variables are dynamic
|
// currently all the variables are dynamic
|
||||||
const isDynamic = true;
|
const isDynamic = true;
|
||||||
updateLocalStorageDashboardVariable(
|
updateLocalStorageDashboardVariables(name, value, allSelected, isDynamic);
|
||||||
dashboardId,
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
allSelected,
|
|
||||||
isDynamic,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (selectedDashboard) {
|
if (selectedDashboard) {
|
||||||
setSelectedDashboard((prev) => {
|
setSelectedDashboard((prev) => {
|
||||||
@@ -110,7 +97,11 @@ export const useDashboardVariableUpdate = (): UseDashboardVariableUpdateReturn =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[dashboardId, selectedDashboard, setSelectedDashboard],
|
[
|
||||||
|
selectedDashboard,
|
||||||
|
setSelectedDashboard,
|
||||||
|
updateLocalStorageDashboardVariables,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const updateVariables = useCallback(
|
const updateVariables = useCallback(
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ const mockDashboard = {
|
|||||||
// Mock the dashboard provider with stable functions to prevent infinite loops
|
// Mock the dashboard provider with stable functions to prevent infinite loops
|
||||||
const mockSetSelectedDashboard = jest.fn();
|
const mockSetSelectedDashboard = jest.fn();
|
||||||
const mockUpdateLocalStorageDashboardVariables = jest.fn();
|
const mockUpdateLocalStorageDashboardVariables = jest.fn();
|
||||||
jest.mock('providers/Dashboard/store/useDashboardStore', () => ({
|
jest.mock('providers/Dashboard/Dashboard', () => ({
|
||||||
useDashboardStore: (): any => ({
|
useDashboard: (): any => ({
|
||||||
selectedDashboard: mockDashboard,
|
selectedDashboard: mockDashboard,
|
||||||
setSelectedDashboard: mockSetSelectedDashboard,
|
setSelectedDashboard: mockSetSelectedDashboard,
|
||||||
updateLocalStorageDashboardVariables: mockUpdateLocalStorageDashboardVariables,
|
updateLocalStorageDashboardVariables: mockUpdateLocalStorageDashboardVariables,
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ const mockDashboard = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
// Mock dependencies
|
// Mock dependencies
|
||||||
jest.mock('providers/Dashboard/store/useDashboardStore', () => ({
|
jest.mock('providers/Dashboard/Dashboard', () => ({
|
||||||
useDashboardStore: (): any => ({
|
useDashboard: (): any => ({
|
||||||
selectedDashboard: mockDashboard,
|
selectedDashboard: mockDashboard,
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
@@ -152,8 +152,8 @@ describe('Panel Management Tests', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Temporarily mock the dashboard
|
// Temporarily mock the dashboard
|
||||||
jest.doMock('providers/Dashboard/store/useDashboardStore', () => ({
|
jest.doMock('providers/Dashboard/Dashboard', () => ({
|
||||||
useDashboardStore: (): any => ({
|
useDashboard: (): any => ({
|
||||||
selectedDashboard: modifiedDashboard,
|
selectedDashboard: modifiedDashboard,
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import ROUTES from 'constants/routes';
|
|||||||
import { DASHBOARDS_LIST_QUERY_PARAMS_STORAGE_KEY } from 'hooks/dashboard/useDashboardsListQueryParams';
|
import { DASHBOARDS_LIST_QUERY_PARAMS_STORAGE_KEY } from 'hooks/dashboard/useDashboardsListQueryParams';
|
||||||
import { useSafeNavigate } from 'hooks/useSafeNavigate';
|
import { useSafeNavigate } from 'hooks/useSafeNavigate';
|
||||||
import { LayoutGrid } from 'lucide-react';
|
import { LayoutGrid } from 'lucide-react';
|
||||||
import { useDashboardStore } from 'providers/Dashboard/store/useDashboardStore';
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { DashboardData } from 'types/api/dashboard/getAll';
|
import { DashboardData } from 'types/api/dashboard/getAll';
|
||||||
|
|
||||||
import { Base64Icons } from '../../DashboardSettings/General/utils';
|
import { Base64Icons } from '../../DashboardSettings/General/utils';
|
||||||
@@ -13,7 +13,7 @@ import './DashboardBreadcrumbs.styles.scss';
|
|||||||
|
|
||||||
function DashboardBreadcrumbs(): JSX.Element {
|
function DashboardBreadcrumbs(): JSX.Element {
|
||||||
const { safeNavigate } = useSafeNavigate();
|
const { safeNavigate } = useSafeNavigate();
|
||||||
const { selectedDashboard } = useDashboardStore();
|
const { selectedDashboard } = useDashboard();
|
||||||
const updatedAtRef = useRef(selectedDashboard?.updatedAt);
|
const updatedAtRef = useRef(selectedDashboard?.updatedAt);
|
||||||
|
|
||||||
const selectedData = selectedDashboard
|
const selectedData = selectedDashboard
|
||||||
|
|||||||
@@ -6,10 +6,7 @@ import { useNotifications } from 'hooks/useNotifications';
|
|||||||
import { UPlotConfigBuilder } from 'lib/uPlotV2/config/UPlotConfigBuilder';
|
import { UPlotConfigBuilder } from 'lib/uPlotV2/config/UPlotConfigBuilder';
|
||||||
import { usePlotContext } from 'lib/uPlotV2/context/PlotContext';
|
import { usePlotContext } from 'lib/uPlotV2/context/PlotContext';
|
||||||
import useLegendsSync from 'lib/uPlotV2/hooks/useLegendsSync';
|
import useLegendsSync from 'lib/uPlotV2/hooks/useLegendsSync';
|
||||||
import {
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
selectIsDashboardLocked,
|
|
||||||
useDashboardStore,
|
|
||||||
} from 'providers/Dashboard/store/useDashboardStore';
|
|
||||||
|
|
||||||
import { getChartManagerColumns } from './getChartMangerColumns';
|
import { getChartManagerColumns } from './getChartMangerColumns';
|
||||||
import { ExtendedChartDataset, getDefaultTableDataSet } from './utils';
|
import { ExtendedChartDataset, getDefaultTableDataSet } from './utils';
|
||||||
@@ -53,7 +50,7 @@ export default function ChartManager({
|
|||||||
onToggleSeriesVisibility,
|
onToggleSeriesVisibility,
|
||||||
syncSeriesVisibilityToLocalStorage,
|
syncSeriesVisibilityToLocalStorage,
|
||||||
} = usePlotContext();
|
} = usePlotContext();
|
||||||
const isDashboardLocked = useDashboardStore(selectIsDashboardLocked);
|
const { isDashboardLocked } = useDashboard();
|
||||||
|
|
||||||
const [tableDataSet, setTableDataSet] = useState<ExtendedChartDataset[]>(() =>
|
const [tableDataSet, setTableDataSet] = useState<ExtendedChartDataset[]>(() =>
|
||||||
getDefaultTableDataSet(
|
getDefaultTableDataSet(
|
||||||
|
|||||||
@@ -32,18 +32,10 @@ jest.mock('lib/uPlotV2/hooks/useLegendsSync', () => ({
|
|||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('providers/Dashboard/store/useDashboardStore', () => ({
|
jest.mock('providers/Dashboard/Dashboard', () => ({
|
||||||
useDashboardStore: (
|
useDashboard: (): { isDashboardLocked: boolean } => ({
|
||||||
selector?: (s: {
|
isDashboardLocked: false,
|
||||||
selectedDashboard: { locked: boolean } | undefined;
|
}),
|
||||||
}) => { selectedDashboard: { locked: boolean } },
|
|
||||||
): { selectedDashboard: { locked: boolean } } => {
|
|
||||||
const mockState = { selectedDashboard: { locked: false } };
|
|
||||||
return selector ? selector(mockState) : mockState;
|
|
||||||
},
|
|
||||||
selectIsDashboardLocked: (s: {
|
|
||||||
selectedDashboard: { locked: boolean } | undefined;
|
|
||||||
}): boolean => s.selectedDashboard?.locked ?? false,
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('hooks/useNotifications', () => ({
|
jest.mock('hooks/useNotifications', () => ({
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ import { isUndefined } from 'lodash-es';
|
|||||||
import { urlKey } from 'pages/ErrorDetails/utils';
|
import { urlKey } from 'pages/ErrorDetails/utils';
|
||||||
import { useTimezone } from 'providers/Timezone';
|
import { useTimezone } from 'providers/Timezone';
|
||||||
import { PayloadProps as GetByErrorTypeAndServicePayload } from 'types/api/errors/getByErrorTypeAndService';
|
import { PayloadProps as GetByErrorTypeAndServicePayload } from 'types/api/errors/getByErrorTypeAndService';
|
||||||
import { isModifierKeyPressed } from 'utils/app';
|
|
||||||
import { openInNewTab } from 'utils/navigation';
|
|
||||||
|
|
||||||
import { keyToExclude } from './config';
|
import { keyToExclude } from './config';
|
||||||
import { DashedContainer, EditorContainer, EventContainer } from './styles';
|
import { DashedContainer, EditorContainer, EventContainer } from './styles';
|
||||||
@@ -113,19 +111,14 @@ function ErrorDetails(props: ErrorDetailsProps): JSX.Element {
|
|||||||
value: errorDetail[key as keyof GetByErrorTypeAndServicePayload],
|
value: errorDetail[key as keyof GetByErrorTypeAndServicePayload],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const onClickTraceHandler = (event: React.MouseEvent): void => {
|
const onClickTraceHandler = (): void => {
|
||||||
logEvent('Exception: Navigate to trace detail page', {
|
logEvent('Exception: Navigate to trace detail page', {
|
||||||
groupId: errorDetail?.groupID,
|
groupId: errorDetail?.groupID,
|
||||||
spanId: errorDetail.spanID,
|
spanId: errorDetail.spanID,
|
||||||
traceId: errorDetail.traceID,
|
traceId: errorDetail.traceID,
|
||||||
exceptionId: errorDetail?.errorId,
|
exceptionId: errorDetail?.errorId,
|
||||||
});
|
});
|
||||||
const path = `/trace/${errorDetail.traceID}?spanId=${errorDetail.spanID}`;
|
history.push(`/trace/${errorDetail.traceID}?spanId=${errorDetail.spanID}`);
|
||||||
if (isModifierKeyPressed(event)) {
|
|
||||||
openInNewTab(path);
|
|
||||||
} else {
|
|
||||||
history.push(path);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const logEventCalledRef = useRef(false);
|
const logEventCalledRef = useRef(false);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useQueryClient } from 'react-query';
|
import { useQueryClient } from 'react-query';
|
||||||
// eslint-disable-next-line no-restricted-imports
|
// eslint-disable-next-line no-restricted-imports
|
||||||
@@ -44,7 +44,6 @@ import { QueryFunction } from 'types/api/v5/queryRange';
|
|||||||
import { EQueryType } from 'types/common/dashboard';
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import { isModifierKeyPressed } from 'utils/app';
|
|
||||||
import { compositeQueryToQueryEnvelope } from 'utils/compositeQueryToQueryEnvelope';
|
import { compositeQueryToQueryEnvelope } from 'utils/compositeQueryToQueryEnvelope';
|
||||||
|
|
||||||
import BasicInfo from './BasicInfo';
|
import BasicInfo from './BasicInfo';
|
||||||
@@ -331,18 +330,13 @@ function FormAlertRules({
|
|||||||
}
|
}
|
||||||
}, [alertDef, currentQuery?.queryType, queryOptions]);
|
}, [alertDef, currentQuery?.queryType, queryOptions]);
|
||||||
|
|
||||||
const onCancelHandler = useCallback(
|
const onCancelHandler = useCallback(() => {
|
||||||
(e?: React.MouseEvent) => {
|
urlQuery.delete(QueryParams.compositeQuery);
|
||||||
urlQuery.delete(QueryParams.compositeQuery);
|
urlQuery.delete(QueryParams.panelTypes);
|
||||||
urlQuery.delete(QueryParams.panelTypes);
|
urlQuery.delete(QueryParams.ruleId);
|
||||||
urlQuery.delete(QueryParams.ruleId);
|
urlQuery.delete(QueryParams.relativeTime);
|
||||||
urlQuery.delete(QueryParams.relativeTime);
|
safeNavigate(`${ROUTES.LIST_ALL_ALERT}?${urlQuery.toString()}`);
|
||||||
safeNavigate(`${ROUTES.LIST_ALL_ALERT}?${urlQuery.toString()}`, {
|
}, [safeNavigate, urlQuery]);
|
||||||
newTab: !!e && isModifierKeyPressed(e),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[safeNavigate, urlQuery],
|
|
||||||
);
|
|
||||||
|
|
||||||
// onQueryCategoryChange handles changes to query category
|
// onQueryCategoryChange handles changes to query category
|
||||||
// in state as well as sets additional defaults
|
// in state as well as sets additional defaults
|
||||||
|
|||||||
@@ -464,10 +464,14 @@ function GeneralSettings({
|
|||||||
onModalToggleHandler(type);
|
onModalToggleHandler(type);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { isCloudUser: isCloudUserVal } = useGetTenantLicense();
|
const {
|
||||||
|
isCloudUser: isCloudUserVal,
|
||||||
|
isEnterpriseSelfHostedUser,
|
||||||
|
} = useGetTenantLicense();
|
||||||
|
|
||||||
const isAdmin = user.role === USER_ROLES.ADMIN;
|
const isAdmin = user.role === USER_ROLES.ADMIN;
|
||||||
const showCustomDomainSettings = isCloudUserVal && isAdmin;
|
const showCustomDomainSettings =
|
||||||
|
(isCloudUserVal || isEnterpriseSelfHostedUser) && isAdmin;
|
||||||
|
|
||||||
const renderConfig = [
|
const renderConfig = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ jest.mock('hooks/useComponentPermission', () => ({
|
|||||||
jest.mock('hooks/useGetTenantLicense', () => ({
|
jest.mock('hooks/useGetTenantLicense', () => ({
|
||||||
useGetTenantLicense: jest.fn(() => ({
|
useGetTenantLicense: jest.fn(() => ({
|
||||||
isCloudUser: false,
|
isCloudUser: false,
|
||||||
|
isEnterpriseSelfHostedUser: false,
|
||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -388,6 +389,7 @@ describe('GeneralSettings - S3 Logs Retention', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
(useGetTenantLicense as jest.Mock).mockReturnValue({
|
(useGetTenantLicense as jest.Mock).mockReturnValue({
|
||||||
isCloudUser: true,
|
isCloudUser: true,
|
||||||
|
isEnterpriseSelfHostedUser: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -409,14 +411,15 @@ describe('GeneralSettings - S3 Logs Retention', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Non-cloud user rendering', () => {
|
describe('Enterprise Self-Hosted User Rendering', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
(useGetTenantLicense as jest.Mock).mockReturnValue({
|
(useGetTenantLicense as jest.Mock).mockReturnValue({
|
||||||
isCloudUser: false,
|
isCloudUser: false,
|
||||||
|
isEnterpriseSelfHostedUser: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not render CustomDomainSettings or GeneralSettingsCloud', () => {
|
it('should render CustomDomainSettings but not GeneralSettingsCloud', () => {
|
||||||
render(
|
render(
|
||||||
<GeneralSettings
|
<GeneralSettings
|
||||||
metricsTtlValuesPayload={mockMetricsRetention}
|
metricsTtlValuesPayload={mockMetricsRetention}
|
||||||
@@ -429,14 +432,12 @@ describe('GeneralSettings - S3 Logs Retention', () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(screen.getByTestId('custom-domain-settings')).toBeInTheDocument();
|
||||||
screen.queryByTestId('custom-domain-settings'),
|
|
||||||
).not.toBeInTheDocument();
|
|
||||||
expect(
|
expect(
|
||||||
screen.queryByTestId('general-settings-cloud'),
|
screen.queryByTestId('general-settings-cloud'),
|
||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
|
|
||||||
// Save buttons should be visible for non-cloud users (these are from retentions)
|
// Save buttons should be visible for self-hosted
|
||||||
const saveButtons = screen.getAllByRole('button', { name: /save/i });
|
const saveButtons = screen.getAllByRole('button', { name: /save/i });
|
||||||
expect(saveButtons.length).toBeGreaterThan(0);
|
expect(saveButtons.length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,11 +8,8 @@ import { VariablesSettingsTab } from 'container/DashboardContainer/DashboardDesc
|
|||||||
import DashboardSettings from 'container/DashboardContainer/DashboardSettings';
|
import DashboardSettings from 'container/DashboardContainer/DashboardSettings';
|
||||||
import useComponentPermission from 'hooks/useComponentPermission';
|
import useComponentPermission from 'hooks/useComponentPermission';
|
||||||
import { useAppContext } from 'providers/App/App';
|
import { useAppContext } from 'providers/App/App';
|
||||||
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { usePanelTypeSelectionModalStore } from 'providers/Dashboard/helpers/panelTypeSelectionModalHelper';
|
import { usePanelTypeSelectionModalStore } from 'providers/Dashboard/helpers/panelTypeSelectionModalHelper';
|
||||||
import {
|
|
||||||
selectIsDashboardLocked,
|
|
||||||
useDashboardStore,
|
|
||||||
} from 'providers/Dashboard/store/useDashboardStore';
|
|
||||||
import { ROLES, USER_ROLES } from 'types/roles';
|
import { ROLES, USER_ROLES } from 'types/roles';
|
||||||
import { ComponentTypes } from 'utils/permission';
|
import { ComponentTypes } from 'utils/permission';
|
||||||
|
|
||||||
@@ -23,8 +20,7 @@ export default function DashboardEmptyState(): JSX.Element {
|
|||||||
(s) => s.setIsPanelTypeSelectionModalOpen,
|
(s) => s.setIsPanelTypeSelectionModalOpen,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { selectedDashboard } = useDashboardStore();
|
const { selectedDashboard, isDashboardLocked } = useDashboard();
|
||||||
const isDashboardLocked = useDashboardStore(selectIsDashboardLocked);
|
|
||||||
|
|
||||||
const variablesSettingsTabHandle = useRef<VariablesSettingsTab>(null);
|
const variablesSettingsTabHandle = useRef<VariablesSettingsTab>(null);
|
||||||
const [isSettingsDrawerOpen, setIsSettingsDrawerOpen] = useState<boolean>(
|
const [isSettingsDrawerOpen, setIsSettingsDrawerOpen] = useState<boolean>(
|
||||||
|
|||||||
@@ -3,10 +3,7 @@ import { Button, Input } from 'antd';
|
|||||||
import type { CheckboxChangeEvent } from 'antd/es/checkbox';
|
import type { CheckboxChangeEvent } from 'antd/es/checkbox';
|
||||||
import { ResizeTable } from 'components/ResizeTable';
|
import { ResizeTable } from 'components/ResizeTable';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import {
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
selectIsDashboardLocked,
|
|
||||||
useDashboardStore,
|
|
||||||
} from 'providers/Dashboard/store/useDashboardStore';
|
|
||||||
|
|
||||||
import { getGraphManagerTableColumns } from './TableRender/GraphManagerColumns';
|
import { getGraphManagerTableColumns } from './TableRender/GraphManagerColumns';
|
||||||
import { ExtendedChartDataset, GraphManagerProps } from './types';
|
import { ExtendedChartDataset, GraphManagerProps } from './types';
|
||||||
@@ -37,7 +34,7 @@ function GraphManager({
|
|||||||
}, [data, options]);
|
}, [data, options]);
|
||||||
|
|
||||||
const { notifications } = useNotifications();
|
const { notifications } = useNotifications();
|
||||||
const isDashboardLocked = useDashboardStore(selectIsDashboardLocked);
|
const { isDashboardLocked } = useDashboard();
|
||||||
|
|
||||||
const checkBoxOnChangeHandler = useCallback(
|
const checkBoxOnChangeHandler = useCallback(
|
||||||
(e: CheckboxChangeEvent, index: number): void => {
|
(e: CheckboxChangeEvent, index: number): void => {
|
||||||
|
|||||||
@@ -1,13 +1,7 @@
|
|||||||
/* eslint-disable sonarjs/cognitive-complexity */
|
/* eslint-disable sonarjs/cognitive-complexity */
|
||||||
import React, {
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
// eslint-disable-next-line no-restricted-imports
|
// eslint-disable-next-line no-restricted-imports
|
||||||
import { useSelector } from 'react-redux'; // old code, TODO: fix this correctly
|
import { useSelector } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
LoadingOutlined,
|
LoadingOutlined,
|
||||||
SearchOutlined,
|
SearchOutlined,
|
||||||
@@ -45,14 +39,10 @@ import { getDashboardVariables } from 'lib/dashboardVariables/getDashboardVariab
|
|||||||
import GetMinMax from 'lib/getMinMax';
|
import GetMinMax from 'lib/getMinMax';
|
||||||
import { isEmpty } from 'lodash-es';
|
import { isEmpty } from 'lodash-es';
|
||||||
import { useAppContext } from 'providers/App/App';
|
import { useAppContext } from 'providers/App/App';
|
||||||
import {
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
selectIsDashboardLocked,
|
|
||||||
useDashboardStore,
|
|
||||||
} from 'providers/Dashboard/store/useDashboardStore';
|
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { Warning } from 'types/api';
|
import { Warning } from 'types/api';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import { isModifierKeyPressed } from 'utils/app';
|
|
||||||
import { getGraphType } from 'utils/getGraphType';
|
import { getGraphType } from 'utils/getGraphType';
|
||||||
import { getSortedSeriesData } from 'utils/getSortedSeriesData';
|
import { getSortedSeriesData } from 'utils/getSortedSeriesData';
|
||||||
|
|
||||||
@@ -91,8 +81,11 @@ function FullView({
|
|||||||
setCurrentGraphRef(fullViewRef);
|
setCurrentGraphRef(fullViewRef);
|
||||||
}, [setCurrentGraphRef]);
|
}, [setCurrentGraphRef]);
|
||||||
|
|
||||||
const { selectedDashboard, setColumnWidths } = useDashboardStore();
|
const {
|
||||||
const isDashboardLocked = useDashboardStore(selectIsDashboardLocked);
|
selectedDashboard,
|
||||||
|
isDashboardLocked,
|
||||||
|
setColumnWidths,
|
||||||
|
} = useDashboard();
|
||||||
|
|
||||||
const onColumnWidthsChange = useCallback(
|
const onColumnWidthsChange = useCallback(
|
||||||
(widths: Record<string, number>) => {
|
(widths: Record<string, number>) => {
|
||||||
@@ -297,11 +290,9 @@ function FullView({
|
|||||||
<Button
|
<Button
|
||||||
className="switch-edit-btn"
|
className="switch-edit-btn"
|
||||||
disabled={response.isFetching || response.isLoading}
|
disabled={response.isFetching || response.isLoading}
|
||||||
onClick={(e: React.MouseEvent): void => {
|
onClick={(): void => {
|
||||||
if (dashboardEditView) {
|
if (dashboardEditView) {
|
||||||
safeNavigate(dashboardEditView, {
|
safeNavigate(dashboardEditView);
|
||||||
newTab: isModifierKeyPressed(e),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -161,8 +161,8 @@ const mockProps: WidgetGraphComponentProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Mock useDashabord hook
|
// Mock useDashabord hook
|
||||||
jest.mock('providers/Dashboard/store/useDashboardStore', () => ({
|
jest.mock('providers/Dashboard/Dashboard', () => ({
|
||||||
useDashboardStore: (): any => ({
|
useDashboard: (): any => ({
|
||||||
selectedDashboard: {
|
selectedDashboard: {
|
||||||
data: {
|
data: {
|
||||||
variables: [],
|
variables: [],
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import {
|
|||||||
getCustomTimeRangeWindowSweepInMS,
|
getCustomTimeRangeWindowSweepInMS,
|
||||||
getStartAndEndTimesInMilliseconds,
|
getStartAndEndTimesInMilliseconds,
|
||||||
} from 'pages/MessagingQueues/MessagingQueuesUtils';
|
} from 'pages/MessagingQueues/MessagingQueuesUtils';
|
||||||
import { useDashboardStore } from 'providers/Dashboard/store/useDashboardStore';
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
import { Props } from 'types/api/dashboard/update';
|
import { Props } from 'types/api/dashboard/update';
|
||||||
import { EQueryType } from 'types/common/dashboard';
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
@@ -106,7 +106,7 @@ function WidgetGraphComponent({
|
|||||||
selectedDashboard,
|
selectedDashboard,
|
||||||
setSelectedDashboard,
|
setSelectedDashboard,
|
||||||
setColumnWidths,
|
setColumnWidths,
|
||||||
} = useDashboardStore();
|
} = useDashboard();
|
||||||
|
|
||||||
const onColumnWidthsChange = useCallback(
|
const onColumnWidthsChange = useCallback(
|
||||||
(widths: Record<string, number>) => {
|
(widths: Record<string, number>) => {
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
import APIError from 'types/api/error';
|
|
||||||
|
|
||||||
import { errorDetails } from '../utils';
|
|
||||||
|
|
||||||
function makeAPIError(
|
|
||||||
message: string,
|
|
||||||
code = 'SOME_CODE',
|
|
||||||
errors: { message: string }[] = [],
|
|
||||||
): APIError {
|
|
||||||
return new APIError({
|
|
||||||
httpStatusCode: 500,
|
|
||||||
error: { code, message, url: '', errors },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('errorDetails', () => {
|
|
||||||
describe('when passed an APIError', () => {
|
|
||||||
it('returns the error message', () => {
|
|
||||||
const error = makeAPIError('something went wrong');
|
|
||||||
expect(errorDetails(error)).toBe('something went wrong');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('appends details when errors array is non-empty', () => {
|
|
||||||
const error = makeAPIError('query failed', 'QUERY_ERROR', [
|
|
||||||
{ message: 'field X is invalid' },
|
|
||||||
{ message: 'field Y is missing' },
|
|
||||||
]);
|
|
||||||
const result = errorDetails(error);
|
|
||||||
expect(result).toContain('query failed');
|
|
||||||
expect(result).toContain('field X is invalid');
|
|
||||||
expect(result).toContain('field Y is missing');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not append details when errors array is empty', () => {
|
|
||||||
const error = makeAPIError('simple error', 'CODE', []);
|
|
||||||
const result = errorDetails(error);
|
|
||||||
expect(result).toBe('simple error');
|
|
||||||
expect(result).not.toContain('Details');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when passed a plain Error (not an APIError)', () => {
|
|
||||||
it('does not throw', () => {
|
|
||||||
const error = new Error('timeout exceeded');
|
|
||||||
expect(() => errorDetails(error)).not.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns the plain error message', () => {
|
|
||||||
const error = new Error('timeout exceeded');
|
|
||||||
expect(errorDetails(error)).toBe('timeout exceeded');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns fallback when plain Error has no message', () => {
|
|
||||||
const error = new Error('');
|
|
||||||
expect(errorDetails(error)).toBe('Unknown error occurred');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('fallback behaviour', () => {
|
|
||||||
it('returns "Unknown error occurred" when message is undefined', () => {
|
|
||||||
const error = makeAPIError('');
|
|
||||||
expect(errorDetails(error)).toBe('Unknown error occurred');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import { memo, useEffect, useMemo, useRef, useState } from 'react';
|
import { memo, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
// eslint-disable-next-line no-restricted-imports
|
// eslint-disable-next-line no-restricted-imports
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import * as Sentry from '@sentry/react';
|
|
||||||
import logEvent from 'api/common/logEvent';
|
import logEvent from 'api/common/logEvent';
|
||||||
import { DEFAULT_ENTITY_VERSION, ENTITY_VERSION_V5 } from 'constants/app';
|
import { DEFAULT_ENTITY_VERSION, ENTITY_VERSION_V5 } from 'constants/app';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
@@ -18,6 +17,7 @@ import { getVariableReferencesInQuery } from 'lib/dashboardVariables/variableRef
|
|||||||
import getTimeString from 'lib/getTimeString';
|
import getTimeString from 'lib/getTimeString';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
import isEmpty from 'lodash-es/isEmpty';
|
import isEmpty from 'lodash-es/isEmpty';
|
||||||
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { UpdateTimeInterval } from 'store/actions';
|
import { UpdateTimeInterval } from 'store/actions';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import APIError from 'types/api/error';
|
import APIError from 'types/api/error';
|
||||||
@@ -68,19 +68,7 @@ function GridCardGraph({
|
|||||||
const [isInternalServerError, setIsInternalServerError] = useState<boolean>(
|
const [isInternalServerError, setIsInternalServerError] = useState<boolean>(
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
const queryRangeCalledRef = useRef(false);
|
const { setDashboardQueryRangeCalled } = useDashboard();
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const timeoutId = setTimeout(() => {
|
|
||||||
if (!queryRangeCalledRef.current) {
|
|
||||||
Sentry.captureEvent({
|
|
||||||
message: `Dashboard query range not called within expected timeframe for widget ${widget?.id}`,
|
|
||||||
level: 'warning',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, 120000);
|
|
||||||
return (): void => clearTimeout(timeoutId);
|
|
||||||
}, [widget?.id]);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
minTime,
|
minTime,
|
||||||
@@ -272,14 +260,14 @@ function GridCardGraph({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
queryRangeCalledRef.current = true;
|
setDashboardQueryRangeCalled(true);
|
||||||
},
|
},
|
||||||
onSettled: (data) => {
|
onSettled: (data) => {
|
||||||
dataAvailable?.(
|
dataAvailable?.(
|
||||||
isDataAvailableByPanelType(data?.payload?.data, widget?.panelTypes),
|
isDataAvailableByPanelType(data?.payload?.data, widget?.panelTypes),
|
||||||
);
|
);
|
||||||
getGraphData?.(data?.payload?.data);
|
getGraphData?.(data?.payload?.data);
|
||||||
queryRangeCalledRef.current = true;
|
setDashboardQueryRangeCalled(true);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -249,14 +249,13 @@ export const handleGraphClick = async ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const errorDetails = (error: APIError | Error): string => {
|
export const errorDetails = (error: APIError): string => {
|
||||||
const { message, errors } =
|
const { message, errors } = error.getErrorDetails()?.error || {};
|
||||||
(error instanceof APIError ? error.getErrorDetails()?.error : null) || {};
|
|
||||||
|
|
||||||
const details =
|
const details =
|
||||||
errors && errors.length > 0
|
errors?.length > 0
|
||||||
? `\n\nDetails: ${errors.map((e) => e.message).join('\n')}`
|
? `\n\nDetails: ${errors.map((e) => e.message).join('\n')}`
|
||||||
: '';
|
: '';
|
||||||
const errorDetails = `${message ?? error.message} ${details}`;
|
const errorDetails = `${message} ${details}`;
|
||||||
return errorDetails.trim() || 'Unknown error occurred';
|
return errorDetails || 'Unknown error occurred';
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { FullScreen, FullScreenHandle } from 'react-full-screen';
|
import { FullScreen, FullScreenHandle } from 'react-full-screen';
|
||||||
import { ItemCallback, Layout } from 'react-grid-layout';
|
import { ItemCallback, Layout } from 'react-grid-layout';
|
||||||
import { useIsFetching } from 'react-query';
|
|
||||||
// eslint-disable-next-line no-restricted-imports
|
// eslint-disable-next-line no-restricted-imports
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
import * as Sentry from '@sentry/react';
|
||||||
import { Color } from '@signozhq/design-tokens';
|
import { Color } from '@signozhq/design-tokens';
|
||||||
import { Button, Form, Input, Modal, Typography } from 'antd';
|
import { Button, Form, Input, Modal, Typography } from 'antd';
|
||||||
import logEvent from 'api/common/logEvent';
|
import logEvent from 'api/common/logEvent';
|
||||||
@@ -12,7 +12,6 @@ import cx from 'classnames';
|
|||||||
import { ENTITY_VERSION_V5 } from 'constants/app';
|
import { ENTITY_VERSION_V5 } from 'constants/app';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import { PANEL_GROUP_TYPES, PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_GROUP_TYPES, PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
|
||||||
import { themeColors } from 'constants/theme';
|
import { themeColors } from 'constants/theme';
|
||||||
import { DEFAULT_ROW_NAME } from 'container/DashboardContainer/DashboardDescription/utils';
|
import { DEFAULT_ROW_NAME } from 'container/DashboardContainer/DashboardDescription/utils';
|
||||||
import { useDashboardVariables } from 'hooks/dashboard/useDashboardVariables';
|
import { useDashboardVariables } from 'hooks/dashboard/useDashboardVariables';
|
||||||
@@ -32,10 +31,7 @@ import {
|
|||||||
X,
|
X,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useAppContext } from 'providers/App/App';
|
import { useAppContext } from 'providers/App/App';
|
||||||
import {
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
selectIsDashboardLocked,
|
|
||||||
useDashboardStore,
|
|
||||||
} from 'providers/Dashboard/store/useDashboardStore';
|
|
||||||
import { sortLayout } from 'providers/Dashboard/util';
|
import { sortLayout } from 'providers/Dashboard/util';
|
||||||
import { UpdateTimeInterval } from 'store/actions';
|
import { UpdateTimeInterval } from 'store/actions';
|
||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
@@ -48,7 +44,6 @@ import DashboardEmptyState from './DashboardEmptyState/DashboardEmptyState';
|
|||||||
import GridCard from './GridCard';
|
import GridCard from './GridCard';
|
||||||
import { Card, CardContainer, ReactGridLayout } from './styles';
|
import { Card, CardContainer, ReactGridLayout } from './styles';
|
||||||
import {
|
import {
|
||||||
applyRowCollapse,
|
|
||||||
hasColumnWidthsChanged,
|
hasColumnWidthsChanged,
|
||||||
removeUndefinedValuesFromLayout,
|
removeUndefinedValuesFromLayout,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
@@ -66,9 +61,6 @@ interface GraphLayoutProps {
|
|||||||
function GraphLayout(props: GraphLayoutProps): JSX.Element {
|
function GraphLayout(props: GraphLayoutProps): JSX.Element {
|
||||||
const { handle, enableDrillDown = false } = props;
|
const { handle, enableDrillDown = false } = props;
|
||||||
const { safeNavigate } = useSafeNavigate();
|
const { safeNavigate } = useSafeNavigate();
|
||||||
const isDashboardFetching =
|
|
||||||
useIsFetching([REACT_QUERY_KEY.DASHBOARD_BY_ID]) > 0;
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
selectedDashboard,
|
selectedDashboard,
|
||||||
layouts,
|
layouts,
|
||||||
@@ -76,9 +68,12 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
|
|||||||
panelMap,
|
panelMap,
|
||||||
setPanelMap,
|
setPanelMap,
|
||||||
setSelectedDashboard,
|
setSelectedDashboard,
|
||||||
|
isDashboardLocked,
|
||||||
|
dashboardQueryRangeCalled,
|
||||||
|
setDashboardQueryRangeCalled,
|
||||||
|
isDashboardFetching,
|
||||||
columnWidths,
|
columnWidths,
|
||||||
} = useDashboardStore();
|
} = useDashboard();
|
||||||
const isDashboardLocked = useDashboardStore(selectIsDashboardLocked);
|
|
||||||
const { data } = selectedDashboard || {};
|
const { data } = selectedDashboard || {};
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@@ -142,6 +137,25 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
|
|||||||
setDashboardLayout(sortLayout(layouts));
|
setDashboardLayout(sortLayout(layouts));
|
||||||
}, [layouts]);
|
}, [layouts]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setDashboardQueryRangeCalled(false);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
// Send Sentry event if query_range is not called within expected timeframe (2 mins) when there are widgets
|
||||||
|
if (!dashboardQueryRangeCalled && data?.widgets?.length) {
|
||||||
|
Sentry.captureEvent({
|
||||||
|
message: `Dashboard query range not called within expected timeframe even when there are ${data?.widgets?.length} widgets`,
|
||||||
|
level: 'warning',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 120000);
|
||||||
|
|
||||||
|
return (): void => clearTimeout(timeoutId);
|
||||||
|
}, [dashboardQueryRangeCalled, data?.widgets?.length]);
|
||||||
|
|
||||||
const logEventCalledRef = useRef(false);
|
const logEventCalledRef = useRef(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!logEventCalledRef.current && !isUndefined(data)) {
|
if (!logEventCalledRef.current && !isUndefined(data)) {
|
||||||
@@ -269,10 +283,13 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedWidgets = selectedDashboard?.data?.widgets?.map((e) =>
|
currentWidget.title = newTitle;
|
||||||
e.id === currentSelectRowId ? { ...e, title: newTitle } : e,
|
const updatedWidgets = selectedDashboard?.data?.widgets?.filter(
|
||||||
|
(e) => e.id !== currentSelectRowId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
updatedWidgets?.push(currentWidget);
|
||||||
|
|
||||||
const updatedSelectedDashboard: Props = {
|
const updatedSelectedDashboard: Props = {
|
||||||
id: selectedDashboard.id,
|
id: selectedDashboard.id,
|
||||||
data: {
|
data: {
|
||||||
@@ -314,13 +331,88 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
|
|||||||
if (!selectedDashboard) {
|
if (!selectedDashboard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { updatedLayout, updatedPanelMap } = applyRowCollapse(
|
const rowProperties = { ...currentPanelMap[id] };
|
||||||
id,
|
const updatedPanelMap = { ...currentPanelMap };
|
||||||
dashboardLayout,
|
|
||||||
currentPanelMap,
|
let updatedDashboardLayout = [...dashboardLayout];
|
||||||
);
|
if (rowProperties.collapsed === true) {
|
||||||
setCurrentPanelMap((prev) => ({ ...prev, ...updatedPanelMap }));
|
rowProperties.collapsed = false;
|
||||||
setDashboardLayout(sortLayout(updatedLayout));
|
const widgetsInsideTheRow = rowProperties.widgets;
|
||||||
|
let maxY = 0;
|
||||||
|
widgetsInsideTheRow.forEach((w) => {
|
||||||
|
maxY = Math.max(maxY, w.y + w.h);
|
||||||
|
});
|
||||||
|
const currentRowWidget = dashboardLayout.find((w) => w.i === id);
|
||||||
|
if (currentRowWidget && widgetsInsideTheRow.length) {
|
||||||
|
maxY -= currentRowWidget.h + currentRowWidget.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
const idxCurrentRow = dashboardLayout.findIndex((w) => w.i === id);
|
||||||
|
|
||||||
|
for (let j = idxCurrentRow + 1; j < dashboardLayout.length; j++) {
|
||||||
|
updatedDashboardLayout[j].y += maxY;
|
||||||
|
if (updatedPanelMap[updatedDashboardLayout[j].i]) {
|
||||||
|
updatedPanelMap[updatedDashboardLayout[j].i].widgets = updatedPanelMap[
|
||||||
|
updatedDashboardLayout[j].i
|
||||||
|
].widgets.map((w) => ({
|
||||||
|
...w,
|
||||||
|
y: w.y + maxY,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updatedDashboardLayout = [...updatedDashboardLayout, ...widgetsInsideTheRow];
|
||||||
|
} else {
|
||||||
|
rowProperties.collapsed = true;
|
||||||
|
const currentIdx = dashboardLayout.findIndex((w) => w.i === id);
|
||||||
|
|
||||||
|
let widgetsInsideTheRow: Layout[] = [];
|
||||||
|
let isPanelMapUpdated = false;
|
||||||
|
for (let j = currentIdx + 1; j < dashboardLayout.length; j++) {
|
||||||
|
if (currentPanelMap[dashboardLayout[j].i]) {
|
||||||
|
rowProperties.widgets = widgetsInsideTheRow;
|
||||||
|
widgetsInsideTheRow = [];
|
||||||
|
isPanelMapUpdated = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
widgetsInsideTheRow.push(dashboardLayout[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isPanelMapUpdated) {
|
||||||
|
rowProperties.widgets = widgetsInsideTheRow;
|
||||||
|
}
|
||||||
|
let maxY = 0;
|
||||||
|
widgetsInsideTheRow.forEach((w) => {
|
||||||
|
maxY = Math.max(maxY, w.y + w.h);
|
||||||
|
});
|
||||||
|
const currentRowWidget = dashboardLayout[currentIdx];
|
||||||
|
if (currentRowWidget && widgetsInsideTheRow.length) {
|
||||||
|
maxY -= currentRowWidget.h + currentRowWidget.y;
|
||||||
|
}
|
||||||
|
for (let j = currentIdx + 1; j < updatedDashboardLayout.length; j++) {
|
||||||
|
updatedDashboardLayout[j].y += maxY;
|
||||||
|
if (updatedPanelMap[updatedDashboardLayout[j].i]) {
|
||||||
|
updatedPanelMap[updatedDashboardLayout[j].i].widgets = updatedPanelMap[
|
||||||
|
updatedDashboardLayout[j].i
|
||||||
|
].widgets.map((w) => ({
|
||||||
|
...w,
|
||||||
|
y: w.y + maxY,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedDashboardLayout = updatedDashboardLayout.filter(
|
||||||
|
(widget) => !rowProperties.widgets.some((w: Layout) => w.i === widget.i),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
setCurrentPanelMap((prev) => ({
|
||||||
|
...prev,
|
||||||
|
...updatedPanelMap,
|
||||||
|
[id]: {
|
||||||
|
...rowProperties,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
setDashboardLayout(sortLayout(updatedDashboardLayout));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDragStop: ItemCallback = (_, oldItem, newItem): void => {
|
const handleDragStop: ItemCallback = (_, oldItem, newItem): void => {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user