mirror of
https://github.com/SigNoz/signoz.git
synced 2026-06-10 19:00:34 +01:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bac933968 | ||
|
|
8df70ed4be | ||
|
|
806f1d0062 | ||
|
|
19d7d5b2ad | ||
|
|
2f762a86b3 | ||
|
|
8861f4523c | ||
|
|
7f97249de3 | ||
|
|
da9b7b25ed | ||
|
|
0546050b82 | ||
|
|
b8de25e2eb | ||
|
|
8f77b5451c | ||
|
|
a55cab858d | ||
|
|
8f4a48ee51 | ||
|
|
c1d788f59e | ||
|
|
cd720beb7d | ||
|
|
63c7b24eac | ||
|
|
c5752829f7 | ||
|
|
404c10685d | ||
|
|
e7c83604de | ||
|
|
3b17f31948 | ||
|
|
ba8c83f47e | ||
|
|
ad2c46eebb | ||
|
|
f9110873f8 | ||
|
|
70bbfdd937 | ||
|
|
10a8db013b | ||
|
|
249d34a9dc | ||
|
|
9b5ad29794 | ||
|
|
90c2622da9 | ||
|
|
36b36fa80d | ||
|
|
5a547cf358 | ||
|
|
937c3f9359 | ||
|
|
a898225737 | ||
|
|
81f382e353 | ||
|
|
ef0ab2fe8e | ||
|
|
6c649d35cb | ||
|
|
d5c3fe1651 | ||
|
|
83c43ece31 | ||
|
|
a87654a614 | ||
|
|
952f5d6e91 | ||
|
|
2154ea30a6 | ||
|
|
6e3857b840 |
23
.github/CODEOWNERS
vendored
23
.github/CODEOWNERS
vendored
@@ -169,26 +169,3 @@ go.mod @therealpandey
|
||||
## Dashboard V2
|
||||
/frontend/src/pages/DashboardPageV2/ @SigNoz/pulse-frontend
|
||||
/frontend/src/pages/DashboardsListPageV2/ @SigNoz/pulse-frontend
|
||||
|
||||
## Infrastructure Monitoring
|
||||
/frontend/src/pages/InfrastructureMonitoring/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/InfraMonitoringHosts/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/InfraMonitoringK8s/ @SigNoz/pulse-frontend
|
||||
|
||||
## Alerts
|
||||
/frontend/src/pages/AlertList/ @SigNoz/pulse-frontend
|
||||
/frontend/src/pages/AlertDetails/ @SigNoz/pulse-frontend
|
||||
/frontend/src/pages/CreateAlert/ @SigNoz/pulse-frontend
|
||||
/frontend/src/pages/EditRules/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/AlertHistory/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/CreateAlertRule/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/CreateAlertV2/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/EditAlertV2/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/FormAlertRules/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/ListAlertRules/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/TriggeredAlerts/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/AnomalyAlertEvaluationView/ @SigNoz/pulse-frontend
|
||||
|
||||
## OpenAPI Schema - Generated
|
||||
/frontend/src/api/generated/services/ @therealpandey @vikrantgupta25 @srikanthccv
|
||||
/docs/api/openapi.yml @therealpandey @vikrantgupta25 @srikanthccv
|
||||
|
||||
2
.github/workflows/build-enterprise.yaml
vendored
2
.github/workflows/build-enterprise.yaml
vendored
@@ -69,8 +69,6 @@ jobs:
|
||||
echo 'VITE_APPCUES_APP_ID="${{ secrets.APPCUES_APP_ID }}"' >> frontend/.env
|
||||
echo 'VITE_PYLON_IDENTITY_SECRET="${{ secrets.PYLON_IDENTITY_SECRET }}"' >> frontend/.env
|
||||
echo 'VITE_DOCS_BASE_URL="https://signoz.io"' >> frontend/.env
|
||||
echo 'VITE_ENVIRONMENT="production"' >> frontend/.env
|
||||
echo 'VITE_VERSION="${{ steps.build-info.outputs.version }}"' >> frontend/.env
|
||||
- name: cache-dotenv
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
|
||||
6
.github/workflows/build-staging.yaml
vendored
6
.github/workflows/build-staging.yaml
vendored
@@ -64,18 +64,12 @@ jobs:
|
||||
run: |
|
||||
mkdir -p frontend
|
||||
echo 'CI=1' > frontend/.env
|
||||
echo 'VITE_SENTRY_AUTH_TOKEN="${{ secrets.SENTRY_AUTH_TOKEN }}"' >> frontend/.env
|
||||
echo 'VITE_SENTRY_ORG="${{ secrets.SENTRY_ORG }}"' >> frontend/.env
|
||||
echo 'VITE_SENTRY_PROJECT_ID="${{ secrets.SENTRY_PROJECT_ID }}"' >> frontend/.env
|
||||
echo 'VITE_SENTRY_DSN="${{ secrets.SENTRY_DSN }}"' >> frontend/.env
|
||||
echo 'VITE_TUNNEL_URL="${{ secrets.NP_TUNNEL_URL }}"' >> frontend/.env
|
||||
echo 'VITE_TUNNEL_DOMAIN="${{ secrets.NP_TUNNEL_DOMAIN }}"' >> frontend/.env
|
||||
echo 'VITE_PYLON_APP_ID="${{ secrets.NP_PYLON_APP_ID }}"' >> frontend/.env
|
||||
echo 'VITE_APPCUES_APP_ID="${{ secrets.NP_APPCUES_APP_ID }}"' >> frontend/.env
|
||||
echo 'VITE_PYLON_IDENTITY_SECRET="${{ secrets.NP_PYLON_IDENTITY_SECRET }}"' >> frontend/.env
|
||||
echo 'VITE_DOCS_BASE_URL="https://staging.signoz.io"' >> frontend/.env
|
||||
echo 'VITE_ENVIRONMENT="staging"' >> frontend/.env
|
||||
echo 'VITE_VERSION="${{ steps.build-info.outputs.version }}"' >> frontend/.env
|
||||
- name: cache-dotenv
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
|
||||
2
.github/workflows/gor-signoz.yaml
vendored
2
.github/workflows/gor-signoz.yaml
vendored
@@ -35,8 +35,6 @@ jobs:
|
||||
echo 'VITE_APPCUES_APP_ID="${{ secrets.APPCUES_APP_ID }}"' >> .env
|
||||
echo 'VITE_PYLON_IDENTITY_SECRET="${{ secrets.PYLON_IDENTITY_SECRET }}"' >> .env
|
||||
echo 'VITE_DOCS_BASE_URL="https://signoz.io"' >> .env
|
||||
echo 'VITE_ENVIRONMENT="production"' >> .env
|
||||
echo 'VITE_VERSION="${{ github.ref_name }}"' >> .env
|
||||
- name: node-setup
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
|
||||
5
.github/workflows/integrationci.yaml
vendored
5
.github/workflows/integrationci.yaml
vendored
@@ -39,12 +39,10 @@ jobs:
|
||||
matrix:
|
||||
suite:
|
||||
- alerts
|
||||
- basepath
|
||||
- callbackauthn
|
||||
- cloudintegrations
|
||||
- dashboard
|
||||
- ingestionkeys
|
||||
- inframonitoring
|
||||
- logspipelines
|
||||
- passwordauthn
|
||||
- preference
|
||||
@@ -54,7 +52,6 @@ jobs:
|
||||
- rootuser
|
||||
- serviceaccount
|
||||
- querier_json_body
|
||||
- querier_skip_resource_fingerprint
|
||||
- ttl
|
||||
sqlstore-provider:
|
||||
- postgres
|
||||
@@ -85,7 +82,7 @@ jobs:
|
||||
run: |
|
||||
cd tests && uv sync
|
||||
- name: webdriver
|
||||
if: matrix.suite == 'callbackauthn' || matrix.suite == 'basepath'
|
||||
if: matrix.suite == 'callbackauthn'
|
||||
run: |
|
||||
wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
|
||||
echo "deb http://dl.google.com/linux/chrome/deb/ stable main" | sudo tee -a /etc/apt/sources.list.d/google-chrome.list
|
||||
|
||||
@@ -91,7 +91,7 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
||||
sqlstoreProviderFactories(),
|
||||
signoz.NewTelemetryStoreProviderFactories(),
|
||||
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, config.Global)
|
||||
return signoz.NewAuthNs(ctx, providerSettings, store, licensing)
|
||||
},
|
||||
func(ctx context.Context, sqlstore sqlstore.SQLStore, config authz.Config, _ licensing.Licensing, _ []authz.OnBeforeRoleDelete) (factory.ProviderFactory[authz.AuthZ, authz.Config], error) {
|
||||
openfgaDataStore, err := openfgaserver.NewSQLStore(sqlstore, config)
|
||||
|
||||
@@ -107,17 +107,17 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
||||
sqlstoreProviderFactories(),
|
||||
signoz.NewTelemetryStoreProviderFactories(),
|
||||
func(ctx context.Context, providerSettings factory.ProviderSettings, store authtypes.AuthNStore, licensing licensing.Licensing) (map[authtypes.AuthNProvider]authn.AuthN, error) {
|
||||
samlCallbackAuthN, err := samlcallbackauthn.New(ctx, store, licensing, config.Global)
|
||||
samlCallbackAuthN, err := samlcallbackauthn.New(ctx, store, licensing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oidcCallbackAuthN, err := oidccallbackauthn.New(store, licensing, providerSettings, config.Global)
|
||||
oidcCallbackAuthN, err := oidccallbackauthn.New(store, licensing, providerSettings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authNs, err := signoz.NewAuthNs(ctx, providerSettings, store, licensing, config.Global)
|
||||
authNs, err := signoz.NewAuthNs(ctx, providerSettings, store, licensing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -64,16 +64,16 @@ web:
|
||||
settings:
|
||||
posthog:
|
||||
# Whether to enable PostHog in web.
|
||||
enabled: false
|
||||
enabled: true
|
||||
appcues:
|
||||
# Whether to enable Appcues in web.
|
||||
enabled: false
|
||||
enabled: true
|
||||
sentry:
|
||||
# Whether to enable Sentry in web.
|
||||
enabled: false
|
||||
enabled: true
|
||||
pylon:
|
||||
# Whether to enable Pylon in web.
|
||||
enabled: false
|
||||
enabled: true
|
||||
|
||||
##################### Cache #####################
|
||||
cache:
|
||||
@@ -432,7 +432,7 @@ cloudintegration:
|
||||
version: v0.0.8
|
||||
|
||||
##################### Trace Detail #####################
|
||||
traces:
|
||||
tracedetail:
|
||||
waterfall:
|
||||
# Number of spans returned per request when the trace is too large to show all at once.
|
||||
span_page_size: 500
|
||||
@@ -440,17 +440,6 @@ traces:
|
||||
max_depth_to_auto_expand: 5
|
||||
# Threshold below which all spans are returned without windowing.
|
||||
max_limit_to_select_all_spans: 10000
|
||||
flamegraph:
|
||||
# Maximum number of BFS depth levels included in a windowed response.
|
||||
max_selected_levels: 50
|
||||
# Maximum spans per level before sampling is applied.
|
||||
max_spans_per_level: 100
|
||||
# Number of highest-latency spans always included when sampling a level.
|
||||
sampling_top_latency_count: 5
|
||||
# Number of timestamp buckets used for uniform sampling within a level.
|
||||
sampling_bucket_count: 50
|
||||
# Threshold below which all spans are returned without windowing or sampling.
|
||||
select_all_spans_limit: 100000
|
||||
|
||||
##################### Authz #################################
|
||||
authz:
|
||||
|
||||
@@ -190,7 +190,7 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:v0.128.0
|
||||
image: signoz/signoz:v0.126.1
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
# - "6060:6060" # pprof port
|
||||
@@ -213,7 +213,7 @@ services:
|
||||
retries: 3
|
||||
otel-collector:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:v0.144.5
|
||||
image: signoz/signoz-otel-collector:v0.144.4
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
command:
|
||||
@@ -241,7 +241,7 @@ services:
|
||||
replicas: 3
|
||||
signoz-telemetrystore-migrator:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:v0.144.5
|
||||
image: signoz/signoz-otel-collector:v0.144.4
|
||||
environment:
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
|
||||
|
||||
@@ -117,7 +117,7 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:v0.128.0
|
||||
image: signoz/signoz:v0.126.1
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
volumes:
|
||||
@@ -139,7 +139,7 @@ services:
|
||||
retries: 3
|
||||
otel-collector:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:v0.144.5
|
||||
image: signoz/signoz-otel-collector:v0.144.4
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
command:
|
||||
@@ -167,7 +167,7 @@ services:
|
||||
replicas: 3
|
||||
signoz-telemetrystore-migrator:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:v0.144.5
|
||||
image: signoz/signoz-otel-collector:v0.144.4
|
||||
environment:
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
|
||||
|
||||
@@ -181,7 +181,7 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:${VERSION:-v0.128.0}
|
||||
image: signoz/signoz:${VERSION:-v0.126.1}
|
||||
container_name: signoz
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
@@ -204,7 +204,7 @@ services:
|
||||
retries: 3
|
||||
otel-collector:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.5}
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.4}
|
||||
container_name: signoz-otel-collector
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
@@ -229,7 +229,7 @@ services:
|
||||
- "4318:4318" # OTLP HTTP receiver
|
||||
signoz-telemetrystore-migrator:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.5}
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.4}
|
||||
container_name: signoz-telemetrystore-migrator
|
||||
environment:
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
|
||||
@@ -109,7 +109,7 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:${VERSION:-v0.128.0}
|
||||
image: signoz/signoz:${VERSION:-v0.126.1}
|
||||
container_name: signoz
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
@@ -132,7 +132,7 @@ services:
|
||||
retries: 3
|
||||
otel-collector:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.5}
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.4}
|
||||
container_name: signoz-otel-collector
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
@@ -157,7 +157,7 @@ services:
|
||||
- "4318:4318" # OTLP HTTP receiver
|
||||
signoz-telemetrystore-migrator:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.5}
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.4}
|
||||
container_name: signoz-telemetrystore-migrator
|
||||
environment:
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,12 +5,10 @@ import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/authn"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/global"
|
||||
"github.com/SigNoz/signoz/pkg/http/client"
|
||||
"github.com/SigNoz/signoz/pkg/licensing"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
@@ -28,14 +26,13 @@ var defaultScopes []string = []string{"email", "profile", oidc.ScopeOpenID}
|
||||
var _ authn.CallbackAuthN = (*AuthN)(nil)
|
||||
|
||||
type AuthN struct {
|
||||
settings factory.ScopedProviderSettings
|
||||
store authtypes.AuthNStore
|
||||
licensing licensing.Licensing
|
||||
httpClient *client.Client
|
||||
globalConfig global.Config
|
||||
settings factory.ScopedProviderSettings
|
||||
store authtypes.AuthNStore
|
||||
licensing licensing.Licensing
|
||||
httpClient *client.Client
|
||||
}
|
||||
|
||||
func New(store authtypes.AuthNStore, licensing licensing.Licensing, providerSettings factory.ProviderSettings, globalConfig global.Config) (*AuthN, error) {
|
||||
func New(store authtypes.AuthNStore, licensing licensing.Licensing, providerSettings factory.ProviderSettings) (*AuthN, error) {
|
||||
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/ee/authn/callbackauthn/oidccallbackauthn")
|
||||
|
||||
httpClient, err := client.New(providerSettings.Logger, providerSettings.TracerProvider, providerSettings.MeterProvider)
|
||||
@@ -44,11 +41,10 @@ func New(store authtypes.AuthNStore, licensing licensing.Licensing, providerSett
|
||||
}
|
||||
|
||||
return &AuthN{
|
||||
settings: settings,
|
||||
store: store,
|
||||
licensing: licensing,
|
||||
httpClient: httpClient,
|
||||
globalConfig: globalConfig,
|
||||
settings: settings,
|
||||
store: store,
|
||||
licensing: licensing,
|
||||
httpClient: httpClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -201,7 +197,7 @@ func (a *AuthN) oidcProviderAndoauth2Config(ctx context.Context, siteURL *url.UR
|
||||
RedirectURL: (&url.URL{
|
||||
Scheme: siteURL.Scheme,
|
||||
Host: siteURL.Host,
|
||||
Path: path.Join(a.globalConfig.ExternalPath(), redirectPath),
|
||||
Path: redirectPath,
|
||||
}).String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -6,12 +6,10 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/authn"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/global"
|
||||
"github.com/SigNoz/signoz/pkg/licensing"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
@@ -26,16 +24,14 @@ const (
|
||||
var _ authn.CallbackAuthN = (*AuthN)(nil)
|
||||
|
||||
type AuthN struct {
|
||||
store authtypes.AuthNStore
|
||||
licensing licensing.Licensing
|
||||
globalConfig global.Config
|
||||
store authtypes.AuthNStore
|
||||
licensing licensing.Licensing
|
||||
}
|
||||
|
||||
func New(ctx context.Context, store authtypes.AuthNStore, licensing licensing.Licensing, globalConfig global.Config) (*AuthN, error) {
|
||||
func New(ctx context.Context, store authtypes.AuthNStore, licensing licensing.Licensing) (*AuthN, error) {
|
||||
return &AuthN{
|
||||
store: store,
|
||||
licensing: licensing,
|
||||
globalConfig: globalConfig,
|
||||
store: store,
|
||||
licensing: licensing,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -136,7 +132,7 @@ func (a *AuthN) serviceProvider(siteURL *url.URL, authDomain *authtypes.AuthDoma
|
||||
return nil, err
|
||||
}
|
||||
|
||||
acsURL := &url.URL{Scheme: siteURL.Scheme, Host: siteURL.Host, Path: path.Join(a.globalConfig.ExternalPath(), redirectPath)}
|
||||
acsURL := &url.URL{Scheme: siteURL.Scheme, Host: siteURL.Host, Path: redirectPath}
|
||||
|
||||
// Note:
|
||||
// The ServiceProviderIssuer is the client id in case of keycloak. Since we set it to the host here, we need to set the client id == host in keycloak.
|
||||
|
||||
@@ -355,32 +355,26 @@ func (module *module) GetService(ctx context.Context, orgID valuer.UUID, service
|
||||
|
||||
var integrationService *cloudintegrationtypes.CloudIntegrationService
|
||||
|
||||
if cloudIntegrationID.IsZero() {
|
||||
return cloudintegrationtypes.NewService(provider, serviceDefinition, nil, nil), nil
|
||||
if !cloudIntegrationID.IsZero() {
|
||||
storedService, err := module.store.GetServiceByServiceID(ctx, cloudIntegrationID, serviceID)
|
||||
if err != nil && !errors.Ast(err, errors.TypeNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
if storedService != nil {
|
||||
serviceConfig, err := cloudintegrationtypes.NewServiceConfigFromJSON(provider, storedService.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
integrationService = cloudintegrationtypes.NewCloudIntegrationServiceFromStorable(storedService, serviceConfig)
|
||||
}
|
||||
|
||||
if err := module.enrichDashboardIDs(ctx, orgID, provider, serviceID, serviceDefinition); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
storedService, err := module.store.GetServiceByServiceID(ctx, cloudIntegrationID, serviceID)
|
||||
if err != nil && !errors.Ast(err, errors.TypeNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
if storedService == nil {
|
||||
return cloudintegrationtypes.NewService(provider, serviceDefinition, nil, nil), nil
|
||||
}
|
||||
|
||||
serviceConfig, err := cloudintegrationtypes.NewServiceConfigFromJSON(provider, storedService.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
integrationService = cloudintegrationtypes.NewCloudIntegrationServiceFromStorable(storedService, serviceConfig)
|
||||
|
||||
slugPrefix := cloudintegrationtypes.CloudIntegrationDashboardSlugPrefix(provider, serviceID)
|
||||
integrationDashboards, err := module.store.ListIntegrationDashboardsBySlugPrefix(ctx, orgID, cloudintegrationtypes.IntegrationDashboardProviderCloudIntegration, slugPrefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cloudintegrationtypes.NewService(provider, serviceDefinition, integrationService, integrationDashboards), nil
|
||||
return cloudintegrationtypes.NewService(*serviceDefinition, integrationService), nil
|
||||
}
|
||||
|
||||
func (module *module) CreateService(ctx context.Context, orgID valuer.UUID, createdBy string, creator valuer.UUID, service *cloudintegrationtypes.CloudIntegrationService, provider cloudintegrationtypes.CloudProviderType) error {
|
||||
@@ -589,3 +583,20 @@ func (module *module) deprovisionDashboards(ctx context.Context, orgID valuer.UU
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// enrichDashboardIDs replaces the raw dashboard name in each Dashboard.ID with the provisioned UUID.
|
||||
// TODO: remove this hack and send idiomatic response to client.
|
||||
func (module *module) enrichDashboardIDs(ctx context.Context, orgID valuer.UUID, provider cloudintegrationtypes.CloudProviderType, serviceID cloudintegrationtypes.ServiceID, serviceDefinition *cloudintegrationtypes.ServiceDefinition) error {
|
||||
for i, d := range serviceDefinition.Assets.Dashboards {
|
||||
slug := cloudintegrationtypes.CloudIntegrationDashboardSlug(provider, serviceID, d.ID)
|
||||
row, err := module.store.GetIntegrationDashboardBySlug(ctx, orgID, cloudintegrationtypes.IntegrationDashboardProviderCloudIntegration, slug)
|
||||
if err != nil {
|
||||
if errors.Ast(err, errors.TypeNotFound) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
serviceDefinition.Assets.Dashboards[i].ID = row.DashboardID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -221,18 +221,6 @@ func (module *module) GetV2(ctx context.Context, orgID valuer.UUID, id valuer.UU
|
||||
return module.pkgDashboardModule.GetV2(ctx, orgID, id)
|
||||
}
|
||||
|
||||
func (module *module) UpdateV2(ctx context.Context, orgID valuer.UUID, id valuer.UUID, updatedBy string, updatable dashboardtypes.UpdatableDashboardV2) (*dashboardtypes.DashboardV2, error) {
|
||||
return module.pkgDashboardModule.UpdateV2(ctx, orgID, id, updatedBy, updatable)
|
||||
}
|
||||
|
||||
func (module *module) PatchV2(ctx context.Context, orgID valuer.UUID, id valuer.UUID, updatedBy string, patch dashboardtypes.PatchableDashboardV2) (*dashboardtypes.DashboardV2, error) {
|
||||
return module.pkgDashboardModule.PatchV2(ctx, orgID, id, updatedBy, patch)
|
||||
}
|
||||
|
||||
func (module *module) LockUnlockV2(ctx context.Context, orgID valuer.UUID, id valuer.UUID, updatedBy string, isAdmin bool, lock bool) error {
|
||||
return module.pkgDashboardModule.LockUnlockV2(ctx, orgID, id, updatedBy, isAdmin, lock)
|
||||
}
|
||||
|
||||
func (module *module) Get(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*dashboardtypes.Dashboard, error) {
|
||||
return module.pkgDashboardModule.Get(ctx, orgID, id)
|
||||
}
|
||||
|
||||
28
frontend/__mocks__/useSafeNavigate.ts
Normal file
28
frontend/__mocks__/useSafeNavigate.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
// Mock for useSafeNavigate hook to avoid React Router version conflicts in tests
|
||||
interface SafeNavigateOptions {
|
||||
replace?: boolean;
|
||||
state?: unknown;
|
||||
newTab?: boolean;
|
||||
}
|
||||
|
||||
interface SafeNavigateTo {
|
||||
pathname?: string;
|
||||
search?: string;
|
||||
hash?: string;
|
||||
}
|
||||
|
||||
type SafeNavigateToType = string | SafeNavigateTo;
|
||||
|
||||
interface UseSafeNavigateReturn {
|
||||
safeNavigate: jest.MockedFunction<
|
||||
(to: SafeNavigateToType, options?: SafeNavigateOptions) => void
|
||||
>;
|
||||
}
|
||||
|
||||
export const useSafeNavigate = (): UseSafeNavigateReturn => ({
|
||||
safeNavigate: jest.fn(
|
||||
(_to: SafeNavigateToType, _options?: SafeNavigateOptions) => {},
|
||||
) as jest.MockedFunction<
|
||||
(to: SafeNavigateToType, options?: SafeNavigateOptions) => void
|
||||
>,
|
||||
});
|
||||
@@ -1,8 +1,6 @@
|
||||
import type { Config } from '@jest/types';
|
||||
|
||||
const USE_SAFE_NAVIGATE_MOCK_PATH =
|
||||
'<rootDir>/src/__tests__/safeNavigateMock.ts';
|
||||
const LOG_EVENT_MOCK_PATH = '<rootDir>/src/__tests__/logEventMock.ts';
|
||||
const USE_SAFE_NAVIGATE_MOCK_PATH = '<rootDir>/__mocks__/useSafeNavigate.ts';
|
||||
|
||||
const config: Config.InitialOptions = {
|
||||
silent: true,
|
||||
@@ -24,8 +22,6 @@ const config: Config.InitialOptions = {
|
||||
'^hooks/useSafeNavigate$': USE_SAFE_NAVIGATE_MOCK_PATH,
|
||||
'^src/hooks/useSafeNavigate$': USE_SAFE_NAVIGATE_MOCK_PATH,
|
||||
'^.*/useSafeNavigate$': USE_SAFE_NAVIGATE_MOCK_PATH,
|
||||
'^api/common/logEvent$': LOG_EVENT_MOCK_PATH,
|
||||
'^src/api/common/logEvent$': LOG_EVENT_MOCK_PATH,
|
||||
'^constants/env$': '<rootDir>/__mocks__/env.ts',
|
||||
'^src/constants/env$': '<rootDir>/__mocks__/env.ts',
|
||||
'^@signozhq/icons$': '<rootDir>/__mocks__/signozhqIconsMock.tsx',
|
||||
|
||||
@@ -41,15 +41,6 @@ if (typeof window.IntersectionObserver === 'undefined') {
|
||||
(window as any).IntersectionObserver = IntersectionObserverMock;
|
||||
}
|
||||
|
||||
if (typeof window.ResizeObserver === 'undefined') {
|
||||
class ResizeObserverMock {
|
||||
observe(): void {}
|
||||
unobserve(): void {}
|
||||
disconnect(): void {}
|
||||
}
|
||||
(window as any).ResizeObserver = ResizeObserverMock;
|
||||
}
|
||||
|
||||
// Patch getComputedStyle to handle CSS parsing errors from @signozhq/* packages.
|
||||
// These packages inject CSS at import time via style-inject / vite-plugin-css-injected-by-js.
|
||||
// jsdom's nwsapi cannot parse some of the injected selectors (e.g. Tailwind's :animate-in),
|
||||
|
||||
@@ -351,18 +351,19 @@ function App(): JSX.Element {
|
||||
Sentry.init({
|
||||
dsn: process.env.SENTRY_DSN,
|
||||
tunnel: process.env.TUNNEL_URL,
|
||||
environment: process.env.ENVIRONMENT,
|
||||
release: process.env.VERSION,
|
||||
environment: 'production',
|
||||
integrations: [
|
||||
// Kept for the `transaction` tag used in routing, even though
|
||||
// tracing is disabled. Ref: https://github.com/SigNoz/platform-pod/issues/2393#issuecomment-4603658055
|
||||
Sentry.browserTracingIntegration(),
|
||||
Sentry.replayIntegration({
|
||||
maskAllText: false,
|
||||
blockAllMedia: false,
|
||||
}),
|
||||
],
|
||||
tracesSampleRate: 0, // Ref: https://github.com/SigNoz/platform-pod/issues/2393#issuecomment-4603658055
|
||||
// Performance Monitoring
|
||||
tracesSampleRate: 1.0, // Capture 100% of the transactions
|
||||
// Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled
|
||||
tracePropagationTargets: [],
|
||||
// Session Replay
|
||||
replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
|
||||
replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
|
||||
beforeSend(event) {
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
// Shared mock for `api/common/logEvent`.
|
||||
// Wired into jest.config.ts moduleNameMapper, so any import of
|
||||
// `api/common/logEvent` in test code resolves to this file.
|
||||
// Tests can import `logEventMock` to assert analytics calls — Jest's
|
||||
// `clearMocks: true` resets call history between tests.
|
||||
|
||||
export const logEventMock: jest.MockedFunction<
|
||||
(eventName: string, attributes?: Record<string, unknown>) => void
|
||||
> = jest.fn();
|
||||
|
||||
export default logEventMock;
|
||||
@@ -1,29 +0,0 @@
|
||||
// Shared mock for `hooks/useSafeNavigate`.
|
||||
// Wired into jest.config.ts moduleNameMapper, so any import of
|
||||
// `hooks/useSafeNavigate` in test code resolves to this file.
|
||||
// Tests can import `safeNavigateMock` to assert navigation calls — Jest's
|
||||
// `clearMocks: true` resets call history between tests.
|
||||
|
||||
interface SafeNavigateOptions {
|
||||
replace?: boolean;
|
||||
state?: unknown;
|
||||
newTab?: boolean;
|
||||
}
|
||||
|
||||
interface SafeNavigateTo {
|
||||
pathname?: string;
|
||||
search?: string;
|
||||
hash?: string;
|
||||
}
|
||||
|
||||
type SafeNavigateToType = string | SafeNavigateTo;
|
||||
|
||||
export const safeNavigateMock: jest.MockedFunction<
|
||||
(to: SafeNavigateToType, options?: SafeNavigateOptions) => void
|
||||
> = jest.fn();
|
||||
|
||||
export const useSafeNavigate = (): {
|
||||
safeNavigate: typeof safeNavigateMock;
|
||||
} => ({
|
||||
safeNavigate: safeNavigateMock,
|
||||
});
|
||||
@@ -5,13 +5,6 @@ import convertObjectIntoParams from 'lib/query/convertObjectIntoParams';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/alerts/getTriggered';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useGetAlerts` hook (or `getAlerts` fetcher) from
|
||||
* `api/generated/services/alerts` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const getTriggered = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
|
||||
@@ -3,36 +3,13 @@ import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
|
||||
export interface DayBreakdownEntry {
|
||||
timestamp: number;
|
||||
total: number;
|
||||
quantity: number;
|
||||
count: number;
|
||||
size: number;
|
||||
}
|
||||
|
||||
export interface TierEntry {
|
||||
quantity: number;
|
||||
unitPrice: number;
|
||||
tierCost: number;
|
||||
}
|
||||
|
||||
export interface BreakdownEntry {
|
||||
type: string;
|
||||
unit: string;
|
||||
dayWiseBreakdown: {
|
||||
breakdown: DayBreakdownEntry[];
|
||||
};
|
||||
tiers?: TierEntry[];
|
||||
}
|
||||
|
||||
export interface UsageResponsePayloadProps {
|
||||
billingPeriodStart: number;
|
||||
billingPeriodEnd: number;
|
||||
billingPeriodStart: Date;
|
||||
billingPeriodEnd: Date;
|
||||
details: {
|
||||
total: number;
|
||||
baseFee: number;
|
||||
breakdown: BreakdownEntry[];
|
||||
breakdown: [];
|
||||
billTotal: number;
|
||||
};
|
||||
discount: number;
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createEmail';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useCreateChannel` hook (or `createChannel` fetcher) from
|
||||
* `api/generated/services/channels` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const create = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createMsTeams';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useCreateChannel` hook (or `createChannel` fetcher) from
|
||||
* `api/generated/services/channels` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const create = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createOpsgenie';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useCreateChannel` hook (or `createChannel` fetcher) from
|
||||
* `api/generated/services/channels` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const create = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createPager';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useCreateChannel` hook (or `createChannel` fetcher) from
|
||||
* `api/generated/services/channels` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const create = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createSlack';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useCreateChannel` hook (or `createChannel` fetcher) from
|
||||
* `api/generated/services/channels` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const create = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createWebhook';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useCreateChannel` hook (or `createChannel` fetcher) from
|
||||
* `api/generated/services/channels` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const create = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/delete';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useDeleteChannelByID` hook (or `deleteChannelByID` fetcher) from
|
||||
* `api/generated/services/channels` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const deleteChannel = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/editEmail';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useUpdateChannelByID` hook (or `updateChannelByID` fetcher) from
|
||||
* `api/generated/services/channels` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const editEmail = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/editMsTeams';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useUpdateChannelByID` hook (or `updateChannelByID` fetcher) from
|
||||
* `api/generated/services/channels` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const editMsTeams = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorResponse, ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/editOpsgenie';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useUpdateChannelByID` hook (or `updateChannelByID` fetcher) from
|
||||
* `api/generated/services/channels` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const editOpsgenie = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps> | ErrorResponse> => {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/editPager';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useUpdateChannelByID` hook (or `updateChannelByID` fetcher) from
|
||||
* `api/generated/services/channels` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const editPager = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/editSlack';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useUpdateChannelByID` hook (or `updateChannelByID` fetcher) from
|
||||
* `api/generated/services/channels` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const editSlack = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/editWebhook';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useUpdateChannelByID` hook (or `updateChannelByID` fetcher) from
|
||||
* `api/generated/services/channels` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const editWebhook = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
|
||||
@@ -5,13 +5,6 @@ import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/get';
|
||||
import { Channels } from 'types/api/channels/getAll';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useGetChannelByID` hook (or `getChannelByID` fetcher) from
|
||||
* `api/generated/services/channels` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const get = async (props: Props): Promise<SuccessResponseV2<Channels>> => {
|
||||
try {
|
||||
const response = await axios.get<PayloadProps>(`/channels/${props.id}`);
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { Channels, PayloadProps } from 'types/api/channels/getAll';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useListChannels` hook (or `listChannels` fetcher) from
|
||||
* `api/generated/services/channels` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const getAll = async (): Promise<SuccessResponseV2<Channels[]>> => {
|
||||
try {
|
||||
const response = await axios.get<PayloadProps>('/channels');
|
||||
|
||||
@@ -5,13 +5,6 @@ import { DEFAULT_TIME_RANGE } from 'container/TopNav/DateTimeSelectionV2/constan
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { CreatePublicDashboardProps } from 'types/api/dashboard/public/create';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useCreatePublicDashboard` hook (or `createPublicDashboard` fetcher) from
|
||||
* `api/generated/services/dashboard` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const createPublicDashboard = async (
|
||||
props: CreatePublicDashboardProps,
|
||||
): Promise<SuccessResponseV2<CreatePublicDashboardProps>> => {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { GetPublicDashboardDataProps, PayloadProps,PublicDashboardDataProps } from 'types/api/dashboard/public/get';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useGetPublicDashboardData` hook (or `getPublicDashboardData` fetcher) from
|
||||
* `api/generated/services/dashboard` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const getPublicDashboardData = async (props: GetPublicDashboardDataProps): Promise<SuccessResponseV2<PublicDashboardDataProps>> => {
|
||||
try {
|
||||
const response = await axios.get<PayloadProps>(`/public/dashboards/${props.id}`);
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { GetPublicDashboardMetaProps, PayloadProps,PublicDashboardMetaProps } from 'types/api/dashboard/public/getMeta';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useGetPublicDashboard` hook (or `getPublicDashboard` fetcher) from
|
||||
* `api/generated/services/dashboard` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const getPublicDashboardMeta = async (props: GetPublicDashboardMetaProps): Promise<SuccessResponseV2<PublicDashboardMetaProps>> => {
|
||||
try {
|
||||
const response = await axios.get<PayloadProps>(`/dashboards/${props.id}/public`);
|
||||
|
||||
@@ -6,13 +6,6 @@ import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { GetPublicDashboardWidgetDataProps } from 'types/api/dashboard/public/getWidgetData';
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useGetPublicDashboardWidgetQueryRange` hook (or `getPublicDashboardWidgetQueryRange` fetcher) from
|
||||
* `api/generated/services/dashboard` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const getPublicDashboardWidgetData = async (props: GetPublicDashboardWidgetDataProps): Promise<SuccessResponseV2<MetricRangePayloadV5>> => {
|
||||
try {
|
||||
const response = await axios.get(`/public/dashboards/${props.id}/widgets/${props.index}/query_range`, {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps,RevokePublicDashboardAccessProps } from 'types/api/dashboard/public/delete';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useDeletePublicDashboard` hook (or `deletePublicDashboard` fetcher) from
|
||||
* `api/generated/services/dashboard` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const revokePublicDashboardAccess = async (
|
||||
props: RevokePublicDashboardAccessProps,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
|
||||
@@ -5,13 +5,6 @@ import { DEFAULT_TIME_RANGE } from 'container/TopNav/DateTimeSelectionV2/constan
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { UpdatePublicDashboardProps } from 'types/api/dashboard/public/update';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useUpdatePublicDashboard` hook (or `updatePublicDashboard` fetcher) from
|
||||
* `api/generated/services/dashboard` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const updatePublicDashboard = async (
|
||||
props: UpdatePublicDashboardProps,
|
||||
): Promise<SuccessResponseV2<UpdatePublicDashboardProps>> => {
|
||||
|
||||
@@ -9,13 +9,6 @@ interface ISubstituteVars {
|
||||
compositeQuery: ICompositeMetricQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useReplaceVariables` hook (or `replaceVariables` fetcher) from
|
||||
* `api/generated/services/querier` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
export const getSubstituteVars = async (
|
||||
props?: Partial<QueryRangePayloadV5>,
|
||||
signal?: AbortSignal,
|
||||
|
||||
@@ -8,12 +8,6 @@ import { FieldKeyResponse } from 'types/api/dynamicVariables/getFieldKeys';
|
||||
* Get field keys for a given signal type
|
||||
* @param signal Type of signal (traces, logs, metrics)
|
||||
* @param name Optional search text
|
||||
*
|
||||
* @deprecated Use the generated `useGetFieldsKeys` hook (or `getFieldsKeys` fetcher) from
|
||||
* `api/generated/services/fields` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
export const getFieldKeys = async (
|
||||
signal?: 'traces' | 'logs' | 'metrics',
|
||||
|
||||
@@ -11,12 +11,6 @@ import { FieldValueResponse } from 'types/api/dynamicVariables/getFieldValues';
|
||||
* @param name Name of the attribute for which values are being fetched
|
||||
* @param value Optional search text
|
||||
* @param existingQuery Optional existing query - across all present dynamic variables
|
||||
*
|
||||
* @deprecated Use the generated `useGetFieldsValues` hook (or `getFieldsValues` fetcher) from
|
||||
* `api/generated/services/fields` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
export const getFieldValues = async (
|
||||
signal?: 'traces' | 'logs' | 'metrics',
|
||||
|
||||
@@ -19,7 +19,7 @@ import type {
|
||||
|
||||
import type {
|
||||
AlertmanagertypesPostableChannelDTO,
|
||||
AlertmanagertypesReceiverDTO,
|
||||
ConfigReceiverDTO,
|
||||
CreateChannel201,
|
||||
DeleteChannelByIDPathParameters,
|
||||
GetChannelByID200,
|
||||
@@ -385,14 +385,14 @@ export const invalidateGetChannelByID = async (
|
||||
*/
|
||||
export const updateChannelByID = (
|
||||
{ id }: UpdateChannelByIDPathParameters,
|
||||
alertmanagertypesReceiverDTO?: BodyType<AlertmanagertypesReceiverDTO>,
|
||||
configReceiverDTO?: BodyType<ConfigReceiverDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v1/channels/${id}`,
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: alertmanagertypesReceiverDTO,
|
||||
data: configReceiverDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
@@ -406,7 +406,7 @@ export const getUpdateChannelByIDMutationOptions = <
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateChannelByIDPathParameters;
|
||||
data?: BodyType<AlertmanagertypesReceiverDTO>;
|
||||
data?: BodyType<ConfigReceiverDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
@@ -415,7 +415,7 @@ export const getUpdateChannelByIDMutationOptions = <
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateChannelByIDPathParameters;
|
||||
data?: BodyType<AlertmanagertypesReceiverDTO>;
|
||||
data?: BodyType<ConfigReceiverDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
@@ -432,7 +432,7 @@ export const getUpdateChannelByIDMutationOptions = <
|
||||
Awaited<ReturnType<typeof updateChannelByID>>,
|
||||
{
|
||||
pathParams: UpdateChannelByIDPathParameters;
|
||||
data?: BodyType<AlertmanagertypesReceiverDTO>;
|
||||
data?: BodyType<ConfigReceiverDTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
@@ -447,7 +447,7 @@ export type UpdateChannelByIDMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof updateChannelByID>>
|
||||
>;
|
||||
export type UpdateChannelByIDMutationBody =
|
||||
| BodyType<AlertmanagertypesReceiverDTO>
|
||||
| BodyType<ConfigReceiverDTO>
|
||||
| undefined;
|
||||
export type UpdateChannelByIDMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
@@ -463,7 +463,7 @@ export const useUpdateChannelByID = <
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateChannelByIDPathParameters;
|
||||
data?: BodyType<AlertmanagertypesReceiverDTO>;
|
||||
data?: BodyType<ConfigReceiverDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
@@ -472,7 +472,7 @@ export const useUpdateChannelByID = <
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateChannelByIDPathParameters;
|
||||
data?: BodyType<AlertmanagertypesReceiverDTO>;
|
||||
data?: BodyType<ConfigReceiverDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
@@ -483,14 +483,14 @@ export const useUpdateChannelByID = <
|
||||
* @summary Test notification channel
|
||||
*/
|
||||
export const testChannel = (
|
||||
alertmanagertypesReceiverDTO?: BodyType<AlertmanagertypesReceiverDTO>,
|
||||
configReceiverDTO?: BodyType<ConfigReceiverDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v1/channels/test`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: alertmanagertypesReceiverDTO,
|
||||
data: configReceiverDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
@@ -502,13 +502,13 @@ export const getTestChannelMutationOptions = <
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof testChannel>>,
|
||||
TError,
|
||||
{ data?: BodyType<AlertmanagertypesReceiverDTO> },
|
||||
{ data?: BodyType<ConfigReceiverDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof testChannel>>,
|
||||
TError,
|
||||
{ data?: BodyType<AlertmanagertypesReceiverDTO> },
|
||||
{ data?: BodyType<ConfigReceiverDTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['testChannel'];
|
||||
@@ -522,7 +522,7 @@ export const getTestChannelMutationOptions = <
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof testChannel>>,
|
||||
{ data?: BodyType<AlertmanagertypesReceiverDTO> }
|
||||
{ data?: BodyType<ConfigReceiverDTO> }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
@@ -535,9 +535,7 @@ export const getTestChannelMutationOptions = <
|
||||
export type TestChannelMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof testChannel>>
|
||||
>;
|
||||
export type TestChannelMutationBody =
|
||||
| BodyType<AlertmanagertypesReceiverDTO>
|
||||
| undefined;
|
||||
export type TestChannelMutationBody = BodyType<ConfigReceiverDTO> | undefined;
|
||||
export type TestChannelMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
@@ -550,13 +548,13 @@ export const useTestChannel = <
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof testChannel>>,
|
||||
TError,
|
||||
{ data?: BodyType<AlertmanagertypesReceiverDTO> },
|
||||
{ data?: BodyType<ConfigReceiverDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof testChannel>>,
|
||||
TError,
|
||||
{ data?: BodyType<AlertmanagertypesReceiverDTO> },
|
||||
{ data?: BodyType<ConfigReceiverDTO> },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getTestChannelMutationOptions(options));
|
||||
@@ -567,14 +565,14 @@ export const useTestChannel = <
|
||||
* @summary Test notification channel (deprecated)
|
||||
*/
|
||||
export const testChannelDeprecated = (
|
||||
alertmanagertypesReceiverDTO?: BodyType<AlertmanagertypesReceiverDTO>,
|
||||
configReceiverDTO?: BodyType<ConfigReceiverDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v1/testChannel`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: alertmanagertypesReceiverDTO,
|
||||
data: configReceiverDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
@@ -586,13 +584,13 @@ export const getTestChannelDeprecatedMutationOptions = <
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof testChannelDeprecated>>,
|
||||
TError,
|
||||
{ data?: BodyType<AlertmanagertypesReceiverDTO> },
|
||||
{ data?: BodyType<ConfigReceiverDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof testChannelDeprecated>>,
|
||||
TError,
|
||||
{ data?: BodyType<AlertmanagertypesReceiverDTO> },
|
||||
{ data?: BodyType<ConfigReceiverDTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['testChannelDeprecated'];
|
||||
@@ -606,7 +604,7 @@ export const getTestChannelDeprecatedMutationOptions = <
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof testChannelDeprecated>>,
|
||||
{ data?: BodyType<AlertmanagertypesReceiverDTO> }
|
||||
{ data?: BodyType<ConfigReceiverDTO> }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
@@ -620,7 +618,7 @@ export type TestChannelDeprecatedMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof testChannelDeprecated>>
|
||||
>;
|
||||
export type TestChannelDeprecatedMutationBody =
|
||||
| BodyType<AlertmanagertypesReceiverDTO>
|
||||
| BodyType<ConfigReceiverDTO>
|
||||
| undefined;
|
||||
export type TestChannelDeprecatedMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
@@ -636,13 +634,13 @@ export const useTestChannelDeprecated = <
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof testChannelDeprecated>>,
|
||||
TError,
|
||||
{ data?: BodyType<AlertmanagertypesReceiverDTO> },
|
||||
{ data?: BodyType<ConfigReceiverDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof testChannelDeprecated>>,
|
||||
TError,
|
||||
{ data?: BodyType<AlertmanagertypesReceiverDTO> },
|
||||
{ data?: BodyType<ConfigReceiverDTO> },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getTestChannelDeprecatedMutationOptions(options));
|
||||
|
||||
@@ -31,15 +31,11 @@ import type {
|
||||
DisconnectAccountPathParameters,
|
||||
GetAccount200,
|
||||
GetAccountPathParameters,
|
||||
GetAccountService200,
|
||||
GetAccountServicePathParameters,
|
||||
GetConnectionCredentials200,
|
||||
GetConnectionCredentialsPathParameters,
|
||||
GetService200,
|
||||
GetServiceParams,
|
||||
GetServicePathParameters,
|
||||
ListAccountServicesMetadata200,
|
||||
ListAccountServicesMetadataPathParameters,
|
||||
ListAccounts200,
|
||||
ListAccountsPathParameters,
|
||||
ListServicesMetadata200,
|
||||
@@ -635,227 +631,6 @@ export const useUpdateAccount = <
|
||||
> => {
|
||||
return useMutation(getUpdateAccountMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint lists the services metadata for the specified account and cloud provider
|
||||
* @summary List account services metadata
|
||||
*/
|
||||
export const listAccountServicesMetadata = (
|
||||
{ cloudProvider, id }: ListAccountServicesMetadataPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<ListAccountServicesMetadata200>({
|
||||
url: `/api/v1/cloud_integrations/${cloudProvider}/accounts/${id}/services`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getListAccountServicesMetadataQueryKey = ({
|
||||
cloudProvider,
|
||||
id,
|
||||
}: ListAccountServicesMetadataPathParameters) => {
|
||||
return [
|
||||
`/api/v1/cloud_integrations/${cloudProvider}/accounts/${id}/services`,
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const getListAccountServicesMetadataQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof listAccountServicesMetadata>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ cloudProvider, id }: ListAccountServicesMetadataPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listAccountServicesMetadata>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ??
|
||||
getListAccountServicesMetadataQueryKey({ cloudProvider, id });
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof listAccountServicesMetadata>>
|
||||
> = ({ signal }) => listAccountServicesMetadata({ cloudProvider, id }, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!(cloudProvider && id),
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listAccountServicesMetadata>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type ListAccountServicesMetadataQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof listAccountServicesMetadata>>
|
||||
>;
|
||||
export type ListAccountServicesMetadataQueryError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary List account services metadata
|
||||
*/
|
||||
|
||||
export function useListAccountServicesMetadata<
|
||||
TData = Awaited<ReturnType<typeof listAccountServicesMetadata>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ cloudProvider, id }: ListAccountServicesMetadataPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listAccountServicesMetadata>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getListAccountServicesMetadataQueryOptions(
|
||||
{ cloudProvider, id },
|
||||
options,
|
||||
);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary List account services metadata
|
||||
*/
|
||||
export const invalidateListAccountServicesMetadata = async (
|
||||
queryClient: QueryClient,
|
||||
{ cloudProvider, id }: ListAccountServicesMetadataPathParameters,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getListAccountServicesMetadataQueryKey({ cloudProvider, id }) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint gets a service and its configuration for the specified cloud integration account
|
||||
* @summary Get service for account
|
||||
*/
|
||||
export const getAccountService = (
|
||||
{ cloudProvider, id, serviceId }: GetAccountServicePathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetAccountService200>({
|
||||
url: `/api/v1/cloud_integrations/${cloudProvider}/accounts/${id}/services/${serviceId}`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetAccountServiceQueryKey = ({
|
||||
cloudProvider,
|
||||
id,
|
||||
serviceId,
|
||||
}: GetAccountServicePathParameters) => {
|
||||
return [
|
||||
`/api/v1/cloud_integrations/${cloudProvider}/accounts/${id}/services/${serviceId}`,
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const getGetAccountServiceQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getAccountService>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ cloudProvider, id, serviceId }: GetAccountServicePathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getAccountService>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ??
|
||||
getGetAccountServiceQueryKey({ cloudProvider, id, serviceId });
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getAccountService>>
|
||||
> = ({ signal }) =>
|
||||
getAccountService({ cloudProvider, id, serviceId }, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!(cloudProvider && id && serviceId),
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getAccountService>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetAccountServiceQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getAccountService>>
|
||||
>;
|
||||
export type GetAccountServiceQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get service for account
|
||||
*/
|
||||
|
||||
export function useGetAccountService<
|
||||
TData = Awaited<ReturnType<typeof getAccountService>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ cloudProvider, id, serviceId }: GetAccountServicePathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getAccountService>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetAccountServiceQueryOptions(
|
||||
{ cloudProvider, id, serviceId },
|
||||
options,
|
||||
);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get service for account
|
||||
*/
|
||||
export const invalidateGetAccountService = async (
|
||||
queryClient: QueryClient,
|
||||
{ cloudProvider, id, serviceId }: GetAccountServicePathParameters,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetAccountServiceQueryKey({ cloudProvider, id, serviceId }) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint updates a service for the specified cloud provider
|
||||
* @summary Update service
|
||||
|
||||
@@ -21,10 +21,8 @@ import type {
|
||||
CreateDashboardV2201,
|
||||
CreatePublicDashboard201,
|
||||
CreatePublicDashboardPathParameters,
|
||||
DashboardtypesPatchableDashboardV2DTO,
|
||||
DashboardtypesPostableDashboardV2DTO,
|
||||
DashboardtypesPostablePublicDashboardDTO,
|
||||
DashboardtypesUpdatableDashboardV2DTO,
|
||||
DashboardtypesUpdatablePublicDashboardDTO,
|
||||
DeletePublicDashboardPathParameters,
|
||||
GetDashboardV2200,
|
||||
@@ -35,13 +33,7 @@ import type {
|
||||
GetPublicDashboardPathParameters,
|
||||
GetPublicDashboardWidgetQueryRange200,
|
||||
GetPublicDashboardWidgetQueryRangePathParameters,
|
||||
LockDashboardV2PathParameters,
|
||||
PatchDashboardV2200,
|
||||
PatchDashboardV2PathParameters,
|
||||
RenderErrorResponseDTO,
|
||||
UnlockDashboardV2PathParameters,
|
||||
UpdateDashboardV2200,
|
||||
UpdateDashboardV2PathParameters,
|
||||
UpdatePublicDashboardPathParameters,
|
||||
} from '../sigNoz.schemas';
|
||||
|
||||
@@ -824,360 +816,3 @@ export const invalidateGetDashboardV2 = async (
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint applies an RFC 6902 JSON Patch to a v2-shape dashboard. The patch is applied against the postable view of the dashboard (metadata, data, tags), so individual panels, queries, variables, layouts, or tags can be updated without re-sending the rest of the dashboard. Apply is lenient: `remove` on a missing path is a no-op (idempotent) and `add` creates any missing parent objects, rather than failing as strict RFC 6902 would. The resulting dashboard is still validated. Locked dashboards are rejected.
|
||||
* @summary Patch dashboard (v2)
|
||||
*/
|
||||
export const patchDashboardV2 = (
|
||||
{ id }: PatchDashboardV2PathParameters,
|
||||
dashboardtypesPatchableDashboardV2DTONull?: BodyType<DashboardtypesPatchableDashboardV2DTO | null> | null,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<PatchDashboardV2200>({
|
||||
url: `/api/v2/dashboards/${id}`,
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: dashboardtypesPatchableDashboardV2DTONull,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getPatchDashboardV2MutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof patchDashboardV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: PatchDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesPatchableDashboardV2DTO | null>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof patchDashboardV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: PatchDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesPatchableDashboardV2DTO | null>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['patchDashboardV2'];
|
||||
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 patchDashboardV2>>,
|
||||
{
|
||||
pathParams: PatchDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesPatchableDashboardV2DTO | null>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
|
||||
return patchDashboardV2(pathParams, data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type PatchDashboardV2MutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof patchDashboardV2>>
|
||||
>;
|
||||
export type PatchDashboardV2MutationBody =
|
||||
| BodyType<DashboardtypesPatchableDashboardV2DTO | null>
|
||||
| undefined;
|
||||
export type PatchDashboardV2MutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Patch dashboard (v2)
|
||||
*/
|
||||
export const usePatchDashboardV2 = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof patchDashboardV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: PatchDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesPatchableDashboardV2DTO | null>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof patchDashboardV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: PatchDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesPatchableDashboardV2DTO | null>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getPatchDashboardV2MutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint updates a v2-shape dashboard's metadata, data, and tag set. Locked dashboards are rejected.
|
||||
* @summary Update dashboard (v2)
|
||||
*/
|
||||
export const updateDashboardV2 = (
|
||||
{ id }: UpdateDashboardV2PathParameters,
|
||||
dashboardtypesUpdatableDashboardV2DTO?: BodyType<DashboardtypesUpdatableDashboardV2DTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<UpdateDashboardV2200>({
|
||||
url: `/api/v2/dashboards/${id}`,
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: dashboardtypesUpdatableDashboardV2DTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getUpdateDashboardV2MutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateDashboardV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesUpdatableDashboardV2DTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateDashboardV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesUpdatableDashboardV2DTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['updateDashboardV2'];
|
||||
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 updateDashboardV2>>,
|
||||
{
|
||||
pathParams: UpdateDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesUpdatableDashboardV2DTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
|
||||
return updateDashboardV2(pathParams, data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type UpdateDashboardV2MutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof updateDashboardV2>>
|
||||
>;
|
||||
export type UpdateDashboardV2MutationBody =
|
||||
| BodyType<DashboardtypesUpdatableDashboardV2DTO>
|
||||
| undefined;
|
||||
export type UpdateDashboardV2MutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Update dashboard (v2)
|
||||
*/
|
||||
export const useUpdateDashboardV2 = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateDashboardV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesUpdatableDashboardV2DTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof updateDashboardV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesUpdatableDashboardV2DTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getUpdateDashboardV2MutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint unlocks a v2-shape dashboard. Only the dashboard's creator or an org admin may lock or unlock.
|
||||
* @summary Unlock dashboard (v2)
|
||||
*/
|
||||
export const unlockDashboardV2 = (
|
||||
{ id }: UnlockDashboardV2PathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<string>({
|
||||
url: `/api/v2/dashboards/${id}/lock`,
|
||||
method: 'DELETE',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getUnlockDashboardV2MutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof unlockDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: UnlockDashboardV2PathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof unlockDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: UnlockDashboardV2PathParameters },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['unlockDashboardV2'];
|
||||
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 unlockDashboardV2>>,
|
||||
{ pathParams: UnlockDashboardV2PathParameters }
|
||||
> = (props) => {
|
||||
const { pathParams } = props ?? {};
|
||||
|
||||
return unlockDashboardV2(pathParams);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type UnlockDashboardV2MutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof unlockDashboardV2>>
|
||||
>;
|
||||
|
||||
export type UnlockDashboardV2MutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Unlock dashboard (v2)
|
||||
*/
|
||||
export const useUnlockDashboardV2 = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof unlockDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: UnlockDashboardV2PathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof unlockDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: UnlockDashboardV2PathParameters },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getUnlockDashboardV2MutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint locks a v2-shape dashboard. Only the dashboard's creator or an org admin may lock or unlock.
|
||||
* @summary Lock dashboard (v2)
|
||||
*/
|
||||
export const lockDashboardV2 = (
|
||||
{ id }: LockDashboardV2PathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<string>({
|
||||
url: `/api/v2/dashboards/${id}/lock`,
|
||||
method: 'PUT',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getLockDashboardV2MutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof lockDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: LockDashboardV2PathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof lockDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: LockDashboardV2PathParameters },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['lockDashboardV2'];
|
||||
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 lockDashboardV2>>,
|
||||
{ pathParams: LockDashboardV2PathParameters }
|
||||
> = (props) => {
|
||||
const { pathParams } = props ?? {};
|
||||
|
||||
return lockDashboardV2(pathParams);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type LockDashboardV2MutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof lockDashboardV2>>
|
||||
>;
|
||||
|
||||
export type LockDashboardV2MutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Lock dashboard (v2)
|
||||
*/
|
||||
export const useLockDashboardV2 = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof lockDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: LockDashboardV2PathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof lockDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: LockDashboardV2PathParameters },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getLockDashboardV2MutationOptions(options));
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,14 +12,13 @@ import type {
|
||||
} from 'react-query';
|
||||
|
||||
import type {
|
||||
GetFlamegraph200,
|
||||
GetFlamegraphPathParameters,
|
||||
GetTraceAggregations200,
|
||||
GetTraceAggregationsPathParameters,
|
||||
GetWaterfall200,
|
||||
GetWaterfallPathParameters,
|
||||
GetWaterfallV4200,
|
||||
GetWaterfallV4PathParameters,
|
||||
RenderErrorResponseDTO,
|
||||
SpantypesPostableFlamegraphDTO,
|
||||
SpantypesPostableTraceAggregationsDTO,
|
||||
SpantypesPostableWaterfallDTO,
|
||||
} from '../sigNoz.schemas';
|
||||
@@ -128,46 +127,46 @@ export const useGetTraceAggregations = <
|
||||
return useMutation(getGetTraceAggregationsMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Returns the flamegraph view of spans for a given trace ID.
|
||||
* @summary Get flamegraph view for a trace
|
||||
* Returns the waterfall view of spans for a given trace ID with tree structure, metadata, and windowed pagination
|
||||
* @summary Get waterfall view for a trace
|
||||
*/
|
||||
export const getFlamegraph = (
|
||||
{ traceID }: GetFlamegraphPathParameters,
|
||||
spantypesPostableFlamegraphDTO?: BodyType<SpantypesPostableFlamegraphDTO>,
|
||||
export const getWaterfall = (
|
||||
{ traceID }: GetWaterfallPathParameters,
|
||||
spantypesPostableWaterfallDTO?: BodyType<SpantypesPostableWaterfallDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetFlamegraph200>({
|
||||
url: `/api/v3/traces/${traceID}/flamegraph`,
|
||||
return GeneratedAPIInstance<GetWaterfall200>({
|
||||
url: `/api/v3/traces/${traceID}/waterfall`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: spantypesPostableFlamegraphDTO,
|
||||
data: spantypesPostableWaterfallDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetFlamegraphMutationOptions = <
|
||||
export const getGetWaterfallMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof getFlamegraph>>,
|
||||
Awaited<ReturnType<typeof getWaterfall>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: GetFlamegraphPathParameters;
|
||||
data?: BodyType<SpantypesPostableFlamegraphDTO>;
|
||||
pathParams: GetWaterfallPathParameters;
|
||||
data?: BodyType<SpantypesPostableWaterfallDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof getFlamegraph>>,
|
||||
Awaited<ReturnType<typeof getWaterfall>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: GetFlamegraphPathParameters;
|
||||
data?: BodyType<SpantypesPostableFlamegraphDTO>;
|
||||
pathParams: GetWaterfallPathParameters;
|
||||
data?: BodyType<SpantypesPostableWaterfallDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['getFlamegraph'];
|
||||
const mutationKey = ['getWaterfall'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
@@ -177,54 +176,54 @@ export const getGetFlamegraphMutationOptions = <
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof getFlamegraph>>,
|
||||
Awaited<ReturnType<typeof getWaterfall>>,
|
||||
{
|
||||
pathParams: GetFlamegraphPathParameters;
|
||||
data?: BodyType<SpantypesPostableFlamegraphDTO>;
|
||||
pathParams: GetWaterfallPathParameters;
|
||||
data?: BodyType<SpantypesPostableWaterfallDTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
|
||||
return getFlamegraph(pathParams, data);
|
||||
return getWaterfall(pathParams, data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type GetFlamegraphMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getFlamegraph>>
|
||||
export type GetWaterfallMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getWaterfall>>
|
||||
>;
|
||||
export type GetFlamegraphMutationBody =
|
||||
| BodyType<SpantypesPostableFlamegraphDTO>
|
||||
export type GetWaterfallMutationBody =
|
||||
| BodyType<SpantypesPostableWaterfallDTO>
|
||||
| undefined;
|
||||
export type GetFlamegraphMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
export type GetWaterfallMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get flamegraph view for a trace
|
||||
* @summary Get waterfall view for a trace
|
||||
*/
|
||||
export const useGetFlamegraph = <
|
||||
export const useGetWaterfall = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof getFlamegraph>>,
|
||||
Awaited<ReturnType<typeof getWaterfall>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: GetFlamegraphPathParameters;
|
||||
data?: BodyType<SpantypesPostableFlamegraphDTO>;
|
||||
pathParams: GetWaterfallPathParameters;
|
||||
data?: BodyType<SpantypesPostableWaterfallDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof getFlamegraph>>,
|
||||
Awaited<ReturnType<typeof getWaterfall>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: GetFlamegraphPathParameters;
|
||||
data?: BodyType<SpantypesPostableFlamegraphDTO>;
|
||||
pathParams: GetWaterfallPathParameters;
|
||||
data?: BodyType<SpantypesPostableWaterfallDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getGetFlamegraphMutationOptions(options));
|
||||
return useMutation(getGetWaterfallMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Returns the waterfall view of spans including all spans if total spans are under a limit, a max count otherwise. Aggregations are dropped compared to v3
|
||||
|
||||
@@ -120,8 +120,7 @@ export const interceptorRejected = async (
|
||||
!(
|
||||
response.config.url === '/sessions' && response.config.method === 'delete'
|
||||
) &&
|
||||
response.config.url !== '/authz/check' &&
|
||||
response.config.url !== '/api/v2/reset_password_tokens/verify'
|
||||
response.config.url !== '/authz/check'
|
||||
) {
|
||||
try {
|
||||
const accessToken = getLocalStorageApi(LOCALSTORAGE.AUTH_TOKEN);
|
||||
|
||||
@@ -5,13 +5,6 @@ import {
|
||||
QueryKeySuggestionsResponseProps,
|
||||
} from 'types/api/querySuggestions/types';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useGetFieldsKeys` hook (or `getFieldsKeys` fetcher) from
|
||||
* `api/generated/services/fields` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
export const getKeySuggestions = (
|
||||
props: QueryKeyRequestProps,
|
||||
): Promise<AxiosResponse<QueryKeySuggestionsResponseProps>> => {
|
||||
|
||||
@@ -5,13 +5,6 @@ import {
|
||||
QueryKeyValueSuggestionsResponseProps,
|
||||
} from 'types/api/querySuggestions/types';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useGetFieldsValues` hook (or `getFieldsValues` fetcher) from
|
||||
* `api/generated/services/fields` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
export const getValueSuggestions = (
|
||||
props: QueryKeyValueRequestProps,
|
||||
): Promise<AxiosResponse<QueryKeyValueSuggestionsResponseProps>> => {
|
||||
|
||||
@@ -15,13 +15,6 @@ export interface CreateRoutingPolicyResponse {
|
||||
message: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useCreateRoutePolicy` hook (or `createRoutePolicy` fetcher) from
|
||||
* `api/generated/services/routepolicies` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const createRoutingPolicy = async (
|
||||
props: CreateRoutingPolicyBody,
|
||||
): Promise<
|
||||
|
||||
@@ -8,13 +8,6 @@ export interface DeleteRoutingPolicyResponse {
|
||||
message: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useDeleteRoutePolicyByID` hook (or `deleteRoutePolicyByID` fetcher) from
|
||||
* `api/generated/services/routepolicies` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const deleteRoutingPolicy = async (
|
||||
routingPolicyId: string,
|
||||
): Promise<
|
||||
|
||||
@@ -20,13 +20,6 @@ export interface GetRoutingPoliciesResponse {
|
||||
data?: ApiRoutingPolicy[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useGetAllRoutePolicies` hook (or `getAllRoutePolicies` fetcher) from
|
||||
* `api/generated/services/routepolicies` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
export const getRoutingPolicies = async (
|
||||
signal?: AbortSignal,
|
||||
headers?: Record<string, string>,
|
||||
|
||||
@@ -15,13 +15,6 @@ export interface UpdateRoutingPolicyResponse {
|
||||
message: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useUpdateRoutePolicy` hook (or `updateRoutePolicy` fetcher) from
|
||||
* `api/generated/services/routepolicies` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const updateRoutingPolicy = async (
|
||||
id: string,
|
||||
props: UpdateRoutingPolicyBody,
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { getWaterfallV4 } from 'api/generated/services/tracedetail';
|
||||
import { ApiV3Instance as axios } from 'api';
|
||||
import { omit } from 'lodash-es';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import {
|
||||
GetTraceV4PayloadProps,
|
||||
GetTraceV4SuccessResponse,
|
||||
GetTraceV3PayloadProps,
|
||||
GetTraceV3SuccessResponse,
|
||||
SpanV3,
|
||||
} from 'types/api/trace/getTraceV3';
|
||||
|
||||
const getTraceV4 = async (
|
||||
props: GetTraceV4PayloadProps,
|
||||
): Promise<SuccessResponse<GetTraceV4SuccessResponse> | ErrorResponse> => {
|
||||
const getTraceV3 = async (
|
||||
props: GetTraceV3PayloadProps,
|
||||
): Promise<SuccessResponse<GetTraceV3SuccessResponse> | ErrorResponse> => {
|
||||
let uncollapsedSpans = [...props.uncollapsedSpans];
|
||||
if (!props.isSelectedSpanIDUnCollapsed) {
|
||||
uncollapsedSpans = uncollapsedSpans.filter(
|
||||
@@ -18,36 +19,31 @@ const getTraceV4 = async (
|
||||
props.selectedSpanId &&
|
||||
!uncollapsedSpans.includes(props.selectedSpanId)
|
||||
) {
|
||||
// Backend only uses the uncollapsedSpans list (unlike V2 which also interprets
|
||||
// V3 backend only uses uncollapsedSpans list (unlike V2 which also interprets
|
||||
// isSelectedSpanIDUnCollapsed server-side), so explicitly add the selected span
|
||||
uncollapsedSpans.push(props.selectedSpanId);
|
||||
}
|
||||
const response = await getWaterfallV4(
|
||||
{ traceID: props.traceId },
|
||||
{
|
||||
selectedSpanId: props.selectedSpanId,
|
||||
uncollapsedSpans,
|
||||
},
|
||||
const postData: GetTraceV3PayloadProps = {
|
||||
...props,
|
||||
uncollapsedSpans,
|
||||
limit: 10000,
|
||||
};
|
||||
const response = await axios.post<GetTraceV3SuccessResponse>(
|
||||
`/traces/${props.traceId}/waterfall`,
|
||||
omit(postData, 'traceId'),
|
||||
);
|
||||
|
||||
// Generated client unwraps the axios response; .data is the waterfall payload.
|
||||
// Wire spans carry time_unix; SpanV3's timestamp + 'service.name' are derived below.
|
||||
type WireSpan = Omit<SpanV3, 'timestamp' | 'service.name'> & {
|
||||
time_unix: number;
|
||||
};
|
||||
const rawPayload = response.data as unknown as Omit<
|
||||
GetTraceV4SuccessResponse,
|
||||
'spans'
|
||||
> & { spans: WireSpan[] | null };
|
||||
// V3 API wraps response in { status, data }
|
||||
const rawPayload = (response.data as any).data || response.data;
|
||||
|
||||
// Derive 'service.name' from resource for convenience — only derived field
|
||||
const spans: SpanV3[] = (rawPayload.spans || []).map((span) => ({
|
||||
const spans: SpanV3[] = (rawPayload.spans || []).map((span: any) => ({
|
||||
...span,
|
||||
'service.name': span.resource?.['service.name'] || '',
|
||||
timestamp: span.time_unix,
|
||||
}));
|
||||
|
||||
// API returns startTimestampMillis/endTimestampMillis as relative durations (ms from epoch offset),
|
||||
// V3 API returns startTimestampMillis/endTimestampMillis as relative durations (ms from epoch offset),
|
||||
// not absolute unix millis like V2. The span timestamps are absolute unix millis.
|
||||
// Convert by using the first span's timestamp as the base if there's a mismatch.
|
||||
let { startTimestampMillis, endTimestampMillis } = rawPayload;
|
||||
@@ -74,4 +70,4 @@ const getTraceV4 = async (
|
||||
};
|
||||
};
|
||||
|
||||
export default getTraceV4;
|
||||
export default getTraceV3;
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/user/resetPassword';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useResetPassword` hook (or `resetPassword` fetcher) from
|
||||
* `api/generated/services/users` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const resetPassword = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { UsersProps } from 'types/api/user/inviteUsers';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useCreateBulkInvite` hook (or `createBulkInvite` fetcher) from
|
||||
* `api/generated/services/users` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const inviteUsers = async (
|
||||
users: UsersProps,
|
||||
): Promise<SuccessResponseV2<null>> => {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/user/setInvite';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useCreateInvite` hook (or `createInvite` fetcher) from
|
||||
* `api/generated/services/users` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const sendInvite = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
|
||||
@@ -5,13 +5,6 @@ import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps } from 'types/api/preferences/list';
|
||||
import { OrgPreference } from 'types/api/preferences/preference';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useListOrgPreferences` hook (or `listOrgPreferences` fetcher) from
|
||||
* `api/generated/services/preferences` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const listPreference = async (): Promise<
|
||||
SuccessResponseV2<OrgPreference[]>
|
||||
> => {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { Props } from 'types/api/preferences/update';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useUpdateOrgPreference` hook (or `updateOrgPreference` fetcher) from
|
||||
* `api/generated/services/preferences` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const update = async (props: Props): Promise<SuccessResponseV2<null>> => {
|
||||
try {
|
||||
const response = await axios.put(`/org/preferences/${props.name}`, {
|
||||
|
||||
@@ -5,13 +5,6 @@ import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps } from 'types/api/preferences/list';
|
||||
import { UserPreference } from 'types/api/preferences/preference';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useListUserPreferences` hook (or `listUserPreferences` fetcher) from
|
||||
* `api/generated/services/preferences` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const list = async (): Promise<SuccessResponseV2<UserPreference[]>> => {
|
||||
try {
|
||||
const response = await axios.get<PayloadProps>(`/user/preferences`);
|
||||
|
||||
@@ -5,13 +5,6 @@ import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/preferences/get';
|
||||
import { UserPreference } from 'types/api/preferences/preference';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useGetUserPreference` hook (or `getUserPreference` fetcher) from
|
||||
* `api/generated/services/preferences` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const get = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<UserPreference>> => {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { Props } from 'types/api/preferences/update';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useUpdateUserPreference` hook (or `updateUserPreference` fetcher) from
|
||||
* `api/generated/services/preferences` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const update = async (props: Props): Promise<SuccessResponseV2<null>> => {
|
||||
try {
|
||||
const response = await axios.put(`/user/preferences/${props.name}`, {
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, RawSuccessResponse, SuccessResponseV2 } from 'types/api';
|
||||
import { Props, SessionsContext } from 'types/api/v2/sessions/context/get';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useGetSessionContext` hook (or `getSessionContext` fetcher) from
|
||||
* `api/generated/services/sessions` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const get = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<SessionsContext>> => {
|
||||
|
||||
@@ -3,13 +3,6 @@ import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, RawSuccessResponse, SuccessResponseV2 } from 'types/api';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useDeleteSession` hook (or `deleteSession` fetcher) from
|
||||
* `api/generated/services/sessions` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const deleteSession = async (): Promise<SuccessResponseV2<null>> => {
|
||||
try {
|
||||
const response = await axios.delete<RawSuccessResponse<null>>('/sessions');
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, RawSuccessResponse, SuccessResponseV2 } from 'types/api';
|
||||
import { Props, Token } from 'types/api/v2/sessions/email_password/post';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useCreateSessionByEmailPassword` hook (or `createSessionByEmailPassword` fetcher) from
|
||||
* `api/generated/services/sessions` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const post = async (props: Props): Promise<SuccessResponseV2<Token>> => {
|
||||
try {
|
||||
const response = await axios.post<RawSuccessResponse<Token>>(
|
||||
|
||||
@@ -4,13 +4,6 @@ import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, RawSuccessResponse, SuccessResponseV2 } from 'types/api';
|
||||
import { Props, Token } from 'types/api/v2/sessions/rotate/post';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useRotateSession` hook (or `rotateSession` fetcher) from
|
||||
* `api/generated/services/sessions` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const post = async (props: Props): Promise<SuccessResponseV2<Token>> => {
|
||||
try {
|
||||
const response = await axios.post<RawSuccessResponse<Token>>(
|
||||
|
||||
@@ -349,7 +349,7 @@ function convertV5DataByType(
|
||||
*/
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
export function convertV5ResponseToLegacy(
|
||||
v5Response: SuccessResponse<MetricRangePayloadV5, QueryRangeRequestV5>,
|
||||
v5Response: SuccessResponse<MetricRangePayloadV5>,
|
||||
legendMap: Record<string, string>,
|
||||
formatForWeb?: boolean,
|
||||
): SuccessResponse<MetricRangePayloadV3> & { warning?: Warning } {
|
||||
@@ -357,7 +357,7 @@ export function convertV5ResponseToLegacy(
|
||||
const v5Data = payload?.data;
|
||||
|
||||
const aggregationPerQuery =
|
||||
params?.compositeQuery?.queries
|
||||
(params as QueryRangeRequestV5)?.compositeQuery?.queries
|
||||
?.filter((query) => query.type === 'builder_query')
|
||||
.reduce(
|
||||
(acc, query) => {
|
||||
|
||||
@@ -8,13 +8,6 @@ import {
|
||||
QueryRangePayloadV5,
|
||||
} from 'types/api/v5/queryRange';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useQueryRangeV5` hook (or `queryRangeV5` fetcher) from
|
||||
* `api/generated/services/querier` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
export const getQueryRangeV5 = async (
|
||||
props: QueryRangePayloadV5,
|
||||
version: string,
|
||||
|
||||
1
frontend/src/assets/Icons/solid-x-circle.svg
Normal file
1
frontend/src/assets/Icons/solid-x-circle.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="14" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#prefix__clip0_4062_7291)" stroke-width="1.167" stroke-linecap="round" stroke-linejoin="round"><path d="M7 12.833A5.833 5.833 0 107 1.167a5.833 5.833 0 000 11.666z" fill="#E5484D" stroke="#E5484D"/><path d="M8.75 5.25l-3.5 3.5M5.25 5.25l3.5 3.5" stroke="#121317"/></g><defs><clipPath id="prefix__clip0_4062_7291"><path fill="#fff" d="M0 0h14v14H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 467 B |
@@ -359,7 +359,8 @@ function CustomTimePickerPopoverContent({
|
||||
<Clock
|
||||
color={Color.BG_ROBIN_400}
|
||||
className="timezone-container__clock-icon"
|
||||
size={14}
|
||||
height={12}
|
||||
width={12}
|
||||
/>
|
||||
|
||||
<span className="timezone__name">{timezone.name}</span>
|
||||
|
||||
@@ -27,15 +27,13 @@ function SortableField({
|
||||
field,
|
||||
onRemove,
|
||||
allowDrag,
|
||||
isRequired,
|
||||
}: {
|
||||
field: TelemetryFieldKey;
|
||||
onRemove: (field: TelemetryFieldKey) => void;
|
||||
allowDrag: boolean;
|
||||
isRequired: boolean;
|
||||
}): JSX.Element {
|
||||
const { attributes, listeners, setNodeRef, transform, transition } =
|
||||
useSortable({ id: field.key as string });
|
||||
useSortable({ id: field.name });
|
||||
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
@@ -55,17 +53,15 @@ function SortableField({
|
||||
{allowDrag && <GripVertical size={14} />}
|
||||
<span className={styles.fieldKey}>{field.name}</span>
|
||||
</div>
|
||||
{!isRequired && (
|
||||
<Button
|
||||
className={cx(styles.removeBtn, 'periscope-btn')}
|
||||
variant="outlined"
|
||||
color="destructive"
|
||||
size="sm"
|
||||
onClick={(): void => onRemove(field)}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
className={cx(styles.removeBtn, 'periscope-btn')}
|
||||
variant="outlined"
|
||||
color="destructive"
|
||||
size="sm"
|
||||
onClick={(): void => onRemove(field)}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -75,7 +71,6 @@ interface AddedFieldsProps {
|
||||
fields: TelemetryFieldKey[];
|
||||
onFieldsChange: (fields: TelemetryFieldKey[]) => void;
|
||||
maxFields?: number;
|
||||
requiredFields?: readonly string[];
|
||||
}
|
||||
|
||||
function AddedFields({
|
||||
@@ -83,18 +78,14 @@ function AddedFields({
|
||||
fields,
|
||||
onFieldsChange,
|
||||
maxFields,
|
||||
requiredFields = [],
|
||||
}: AddedFieldsProps): JSX.Element {
|
||||
const sensors = useSensors(useSensor(PointerSensor));
|
||||
|
||||
// Contract: caller (FieldsSelector) normalizes `fields` so every entry has
|
||||
// `.key` populated. AddedFields reads it directly.
|
||||
|
||||
const handleDragEnd = (event: DragEndEvent): void => {
|
||||
const { active, over } = event;
|
||||
if (over && active.id !== over.id) {
|
||||
const oldIndex = fields.findIndex((f) => f.key === active.id);
|
||||
const newIndex = fields.findIndex((f) => f.key === over.id);
|
||||
const oldIndex = fields.findIndex((f) => f.name === active.id);
|
||||
const newIndex = fields.findIndex((f) => f.name === over.id);
|
||||
onFieldsChange(arrayMove(fields, oldIndex, newIndex));
|
||||
}
|
||||
};
|
||||
@@ -108,7 +99,7 @@ function AddedFields({
|
||||
);
|
||||
|
||||
const handleRemove = (field: TelemetryFieldKey): void => {
|
||||
onFieldsChange(fields.filter((f) => f.key !== field.key));
|
||||
onFieldsChange(fields.filter((f) => f.name !== field.name));
|
||||
};
|
||||
|
||||
const allowDrag = inputValue.length === 0;
|
||||
@@ -134,17 +125,16 @@ function AddedFields({
|
||||
<div className={styles.noValues}>No values found</div>
|
||||
) : (
|
||||
<SortableContext
|
||||
items={fields.map((f) => f.key as string)}
|
||||
items={fields.map((f) => f.name)}
|
||||
strategy={verticalListSortingStrategy}
|
||||
disabled={!allowDrag}
|
||||
>
|
||||
{filteredFields.map((field) => (
|
||||
<SortableField
|
||||
key={field.key}
|
||||
key={field.name}
|
||||
field={field}
|
||||
onRemove={handleRemove}
|
||||
allowDrag={allowDrag}
|
||||
isRequired={requiredFields.includes(field.key as string)}
|
||||
/>
|
||||
))}
|
||||
</SortableContext>
|
||||
|
||||
@@ -76,8 +76,11 @@
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
|
||||
// Ant Skeleton.Input rendered inside the loading state — override its
|
||||
// hard-coded width.
|
||||
:global(.ant-skeleton-input) {
|
||||
width: 50% !important;
|
||||
width: 300px;
|
||||
margin: 8px 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,8 +95,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 36px;
|
||||
padding: 0 12px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
user-select: none;
|
||||
font-size: 13px;
|
||||
@@ -130,7 +132,7 @@
|
||||
}
|
||||
|
||||
.isDragDisabled {
|
||||
cursor: default;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.otherFieldItem {
|
||||
|
||||
@@ -5,7 +5,6 @@ import { Input } from '@signozhq/ui/input';
|
||||
import useDebouncedFn from 'hooks/useDebouncedFunction';
|
||||
import { Check, TableColumnsSplit, X } from '@signozhq/icons';
|
||||
import { FloatingPanel } from 'periscope/components/FloatingPanel';
|
||||
import { buildCompositeKey } from 'container/OptionsMenu/utils';
|
||||
import { TelemetryFieldKey } from 'types/api/v5/queryRange';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
@@ -27,36 +26,33 @@ interface FieldsSelectorProps {
|
||||
onClose: () => void;
|
||||
signal: DataSource;
|
||||
maxFields?: number;
|
||||
requiredFields?: readonly string[];
|
||||
width?: number;
|
||||
height?: number;
|
||||
defaultPosition?: { x: number; y: number };
|
||||
}
|
||||
|
||||
type FieldsSelectorContentProps = Omit<FieldsSelectorProps, 'isOpen'>;
|
||||
|
||||
// Inner component: holds all hooks + UI. Gets mounted/unmounted via the
|
||||
// outer gate so opening always seeds a fresh draft from `fields`.
|
||||
// Assumes `fields` arrives normalized (key populated) — see outer gate.
|
||||
function FieldsSelectorContent({
|
||||
function FieldsSelector({
|
||||
isOpen,
|
||||
title,
|
||||
fields,
|
||||
onFieldsChange,
|
||||
onClose,
|
||||
signal,
|
||||
maxFields,
|
||||
requiredFields,
|
||||
width = DEFAULT_PANEL_WIDTH,
|
||||
height,
|
||||
defaultPosition,
|
||||
}: FieldsSelectorContentProps): JSX.Element {
|
||||
}: FieldsSelectorProps): JSX.Element | null {
|
||||
if (!isOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const resolvedHeight =
|
||||
height ?? window.innerHeight - DEFAULT_PANEL_HEIGHT_OFFSET;
|
||||
const resolvedPosition = defaultPosition ?? {
|
||||
x: window.innerWidth - width - DEFAULT_PANEL_RIGHT_INSET,
|
||||
y: DEFAULT_PANEL_TOP_INSET,
|
||||
};
|
||||
|
||||
const [draftFields, setDraftFields] = useState<TelemetryFieldKey[]>(fields);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [debouncedInputValue, setDebouncedInputValue] = useState('');
|
||||
@@ -76,17 +72,15 @@ function FieldsSelectorContent({
|
||||
|
||||
const handleAdd = useCallback(
|
||||
(field: TelemetryFieldKey): void => {
|
||||
setDraftFields((prev) => {
|
||||
if (maxFields !== undefined && prev.length >= maxFields) {
|
||||
return prev;
|
||||
}
|
||||
if (prev.some((f) => f.key === field.key)) {
|
||||
return prev;
|
||||
}
|
||||
return [...prev, field];
|
||||
});
|
||||
if (maxFields !== undefined && draftFields.length >= maxFields) {
|
||||
return;
|
||||
}
|
||||
if (draftFields.some((f) => f.name === field.name)) {
|
||||
return;
|
||||
}
|
||||
setDraftFields((prev) => [...prev, field]);
|
||||
},
|
||||
[maxFields],
|
||||
[draftFields, maxFields],
|
||||
);
|
||||
|
||||
const handleSave = useCallback((): void => {
|
||||
@@ -105,7 +99,7 @@ function FieldsSelectorContent({
|
||||
() =>
|
||||
!(
|
||||
draftFields.length === fields.length &&
|
||||
draftFields.every((f, i) => f.key === fields[i]?.key)
|
||||
draftFields.every((f, i) => f.name === fields[i]?.name)
|
||||
),
|
||||
[draftFields, fields],
|
||||
);
|
||||
@@ -144,7 +138,6 @@ function FieldsSelectorContent({
|
||||
fields={draftFields}
|
||||
onFieldsChange={setDraftFields}
|
||||
maxFields={maxFields}
|
||||
requiredFields={requiredFields}
|
||||
/>
|
||||
|
||||
<OtherFields
|
||||
@@ -180,27 +173,4 @@ function FieldsSelectorContent({
|
||||
);
|
||||
}
|
||||
|
||||
// Outer gate: normalizes `fields` once (populates `key` so downstream code
|
||||
// can read it directly) and decides whether the inner component renders.
|
||||
// When isOpen flips false→true, the inner remounts → draft state seeds fresh.
|
||||
function FieldsSelector({
|
||||
isOpen,
|
||||
fields,
|
||||
...rest
|
||||
}: FieldsSelectorProps): JSX.Element | null {
|
||||
const normalizedFields = useMemo<TelemetryFieldKey[]>(
|
||||
() =>
|
||||
fields.map((f) => ({
|
||||
...f,
|
||||
key: f.key ?? buildCompositeKey(f.name, f.fieldContext),
|
||||
})),
|
||||
[fields],
|
||||
);
|
||||
|
||||
if (!isOpen) {
|
||||
return null;
|
||||
}
|
||||
return <FieldsSelectorContent {...rest} fields={normalizedFields} />;
|
||||
}
|
||||
|
||||
export default FieldsSelector;
|
||||
|
||||
@@ -4,7 +4,6 @@ import { Skeleton } from 'antd';
|
||||
import cx from 'classnames';
|
||||
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import { buildCompositeKey } from 'container/OptionsMenu/utils';
|
||||
import { useGetQueryKeySuggestions } from 'hooks/querySuggestions/useGetQueryKeySuggestions';
|
||||
import {
|
||||
FieldContext,
|
||||
@@ -48,22 +47,15 @@ function OtherFields({
|
||||
|
||||
const otherFields: TelemetryFieldKey[] = useMemo(() => {
|
||||
const suggestions = Object.values(data?.data.data.keys || {}).flat();
|
||||
// Normalize: synthesize `key` once so downstream reads can trust it.
|
||||
const normalizedSuggestions: TelemetryFieldKey[] = suggestions.map(
|
||||
(attr) => ({
|
||||
const addedNames = new Set(addedFields.map((f) => f.name));
|
||||
return suggestions
|
||||
.filter((attr) => !addedNames.has(attr.name))
|
||||
.map((attr) => ({
|
||||
...attr,
|
||||
key: buildCompositeKey(attr.name, attr.fieldContext as string),
|
||||
signal: attr.signal as SignalType,
|
||||
fieldContext: attr.fieldContext as FieldContext,
|
||||
fieldDataType: attr.fieldDataType as FieldDataType,
|
||||
}),
|
||||
);
|
||||
const addedIds = new Set(
|
||||
addedFields.map((f) => f.key ?? buildCompositeKey(f.name, f.fieldContext)),
|
||||
);
|
||||
return normalizedSuggestions.filter(
|
||||
(attr) => !addedIds.has(attr.key as string),
|
||||
);
|
||||
}));
|
||||
}, [data, addedFields]);
|
||||
|
||||
if (isFetching) {
|
||||
@@ -72,13 +64,8 @@ function OtherFields({
|
||||
<div className={styles.sectionHeader}>OTHER FIELDS</div>
|
||||
<div className={styles.otherList}>
|
||||
{Array.from({ length: 5 }).map((_, i) => (
|
||||
<div
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
key={i}
|
||||
className={cx(styles.fieldItem, styles.otherFieldItem)}
|
||||
>
|
||||
<Skeleton.Input active size="small" block />
|
||||
</div>
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<Skeleton.Input active size="small" key={i} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
@@ -96,7 +83,7 @@ function OtherFields({
|
||||
) : (
|
||||
otherFields.map((attr) => (
|
||||
<div
|
||||
key={attr.key}
|
||||
key={attr.name}
|
||||
className={cx(styles.fieldItem, styles.otherFieldItem)}
|
||||
>
|
||||
<span className={styles.fieldKey}>{attr.name}</span>
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
import { render, screen } from 'tests/test-utils';
|
||||
import { TelemetryFieldKey } from 'types/api/v5/queryRange';
|
||||
|
||||
import AddedFields from '../AddedFields';
|
||||
|
||||
// AddedFields assumes the caller has populated `key` (the parent
|
||||
// FieldsSelector does this via its normalization useMemo). Tests pre-populate
|
||||
// it directly.
|
||||
const makeField = (name: string, fieldContext = 'log'): TelemetryFieldKey => ({
|
||||
name,
|
||||
signal: 'logs',
|
||||
fieldContext: fieldContext as TelemetryFieldKey['fieldContext'],
|
||||
fieldDataType: 'string',
|
||||
key: `${fieldContext}.${name}`,
|
||||
});
|
||||
|
||||
describe('AddedFields — requiredFields', () => {
|
||||
it('renders a Remove button for every field when no requiredFields are passed', () => {
|
||||
const fields = [makeField('a'), makeField('b'), makeField('c')];
|
||||
|
||||
render(
|
||||
<AddedFields inputValue="" fields={fields} onFieldsChange={jest.fn()} />,
|
||||
);
|
||||
|
||||
expect(screen.getAllByRole('button', { name: /remove/i })).toHaveLength(3);
|
||||
});
|
||||
|
||||
it('hides the Remove button for fields whose composite key is in requiredFields', () => {
|
||||
const fields = [makeField('a'), makeField('b'), makeField('c')];
|
||||
|
||||
render(
|
||||
<AddedFields
|
||||
inputValue=""
|
||||
fields={fields}
|
||||
onFieldsChange={jest.fn()}
|
||||
requiredFields={['log.a', 'log.c']}
|
||||
/>,
|
||||
);
|
||||
|
||||
// Only 'b' is removable.
|
||||
const removeButtons = screen.getAllByRole('button', { name: /remove/i });
|
||||
expect(removeButtons).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('still renders the field name for required fields', () => {
|
||||
const fields = [makeField('a'), makeField('b')];
|
||||
|
||||
render(
|
||||
<AddedFields
|
||||
inputValue=""
|
||||
fields={fields}
|
||||
onFieldsChange={jest.fn()}
|
||||
requiredFields={['log.a']}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText('a')).toBeInTheDocument();
|
||||
expect(screen.getByText('b')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('locks ONLY the canonical variant — a same-name field from another context stays removable', () => {
|
||||
// Two `body` fields with different contexts. requiredFields holds the
|
||||
// canonical composite key only, so the attribute variant is deletable.
|
||||
const fields = [makeField('body', 'log'), makeField('body', 'attribute')];
|
||||
|
||||
render(
|
||||
<AddedFields
|
||||
inputValue=""
|
||||
fields={fields}
|
||||
onFieldsChange={jest.fn()}
|
||||
requiredFields={['log.body']}
|
||||
/>,
|
||||
);
|
||||
|
||||
// One Remove button: the attribute variant. log variant is locked.
|
||||
expect(screen.getAllByRole('button', { name: /remove/i })).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('does not lock anything when a bare name is passed (composite key required)', () => {
|
||||
// Bare `body` no longer matches — matching is composite-key only now.
|
||||
const fields = [makeField('body', 'log'), makeField('body', 'attribute')];
|
||||
|
||||
render(
|
||||
<AddedFields
|
||||
inputValue=""
|
||||
fields={fields}
|
||||
onFieldsChange={jest.fn()}
|
||||
requiredFields={['body']}
|
||||
/>,
|
||||
);
|
||||
|
||||
// Neither variant locked → both removable.
|
||||
expect(screen.getAllByRole('button', { name: /remove/i })).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('treats requiredFields as exact composite-key match (substring does not lock)', () => {
|
||||
const fields = [makeField('body'), makeField('body_extra')];
|
||||
|
||||
render(
|
||||
<AddedFields
|
||||
inputValue=""
|
||||
fields={fields}
|
||||
onFieldsChange={jest.fn()}
|
||||
requiredFields={['log.body']}
|
||||
/>,
|
||||
);
|
||||
|
||||
// 'log.body' locked, 'log.body_extra' removable.
|
||||
expect(screen.getAllByRole('button', { name: /remove/i })).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
@@ -3,12 +3,17 @@ import { useLocation } from 'react-router-dom';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { logEventMock } from '__tests__/logEventMock';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { handleContactSupport } from 'container/Integrations/utils';
|
||||
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
|
||||
|
||||
import FeedbackModal from '../FeedbackModal';
|
||||
|
||||
jest.mock('api/common/logEvent', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(() => Promise.resolve()),
|
||||
}));
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useLocation: jest.fn(),
|
||||
@@ -30,6 +35,7 @@ jest.mock('container/Integrations/utils', () => ({
|
||||
handleContactSupport: jest.fn(),
|
||||
}));
|
||||
|
||||
const mockLogEvent = logEvent as jest.MockedFunction<typeof logEvent>;
|
||||
const mockUseLocation = useLocation as jest.Mock;
|
||||
const mockUseGetTenantLicense = useGetTenantLicense as jest.Mock;
|
||||
const mockHandleContactSupport = handleContactSupport as jest.Mock;
|
||||
@@ -44,7 +50,6 @@ const mockLocation = {
|
||||
describe('FeedbackModal', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
logEventMock.mockReturnValue(Promise.resolve() as never);
|
||||
mockUseLocation.mockReturnValue(mockLocation);
|
||||
mockUseGetTenantLicense.mockReturnValue({
|
||||
isCloudUser: false,
|
||||
@@ -111,7 +116,7 @@ describe('FeedbackModal', () => {
|
||||
await user.type(textarea, testFeedback);
|
||||
await user.click(submitButton);
|
||||
|
||||
expect(logEventMock).toHaveBeenCalledWith('Feedback: Submitted', {
|
||||
expect(mockLogEvent).toHaveBeenCalledWith('Feedback: Submitted', {
|
||||
data: testFeedback,
|
||||
type: 'feedback',
|
||||
page: mockLocation.pathname,
|
||||
@@ -144,7 +149,7 @@ describe('FeedbackModal', () => {
|
||||
await user.type(textarea, testFeedback);
|
||||
await user.click(submitButton);
|
||||
|
||||
expect(logEventMock).toHaveBeenCalledWith('Feedback: Submitted', {
|
||||
expect(mockLogEvent).toHaveBeenCalledWith('Feedback: Submitted', {
|
||||
data: testFeedback,
|
||||
type: 'reportBug',
|
||||
page: mockLocation.pathname,
|
||||
@@ -177,7 +182,7 @@ describe('FeedbackModal', () => {
|
||||
await user.type(textarea, testFeedback);
|
||||
await user.click(submitButton);
|
||||
|
||||
expect(logEventMock).toHaveBeenCalledWith('Feedback: Submitted', {
|
||||
expect(mockLogEvent).toHaveBeenCalledWith('Feedback: Submitted', {
|
||||
data: testFeedback,
|
||||
type: 'featureRequest',
|
||||
page: mockLocation.pathname,
|
||||
|
||||
@@ -2,11 +2,16 @@
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { logEventMock } from '__tests__/logEventMock';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
|
||||
|
||||
import HeaderRightSection from '../HeaderRightSection';
|
||||
|
||||
jest.mock('api/common/logEvent', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useLocation: jest.fn(),
|
||||
@@ -45,6 +50,7 @@ jest.mock('hooks/useIsAIAssistantEnabled', () => ({
|
||||
useIsAIAssistantEnabled: (): boolean => false,
|
||||
}));
|
||||
|
||||
const mockLogEvent = logEvent as jest.Mock;
|
||||
const mockUseLocation = useLocation as jest.Mock;
|
||||
const mockUseGetTenantLicense = useGetTenantLicense as jest.Mock;
|
||||
|
||||
@@ -114,7 +120,7 @@ describe('HeaderRightSection', () => {
|
||||
|
||||
await user.click(feedbackButton!);
|
||||
|
||||
expect(logEventMock).toHaveBeenCalledWith('Feedback: Clicked', {
|
||||
expect(mockLogEvent).toHaveBeenCalledWith('Feedback: Clicked', {
|
||||
page: mockLocation.pathname,
|
||||
});
|
||||
expect(screen.getByTestId('feedback-modal')).toBeInTheDocument();
|
||||
@@ -127,7 +133,7 @@ describe('HeaderRightSection', () => {
|
||||
const shareButton = screen.getByRole('button', { name: /share/i });
|
||||
await user.click(shareButton);
|
||||
|
||||
expect(logEventMock).toHaveBeenCalledWith('Share: Clicked', {
|
||||
expect(mockLogEvent).toHaveBeenCalledWith('Share: Clicked', {
|
||||
page: mockLocation.pathname,
|
||||
});
|
||||
expect(screen.getByTestId('share-modal')).toBeInTheDocument();
|
||||
@@ -144,7 +150,7 @@ describe('HeaderRightSection', () => {
|
||||
|
||||
await user.click(announcementsButton!);
|
||||
|
||||
expect(logEventMock).toHaveBeenCalledWith('Announcements: Clicked', {
|
||||
expect(mockLogEvent).toHaveBeenCalledWith('Announcements: Clicked', {
|
||||
page: mockLocation.pathname,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,13 +5,18 @@ import { matchPath, useLocation } from 'react-router-dom';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { logEventMock } from '__tests__/logEventMock';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import ROUTES from 'constants/routes';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import GetMinMax from 'lib/getMinMax';
|
||||
|
||||
import ShareURLModal from '../ShareURLModal';
|
||||
|
||||
jest.mock('api/common/logEvent', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useLocation: jest.fn(),
|
||||
@@ -48,6 +53,7 @@ Object.defineProperty(window, 'location', {
|
||||
writable: true,
|
||||
});
|
||||
|
||||
const mockLogEvent = logEvent as jest.Mock;
|
||||
const mockUseLocation = useLocation as jest.Mock;
|
||||
const mockUseUrlQuery = useUrlQuery as jest.Mock;
|
||||
const mockUseSelector = useSelector as jest.Mock;
|
||||
@@ -119,7 +125,7 @@ describe('ShareURLModal', () => {
|
||||
await user.click(copyButton);
|
||||
|
||||
expect(mockHandleCopyToClipboard).toHaveBeenCalled();
|
||||
expect(logEventMock).toHaveBeenCalledWith('Share: Copy link clicked', {
|
||||
expect(mockLogEvent).toHaveBeenCalledWith('Share: Copy link clicked', {
|
||||
page: TEST_PATH,
|
||||
URL: expect.any(String),
|
||||
});
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { FontSize } from 'container/OptionsMenu/types';
|
||||
import { IField } from 'types/api/logs/fields';
|
||||
|
||||
import { useLogsTableColumns } from '../useLogsTableColumns';
|
||||
|
||||
jest.mock('providers/Timezone', () => ({
|
||||
useTimezone: (): { formatTimezoneAdjustedTimestamp: jest.Mock } => ({
|
||||
formatTimezoneAdjustedTimestamp: jest.fn(() => 'TS'),
|
||||
}),
|
||||
}));
|
||||
|
||||
const field = (name: string, type = ''): IField => ({
|
||||
name,
|
||||
type,
|
||||
dataType: 'string',
|
||||
});
|
||||
|
||||
describe('useLogsTableColumns — selectColumns-order respected', () => {
|
||||
it('prepends stateIndicator and renders user fields in array order', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useLogsTableColumns({
|
||||
fields: [field('c'), field('a'), field('b')],
|
||||
fontSize: FontSize.SMALL,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.current.map((c) => c.id)).toStrictEqual([
|
||||
'state-indicator',
|
||||
'c',
|
||||
'a',
|
||||
'b',
|
||||
]);
|
||||
});
|
||||
|
||||
it('slots body and timestamp at their position in the fields array', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useLogsTableColumns({
|
||||
fields: [
|
||||
field('service.name'),
|
||||
field('body', 'log'),
|
||||
field('request.id'),
|
||||
field('timestamp', 'log'),
|
||||
],
|
||||
fontSize: FontSize.SMALL,
|
||||
}),
|
||||
);
|
||||
|
||||
// body/timestamp appear where the caller placed them, keyed by their
|
||||
// composite IDs ('log.*'); contextless user fields collapse to bare name.
|
||||
expect(result.current.map((c) => c.id)).toStrictEqual([
|
||||
'state-indicator',
|
||||
'service.name',
|
||||
'log.body',
|
||||
'request.id',
|
||||
'log.timestamp',
|
||||
]);
|
||||
});
|
||||
|
||||
it('renders a same-name field from another context as a DISTINCT column (no collision)', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useLogsTableColumns({
|
||||
fields: [field('body', 'log'), field('body', 'attribute')],
|
||||
fontSize: FontSize.SMALL,
|
||||
}),
|
||||
);
|
||||
|
||||
const byId = new Map(result.current.map((c) => [c.id, c]));
|
||||
// Attribute variant is its own column, not a duplicate 'log.body'.
|
||||
expect(result.current.map((c) => c.id)).toStrictEqual([
|
||||
'state-indicator',
|
||||
'log.body',
|
||||
'attribute.body',
|
||||
]);
|
||||
expect(byId.get('log.body')?.enableRemove).toBe(false);
|
||||
expect(byId.get('attribute.body')?.enableRemove).toBe(true);
|
||||
});
|
||||
|
||||
it('applies the same distinct-column treatment to timestamp variants', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useLogsTableColumns({
|
||||
fields: [field('timestamp', 'log'), field('timestamp', 'attribute')],
|
||||
fontSize: FontSize.SMALL,
|
||||
}),
|
||||
);
|
||||
|
||||
const byId = new Map(result.current.map((c) => [c.id, c]));
|
||||
expect(result.current.map((c) => c.id)).toStrictEqual([
|
||||
'state-indicator',
|
||||
'log.timestamp',
|
||||
'attribute.timestamp',
|
||||
]);
|
||||
expect(byId.get('log.timestamp')?.enableRemove).toBe(false);
|
||||
expect(byId.get('attribute.timestamp')?.enableRemove).toBe(true);
|
||||
});
|
||||
|
||||
it('skips the synthetic "id" field name', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useLogsTableColumns({
|
||||
fields: [field('id'), field('a'), field('b')],
|
||||
fontSize: FontSize.SMALL,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.current.map((c) => c.id)).toStrictEqual([
|
||||
'state-indicator',
|
||||
'a',
|
||||
'b',
|
||||
]);
|
||||
});
|
||||
|
||||
it('uses the special body/timestamp coldefs (canBeHidden=false), not the generic user field def', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useLogsTableColumns({
|
||||
fields: [
|
||||
field('body', 'log'),
|
||||
field('timestamp', 'log'),
|
||||
field('user_field'),
|
||||
],
|
||||
fontSize: FontSize.SMALL,
|
||||
}),
|
||||
);
|
||||
|
||||
const byId = new Map(result.current.map((c) => [c.id, c]));
|
||||
// body + timestamp are locked from the table-X removal pathway.
|
||||
expect(byId.get('log.body')?.canBeHidden).toBe(false);
|
||||
expect(byId.get('log.body')?.enableRemove).toBe(false);
|
||||
expect(byId.get('log.timestamp')?.canBeHidden).toBe(false);
|
||||
expect(byId.get('log.timestamp')?.enableRemove).toBe(false);
|
||||
// User-added fields stay removable. User field has type='' so composite
|
||||
// collapses to bare name.
|
||||
expect(byId.get('user_field')?.enableRemove).toBe(true);
|
||||
});
|
||||
|
||||
it('renders only the stateIndicator when fields is empty', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useLogsTableColumns({
|
||||
fields: [],
|
||||
fontSize: FontSize.SMALL,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.current.map((c) => c.id)).toStrictEqual(['state-indicator']);
|
||||
});
|
||||
});
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
getSanitizedLogBody,
|
||||
} from 'container/LogDetailedView/utils';
|
||||
import { FontSize } from 'container/OptionsMenu/types';
|
||||
import { buildCompositeKey } from 'container/OptionsMenu/utils';
|
||||
import { FlatLogData } from 'lib/logs/flatLogData';
|
||||
import { useTimezone } from 'providers/Timezone';
|
||||
import { IField } from 'types/api/logs/fields';
|
||||
@@ -19,11 +18,13 @@ import LogStateIndicator from '../LogStateIndicator/LogStateIndicator';
|
||||
type UseLogsTableColumnsProps = {
|
||||
fields: IField[];
|
||||
fontSize: FontSize;
|
||||
appendTo?: 'center' | 'end';
|
||||
};
|
||||
|
||||
export function useLogsTableColumns({
|
||||
fields,
|
||||
fontSize,
|
||||
appendTo = 'center',
|
||||
}: UseLogsTableColumnsProps): TableColumnDef<ILog>[] {
|
||||
const { formatTimezoneAdjustedTimestamp } = useTimezone();
|
||||
|
||||
@@ -46,74 +47,74 @@ export function useLogsTableColumns({
|
||||
),
|
||||
};
|
||||
|
||||
const timestampCol: TableColumnDef<ILog> = {
|
||||
id: buildCompositeKey('timestamp', 'log'),
|
||||
header: 'Timestamp',
|
||||
accessorFn: (log): unknown => log.timestamp,
|
||||
canBeHidden: false,
|
||||
enableRemove: false,
|
||||
width: { default: 170, min: 170 },
|
||||
cell: ({ value }): ReactElement => {
|
||||
const ts = value as string | number;
|
||||
const formatted =
|
||||
typeof ts === 'string'
|
||||
? formatTimezoneAdjustedTimestamp(ts, DATE_TIME_FORMATS.ISO_DATETIME_MS)
|
||||
: formatTimezoneAdjustedTimestamp(
|
||||
ts / 1e6,
|
||||
DATE_TIME_FORMATS.ISO_DATETIME_MS,
|
||||
);
|
||||
return <TanStackTable.Text>{formatted}</TanStackTable.Text>;
|
||||
},
|
||||
};
|
||||
const fieldColumns: TableColumnDef<ILog>[] = fields
|
||||
.filter((f): boolean => !['id', 'body', 'timestamp'].includes(f.name))
|
||||
.map(
|
||||
(f): TableColumnDef<ILog> => ({
|
||||
id: f.name,
|
||||
header: f.name,
|
||||
accessorFn: (log): unknown => FlatLogData(log)[f.name],
|
||||
enableRemove: true,
|
||||
width: { min: 192 },
|
||||
cell: ({ value }): ReactElement => (
|
||||
<TanStackTable.Text>{String(value ?? '')}</TanStackTable.Text>
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
const bodyCol: TableColumnDef<ILog> = {
|
||||
id: buildCompositeKey('body', 'log'),
|
||||
header: 'Body',
|
||||
accessorFn: (log): string => getBodyDisplayString(log.body),
|
||||
canBeHidden: false,
|
||||
enableRemove: false,
|
||||
width: { default: '100%', min: 300 },
|
||||
cell: ({ value, isActive }): ReactElement => (
|
||||
<TanStackTable.Text
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: getSanitizedLogBody(value as string, {
|
||||
shouldEscapeHtml: true,
|
||||
}),
|
||||
}}
|
||||
data-active={isActive}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
const makeUserFieldCol = (f: IField): TableColumnDef<ILog> => ({
|
||||
id: buildCompositeKey(f.name, f.type),
|
||||
header: f.name,
|
||||
accessorFn: (log): unknown => FlatLogData(log)[f.name],
|
||||
enableRemove: true,
|
||||
width: { min: 192 },
|
||||
cell: ({ value }): ReactElement => (
|
||||
<TanStackTable.Text>{String(value ?? '')}</TanStackTable.Text>
|
||||
),
|
||||
});
|
||||
|
||||
// Match body/timestamp by composite key, not bare name — else a variant
|
||||
// like `attribute.body` collapses onto `log.body`, duplicating the column.
|
||||
const fieldCols = fields
|
||||
.map((f): TableColumnDef<ILog> | null => {
|
||||
if (f.name === 'id') {
|
||||
return null;
|
||||
const timestampCol: TableColumnDef<ILog> | null = fields.some(
|
||||
(f) => f.name === 'timestamp',
|
||||
)
|
||||
? {
|
||||
id: 'timestamp',
|
||||
header: 'Timestamp',
|
||||
accessorFn: (log): unknown => log.timestamp,
|
||||
canBeHidden: false,
|
||||
enableRemove: false,
|
||||
width: { default: 170, min: 170 },
|
||||
cell: ({ value }): ReactElement => {
|
||||
const ts = value as string | number;
|
||||
const formatted =
|
||||
typeof ts === 'string'
|
||||
? formatTimezoneAdjustedTimestamp(ts, DATE_TIME_FORMATS.ISO_DATETIME_MS)
|
||||
: formatTimezoneAdjustedTimestamp(
|
||||
ts / 1e6,
|
||||
DATE_TIME_FORMATS.ISO_DATETIME_MS,
|
||||
);
|
||||
return <TanStackTable.Text>{formatted}</TanStackTable.Text>;
|
||||
},
|
||||
}
|
||||
const compositeKey = buildCompositeKey(f.name, f.type);
|
||||
if (compositeKey === timestampCol.id) {
|
||||
return timestampCol;
|
||||
}
|
||||
if (compositeKey === bodyCol.id) {
|
||||
return bodyCol;
|
||||
}
|
||||
return makeUserFieldCol(f);
|
||||
})
|
||||
.filter((c): c is TableColumnDef<ILog> => c !== null);
|
||||
: null;
|
||||
|
||||
return [stateIndicatorCol, ...fieldCols];
|
||||
}, [fields, fontSize, formatTimezoneAdjustedTimestamp]);
|
||||
const bodyCol: TableColumnDef<ILog> | null = fields.some(
|
||||
(f) => f.name === 'body',
|
||||
)
|
||||
? {
|
||||
id: 'body',
|
||||
header: 'Body',
|
||||
accessorFn: (log): string => getBodyDisplayString(log.body),
|
||||
canBeHidden: false,
|
||||
enableRemove: false,
|
||||
width: { default: '100%', min: 300 },
|
||||
cell: ({ value, isActive }): ReactElement => (
|
||||
<TanStackTable.Text
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: getSanitizedLogBody(value as string, {
|
||||
shouldEscapeHtml: true,
|
||||
}),
|
||||
}}
|
||||
data-active={isActive}
|
||||
/>
|
||||
),
|
||||
}
|
||||
: null;
|
||||
|
||||
return [
|
||||
stateIndicatorCol,
|
||||
...(timestampCol ? [timestampCol] : []),
|
||||
...(appendTo === 'center' ? fieldColumns : []),
|
||||
...(bodyCol ? [bodyCol] : []),
|
||||
...(appendTo === 'end' ? fieldColumns : []),
|
||||
];
|
||||
}, [fields, appendTo, fontSize, formatTimezoneAdjustedTimestamp]);
|
||||
}
|
||||
|
||||
@@ -256,34 +256,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.edit-columns-container {
|
||||
padding: 12px;
|
||||
|
||||
.edit-columns-btn {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
padding: 4px 0px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border: none !important;
|
||||
|
||||
.edit-columns-text {
|
||||
color: var(--l2-foreground);
|
||||
font-family: Inter;
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
letter-spacing: 0.14px;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-columns-btn:hover {
|
||||
background-color: unset !important;
|
||||
}
|
||||
}
|
||||
|
||||
.add-new-column-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Input } from '@signozhq/ui/input';
|
||||
import { Button, InputNumber, Popover, Tooltip } from 'antd';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import type { DefaultOptionType } from 'antd/es/select';
|
||||
import cx from 'classnames';
|
||||
import { LogViewMode } from 'container/LogsTable';
|
||||
import { FontSize, OptionsMenuConfig } from 'container/OptionsMenu/types';
|
||||
import useDebouncedFn from 'hooks/useDebouncedFunction';
|
||||
import {
|
||||
Check,
|
||||
ChevronLeft,
|
||||
@@ -11,6 +14,7 @@ import {
|
||||
Minus,
|
||||
Plus,
|
||||
SlidersVertical,
|
||||
X,
|
||||
} from '@signozhq/icons';
|
||||
|
||||
import './LogsFormatOptionsMenu.styles.scss';
|
||||
@@ -19,21 +23,14 @@ interface LogsFormatOptionsMenuProps {
|
||||
items: any;
|
||||
selectedOptionFormat: any;
|
||||
config: OptionsMenuConfig;
|
||||
onOpenColumns?: () => void;
|
||||
}
|
||||
|
||||
interface OptionsMenuContentProps extends LogsFormatOptionsMenuProps {
|
||||
closePopover: () => void;
|
||||
}
|
||||
|
||||
function OptionsMenu({
|
||||
items,
|
||||
selectedOptionFormat,
|
||||
config,
|
||||
onOpenColumns,
|
||||
closePopover,
|
||||
}: OptionsMenuContentProps): JSX.Element {
|
||||
const { maxLines, format, fontSize } = config;
|
||||
}: LogsFormatOptionsMenuProps): JSX.Element {
|
||||
const { maxLines, format, addColumn, fontSize } = config;
|
||||
const [selectedItem, setSelectedItem] = useState(selectedOptionFormat);
|
||||
const maxLinesNumber = (maxLines?.value as number) || 1;
|
||||
const [maxLinesPerRow, setMaxLinesPerRow] = useState<number>(maxLinesNumber);
|
||||
@@ -43,6 +40,13 @@ function OptionsMenu({
|
||||
const [isFontSizeOptionsOpen, setIsFontSizeOptionsOpen] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const [showAddNewColumnContainer, setShowAddNewColumnContainer] =
|
||||
useState(false);
|
||||
|
||||
const [selectedValue, setSelectedValue] = useState<string | null>(null);
|
||||
const listRef = useRef<HTMLDivElement>(null);
|
||||
const initialMouseEnterRef = useRef<boolean>(false);
|
||||
|
||||
const onChange = useCallback(
|
||||
(key: LogViewMode) => {
|
||||
if (!format) {
|
||||
@@ -57,6 +61,7 @@ function OptionsMenu({
|
||||
const handleMenuItemClick = (key: LogViewMode): void => {
|
||||
setSelectedItem(key);
|
||||
onChange(key);
|
||||
setShowAddNewColumnContainer(false);
|
||||
};
|
||||
|
||||
const incrementMaxLinesPerRow = (): void => {
|
||||
@@ -71,6 +76,20 @@ function OptionsMenu({
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearchValueChange = useDebouncedFn((event): void => {
|
||||
// @ts-expect-error
|
||||
const value = event?.target?.value || '';
|
||||
|
||||
if (addColumn && addColumn?.onSearch) {
|
||||
addColumn?.onSearch(value);
|
||||
}
|
||||
}, 300);
|
||||
|
||||
const handleToggleAddNewColumn = (): void => {
|
||||
addColumn?.onSearch?.('');
|
||||
setShowAddNewColumnContainer(!showAddNewColumnContainer);
|
||||
};
|
||||
|
||||
const handleLinesPerRowChange = (maxLinesPerRow: number | null): void => {
|
||||
if (
|
||||
maxLinesPerRow &&
|
||||
@@ -81,11 +100,6 @@ function OptionsMenu({
|
||||
}
|
||||
};
|
||||
|
||||
const handleEditColumns = (): void => {
|
||||
onOpenColumns?.();
|
||||
closePopover();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (maxLinesPerRow && config && config.maxLines?.onChange) {
|
||||
config.maxLines.onChange(maxLinesPerRow);
|
||||
@@ -98,10 +112,110 @@ function OptionsMenu({
|
||||
}
|
||||
}, [fontSizeValue]);
|
||||
|
||||
function handleColumnSelection(
|
||||
currentIndex: number,
|
||||
optionsData: DefaultOptionType[],
|
||||
): void {
|
||||
const currentItem = optionsData[currentIndex];
|
||||
const itemLength = optionsData.length;
|
||||
if (addColumn && addColumn?.onSelect) {
|
||||
addColumn?.onSelect(selectedValue, {
|
||||
label: currentItem.label,
|
||||
disabled: false,
|
||||
});
|
||||
|
||||
// if the last element is selected then select the previous one
|
||||
if (currentIndex === itemLength - 1) {
|
||||
// there should be more than 1 element in the list
|
||||
if (currentIndex - 1 >= 0) {
|
||||
const prevValue = optionsData[currentIndex - 1]?.value || null;
|
||||
setSelectedValue(prevValue as string | null);
|
||||
} else {
|
||||
// if there is only one element then just select and do nothing
|
||||
setSelectedValue(null);
|
||||
}
|
||||
} else {
|
||||
// selecting any random element from the list except the last one
|
||||
const nextIndex = currentIndex + 1;
|
||||
|
||||
const nextValue = optionsData[nextIndex]?.value || null;
|
||||
|
||||
setSelectedValue(nextValue as string | null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent): void => {
|
||||
if (!selectedValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
const optionsData = addColumn?.options || [];
|
||||
|
||||
const currentIndex = optionsData.findIndex(
|
||||
(item) => item?.value === selectedValue,
|
||||
);
|
||||
|
||||
const itemLength = optionsData.length;
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowUp': {
|
||||
const newValue = optionsData[Math.max(0, currentIndex - 1)]?.value;
|
||||
|
||||
setSelectedValue(newValue as string | null);
|
||||
e.preventDefault();
|
||||
break;
|
||||
}
|
||||
case 'ArrowDown': {
|
||||
const newValue =
|
||||
optionsData[Math.min(itemLength - 1, currentIndex + 1)]?.value;
|
||||
|
||||
setSelectedValue(newValue as string | null);
|
||||
e.preventDefault();
|
||||
break;
|
||||
}
|
||||
case 'Enter':
|
||||
e.preventDefault();
|
||||
handleColumnSelection(currentIndex, optionsData);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Scroll the selected item into view
|
||||
const listNode = listRef.current;
|
||||
if (listNode && selectedValue) {
|
||||
const optionsData = addColumn?.options || [];
|
||||
const currentIndex = optionsData.findIndex(
|
||||
(item) => item?.value === selectedValue,
|
||||
);
|
||||
const itemNode = listNode.children[currentIndex] as HTMLElement;
|
||||
if (itemNode) {
|
||||
itemNode.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest',
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [selectedValue]);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return (): void => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [selectedValue]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="nested-menu-container"
|
||||
className={cx(
|
||||
'nested-menu-container',
|
||||
showAddNewColumnContainer ? 'active' : '',
|
||||
)}
|
||||
onClick={(event): void => {
|
||||
// this is to restrict click events to propogate to parent
|
||||
event.stopPropagation();
|
||||
}}
|
||||
>
|
||||
@@ -155,7 +269,71 @@ function OptionsMenu({
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
) : null}
|
||||
|
||||
{showAddNewColumnContainer && (
|
||||
<div className="add-new-column-container">
|
||||
<div className="add-new-column-header">
|
||||
<div className="title">
|
||||
<div className="periscope-btn ghost" onClick={handleToggleAddNewColumn}>
|
||||
<ChevronLeft
|
||||
size={14}
|
||||
className="back-icon"
|
||||
onClick={handleToggleAddNewColumn}
|
||||
/>
|
||||
</div>
|
||||
Add New Column
|
||||
</div>
|
||||
|
||||
<Input
|
||||
tabIndex={0}
|
||||
type="text"
|
||||
autoFocus
|
||||
onFocus={addColumn?.onFocus}
|
||||
onChange={handleSearchValueChange}
|
||||
placeholder="Search..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="add-new-column-content">
|
||||
{addColumn?.isFetching && (
|
||||
<div className="loading-container"> Loading ... </div>
|
||||
)}
|
||||
|
||||
<div className="column-format-new-options" ref={listRef}>
|
||||
{addColumn?.options?.map(({ label, value }, index) => (
|
||||
<div
|
||||
className={cx('column-name', value === selectedValue && 'selected')}
|
||||
key={value}
|
||||
onMouseEnter={(): void => {
|
||||
if (!initialMouseEnterRef.current) {
|
||||
setSelectedValue(value as string | null);
|
||||
}
|
||||
|
||||
initialMouseEnterRef.current = true;
|
||||
}}
|
||||
onMouseMove={(): void => {
|
||||
// this is added to handle the mouse move explicit event and not the re-rendered on mouse enter event
|
||||
setSelectedValue(value as string | null);
|
||||
}}
|
||||
onClick={(eve): void => {
|
||||
eve.stopPropagation();
|
||||
handleColumnSelection(index, addColumn?.options || []);
|
||||
}}
|
||||
>
|
||||
<div className="name">
|
||||
<Tooltip placement="left" title={label}>
|
||||
{label}
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isFontSizeOptionsOpen && !showAddNewColumnContainer && (
|
||||
<div>
|
||||
<div className="font-size-container">
|
||||
<div className="title">Font Size</div>
|
||||
@@ -195,52 +373,72 @@ function OptionsMenu({
|
||||
|
||||
{selectedItem && (
|
||||
<>
|
||||
<div className="horizontal-line" />
|
||||
<div className="max-lines-per-row">
|
||||
<div className="title"> max lines per row </div>
|
||||
<div className="raw-format max-lines-per-row-input">
|
||||
<button
|
||||
type="button"
|
||||
className="periscope-btn"
|
||||
onClick={decrementMaxLinesPerRow}
|
||||
>
|
||||
{' '}
|
||||
<Minus size={12} />{' '}
|
||||
</button>
|
||||
<InputNumber
|
||||
min={1}
|
||||
max={10}
|
||||
value={maxLinesPerRow}
|
||||
onChange={handleLinesPerRowChange}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="periscope-btn"
|
||||
onClick={incrementMaxLinesPerRow}
|
||||
>
|
||||
{' '}
|
||||
<Plus size={12} />{' '}
|
||||
</button>
|
||||
<>
|
||||
<div className="horizontal-line" />
|
||||
<div className="max-lines-per-row">
|
||||
<div className="title"> max lines per row </div>
|
||||
<div className="raw-format max-lines-per-row-input">
|
||||
<button
|
||||
type="button"
|
||||
className="periscope-btn"
|
||||
onClick={decrementMaxLinesPerRow}
|
||||
>
|
||||
{' '}
|
||||
<Minus size={12} />{' '}
|
||||
</button>
|
||||
<InputNumber
|
||||
min={1}
|
||||
max={10}
|
||||
value={maxLinesPerRow}
|
||||
onChange={handleLinesPerRowChange}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="periscope-btn"
|
||||
onClick={incrementMaxLinesPerRow}
|
||||
>
|
||||
{' '}
|
||||
<Plus size={12} />{' '}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
{selectedItem === 'table' && onOpenColumns && (
|
||||
<>
|
||||
<div className="horizontal-line" />
|
||||
<div className="edit-columns-container">
|
||||
<Button
|
||||
className="edit-columns-btn"
|
||||
type="text"
|
||||
onClick={handleEditColumns}
|
||||
data-testid="periscope-btn-edit-columns"
|
||||
>
|
||||
<Typography.Text className="edit-columns-text">
|
||||
Edit columns
|
||||
</Typography.Text>
|
||||
<ChevronRight size={14} className="icon" />
|
||||
</Button>
|
||||
<div className="selected-item-content-container active">
|
||||
{!showAddNewColumnContainer && <div className="horizontal-line" />}
|
||||
|
||||
<div className="item-content">
|
||||
{!showAddNewColumnContainer && (
|
||||
<div className="title">
|
||||
columns
|
||||
<Plus size={14} onClick={handleToggleAddNewColumn} />{' '}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="column-format">
|
||||
{addColumn?.value?.map(({ name }) => (
|
||||
<div className="column-name" key={name}>
|
||||
<div className="name">
|
||||
<Tooltip placement="left" title={name}>
|
||||
{name}
|
||||
</Tooltip>
|
||||
</div>
|
||||
{addColumn?.value?.length > 1 && (
|
||||
<X
|
||||
className="delete-btn"
|
||||
size={14}
|
||||
onClick={(): void => addColumn.onRemove(name)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{addColumn && addColumn?.value?.length === 0 && (
|
||||
<div className="column-name no-columns-selected">
|
||||
No columns selected
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@@ -254,7 +452,6 @@ function LogsFormatOptionsMenu({
|
||||
items,
|
||||
selectedOptionFormat,
|
||||
config,
|
||||
onOpenColumns,
|
||||
}: LogsFormatOptionsMenuProps): JSX.Element {
|
||||
const [isPopoverOpen, setIsPopoverOpen] = useState<boolean>(false);
|
||||
return (
|
||||
@@ -264,8 +461,6 @@ function LogsFormatOptionsMenu({
|
||||
items={items}
|
||||
selectedOptionFormat={selectedOptionFormat}
|
||||
config={config}
|
||||
onOpenColumns={onOpenColumns}
|
||||
closePopover={(): void => setIsPopoverOpen(false)}
|
||||
/>
|
||||
}
|
||||
trigger="click"
|
||||
|
||||
@@ -155,66 +155,4 @@ describe('LogsFormatOptionsMenu (unit)', () => {
|
||||
expect(fontSizeOnChange).toHaveBeenCalledWith(FontSize.MEDIUM);
|
||||
});
|
||||
});
|
||||
|
||||
function renderWithOnOpen(
|
||||
onOpenColumns?: jest.Mock,
|
||||
selectedOptionFormat: 'table' | 'raw' | 'list' = 'table',
|
||||
): { getByTestId: ReturnType<typeof render>['getByTestId'] } {
|
||||
const items = [
|
||||
{ key: 'raw', label: 'Raw', data: { title: 'max lines per row' } },
|
||||
{ key: 'list', label: 'Default' },
|
||||
{ key: 'table', label: 'Column', data: { title: 'columns' } },
|
||||
];
|
||||
|
||||
const { getByTestId } = render(
|
||||
<LogsFormatOptionsMenu
|
||||
items={items}
|
||||
selectedOptionFormat={selectedOptionFormat}
|
||||
config={{
|
||||
format: { value: selectedOptionFormat, onChange: jest.fn() },
|
||||
maxLines: { value: 1, onChange: jest.fn() },
|
||||
fontSize: { value: FontSize.SMALL, onChange: jest.fn() },
|
||||
}}
|
||||
onOpenColumns={onOpenColumns}
|
||||
/>,
|
||||
);
|
||||
fireEvent.click(getByTestId('periscope-btn-format-options'));
|
||||
return { getByTestId };
|
||||
}
|
||||
|
||||
it('renders "Edit columns" row when format=table and onOpenColumns provided', () => {
|
||||
const onOpenColumns = jest.fn();
|
||||
const { getByTestId } = renderWithOnOpen(onOpenColumns, 'table');
|
||||
|
||||
expect(getByTestId('periscope-btn-edit-columns')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not render "Edit columns" row when onOpenColumns is not provided', () => {
|
||||
renderWithOnOpen(undefined, 'table');
|
||||
|
||||
expect(
|
||||
document.querySelector('[data-testid="periscope-btn-edit-columns"]'),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('does not render "Edit columns" row when format is not table', () => {
|
||||
renderWithOnOpen(jest.fn(), 'raw');
|
||||
|
||||
expect(
|
||||
document.querySelector('[data-testid="periscope-btn-edit-columns"]'),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('fires onOpenColumns and closes the popover when "Edit columns" is clicked', async () => {
|
||||
const onOpenColumns = jest.fn();
|
||||
const { getByTestId } = renderWithOnOpen(onOpenColumns, 'table');
|
||||
|
||||
fireEvent.click(getByTestId('periscope-btn-edit-columns'));
|
||||
|
||||
expect(onOpenColumns).toHaveBeenCalledTimes(1);
|
||||
await waitFor(() => {
|
||||
// Popover content unmounts on close.
|
||||
expect(document.querySelector('.menu-container')).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -109,16 +109,6 @@ $custom-border-color: #2c3044;
|
||||
color: color-mix(in srgb, var(--l2-foreground) 45%, transparent);
|
||||
}
|
||||
|
||||
.ant-select-clear {
|
||||
background-color: var(--l2-background);
|
||||
color: var(--l2-foreground);
|
||||
font-size: 12px;
|
||||
|
||||
&:hover {
|
||||
color: var(--l1-foreground);
|
||||
}
|
||||
}
|
||||
|
||||
// Customize tags in multiselect (dark mode by default)
|
||||
.ant-select-selection-item {
|
||||
background-color: var(--l1-border);
|
||||
@@ -402,9 +392,7 @@ $custom-border-color: #2c3044;
|
||||
// Custom dropdown styles for multi-select
|
||||
.custom-multiselect-dropdown {
|
||||
padding: 8px 0 0 0;
|
||||
// Tall enough to hold the react-virtuoso list (<=300px) + header/footer
|
||||
// so only the list scrolls (avoids a second scrollbar on this container).
|
||||
max-height: 410px;
|
||||
max-height: 350px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
scrollbar-width: thin;
|
||||
@@ -495,12 +483,8 @@ $custom-border-color: #2c3044;
|
||||
.option-checkbox {
|
||||
width: 100%;
|
||||
|
||||
// @signozhq/ui Checkbox renders children inside a <label> that is
|
||||
// content-sized by default. Make it fill the row (min-width: 0 lets it
|
||||
// shrink) so the option text below can truncate instead of overflowing.
|
||||
> label {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
> span:not(.ant-checkbox) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.all-option-text {
|
||||
@@ -517,12 +501,7 @@ $custom-border-color: #2c3044;
|
||||
width: 100%;
|
||||
|
||||
.option-label-text {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
margin-bottom: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.option-badge {
|
||||
@@ -535,30 +514,26 @@ $custom-border-color: #2c3044;
|
||||
}
|
||||
}
|
||||
|
||||
// Size the buttons to the row's resting content height (20px) and fully
|
||||
// override antd's default 32px Button box, so revealing them on hover
|
||||
// never changes the row height.
|
||||
.only-btn,
|
||||
.only-btn {
|
||||
display: none;
|
||||
}
|
||||
.toggle-btn {
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 18px;
|
||||
min-height: 0;
|
||||
padding: 0 6px;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: unset;
|
||||
}
|
||||
.only-btn:hover {
|
||||
background-color: unset;
|
||||
}
|
||||
.toggle-btn:hover {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
.option-content:hover {
|
||||
.only-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 21px;
|
||||
}
|
||||
.toggle-btn {
|
||||
display: none;
|
||||
@@ -573,6 +548,9 @@ $custom-border-color: #2c3044;
|
||||
.option-checkbox:hover {
|
||||
.toggle-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 21px;
|
||||
}
|
||||
.option-badge {
|
||||
display: none;
|
||||
|
||||
@@ -721,53 +721,6 @@ export const removeKeysFromExpression = (
|
||||
return result?.text ?? '';
|
||||
};
|
||||
|
||||
const escapeRegExp = (value: string): string =>
|
||||
value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
|
||||
export const createVariablePlaceholderRegExp = (
|
||||
variableName: string,
|
||||
): RegExp => {
|
||||
const escapedName = escapeRegExp(variableName);
|
||||
// (?![\w.]) prevents $env from matching inside $environment or $env.attr
|
||||
return new RegExp(
|
||||
`(\\$${escapedName}(?![\\w.])|\\{\\{\\s*\\.?${escapedName}\\s*\\}\\}|\\[\\[\\s*${escapedName}\\s*\\]\\])`,
|
||||
'g',
|
||||
);
|
||||
};
|
||||
|
||||
const matchesVariablePlaceholder = (
|
||||
text: string,
|
||||
variableName: string,
|
||||
): boolean => createVariablePlaceholderRegExp(variableName).test(text);
|
||||
|
||||
export const removeVariableFromExpression = (
|
||||
expression: string | undefined,
|
||||
variableName: string,
|
||||
): string => {
|
||||
if (!expression) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const queryPairs = extractQueryPairs(expression);
|
||||
|
||||
const keysToRemove = queryPairs
|
||||
.filter((pair) => {
|
||||
const singleValue = pair.value?.toString() ?? '';
|
||||
const listValues = (pair.valueList ?? []).join(' ');
|
||||
return (
|
||||
matchesVariablePlaceholder(singleValue, variableName) ||
|
||||
matchesVariablePlaceholder(listValues, variableName)
|
||||
);
|
||||
})
|
||||
.map((pair) => pair.key);
|
||||
|
||||
if (keysToRemove.length === 0) {
|
||||
return expression;
|
||||
}
|
||||
|
||||
return removeKeysFromExpression(expression, keysToRemove, `$${variableName}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert old having format to new having format
|
||||
* @param having - Array of old having objects with columnName, op, and value
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Tooltip } from 'antd';
|
||||
import refreshPaymentStatus from 'api/v3/licenses/put';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import cx from 'classnames';
|
||||
import { RefreshCcw } from '@signozhq/icons';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
|
||||
function RefreshPaymentStatus({
|
||||
btnShape,
|
||||
type,
|
||||
className,
|
||||
}: {
|
||||
btnShape?: 'default' | 'round' | 'circle';
|
||||
type?: 'button' | 'text' | 'tooltip';
|
||||
className?: string;
|
||||
}): JSX.Element {
|
||||
const { t } = useTranslation(['failedPayment']);
|
||||
const { activeLicenseRefetch } = useAppContext();
|
||||
@@ -31,33 +31,26 @@ function RefreshPaymentStatus({
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const button = (
|
||||
<Button
|
||||
variant="link"
|
||||
color={type === 'text' ? 'none' : 'secondary'}
|
||||
size="md"
|
||||
className={className}
|
||||
onClick={handleRefreshPaymentStatus}
|
||||
prefix={<RefreshCcw size={14} />}
|
||||
loading={isLoading}
|
||||
>
|
||||
{type !== 'tooltip' ? t('refreshPaymentStatus') : ''}
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<span className="refresh-payment-status-btn-wrapper">
|
||||
{type === 'tooltip' ? (
|
||||
<TooltipSimple title={t('refreshPaymentStatus')}>{button}</TooltipSimple>
|
||||
) : (
|
||||
button
|
||||
)}
|
||||
<Tooltip title={type === 'tooltip' ? t('refreshPaymentStatus') : ''}>
|
||||
<Button
|
||||
type={type === 'text' ? 'text' : 'default'}
|
||||
shape={btnShape}
|
||||
className={cx('periscope-btn', { text: type === 'text' })}
|
||||
onClick={handleRefreshPaymentStatus}
|
||||
icon={<RefreshCcw size={14} />}
|
||||
loading={isLoading}
|
||||
>
|
||||
{type !== 'tooltip' ? t('refreshPaymentStatus') : ''}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
RefreshPaymentStatus.defaultProps = {
|
||||
btnShape: 'default',
|
||||
type: 'button',
|
||||
className: undefined,
|
||||
};
|
||||
|
||||
export default RefreshPaymentStatus;
|
||||
|
||||
@@ -72,7 +72,6 @@ function TanStackTableInner<TData>(
|
||||
data,
|
||||
columns,
|
||||
columnStorageKey,
|
||||
respectColumnOrder = true,
|
||||
columnSizing: columnSizingProp,
|
||||
onColumnSizingChange,
|
||||
onColumnOrderChange,
|
||||
@@ -176,7 +175,6 @@ function TanStackTableInner<TData>(
|
||||
storageKey: columnStorageKey,
|
||||
columns,
|
||||
isGrouped,
|
||||
respectColumnOrder,
|
||||
});
|
||||
|
||||
// Use store values when columnStorageKey is provided, otherwise fall back to props/defaults
|
||||
@@ -208,7 +206,6 @@ function TanStackTableInner<TData>(
|
||||
handleRemoveColumn,
|
||||
} = useColumnHandlers({
|
||||
columnStorageKey,
|
||||
respectColumnOrder,
|
||||
effectiveSizing,
|
||||
storeSetSizing,
|
||||
storeSetOrder,
|
||||
|
||||
@@ -24,8 +24,6 @@ jest.mock('../TanStackTable.module.scss', () => ({
|
||||
}));
|
||||
|
||||
beforeAll(() => {
|
||||
// jsdom doesn't include ResizeObserver — must direct-assign rather than
|
||||
// spyOn (spyOn requires the property to already exist).
|
||||
window.ResizeObserver = jest.fn().mockImplementation(() => ({
|
||||
disconnect: jest.fn(),
|
||||
observe: jest.fn(),
|
||||
@@ -869,110 +867,4 @@ describe('TanStackTableView Integration', () => {
|
||||
expect(screen.queryByRole('navigation')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasSingleColumn — gates the Remove popover per-column', () => {
|
||||
const hasSingleColumnFlagPresent = (): boolean =>
|
||||
Boolean(document.querySelector('th[data-single-column="true"]'));
|
||||
|
||||
it('is true when only one non-pinned column exists', async () => {
|
||||
renderTanStackTable({
|
||||
props: {
|
||||
data: [{ id: '1', name: 'Item 1', value: 100 }],
|
||||
columns: [
|
||||
{
|
||||
id: 'name',
|
||||
header: 'Name',
|
||||
accessorKey: 'name',
|
||||
cell: ({ value }): string => String(value),
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Item 1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(hasSingleColumnFlagPresent()).toBe(true);
|
||||
});
|
||||
|
||||
it('is false when multiple non-pinned columns exist (all removable)', async () => {
|
||||
renderTanStackTable({});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Item 1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 3 default columns (id/name/value), none pinned, none non-removable
|
||||
// → table is not single-column.
|
||||
expect(hasSingleColumnFlagPresent()).toBe(false);
|
||||
});
|
||||
|
||||
it('is false when removable + non-removable mix exists (the body/timestamp case)', async () => {
|
||||
renderTanStackTable({
|
||||
props: {
|
||||
data: [{ id: '1', name: 'Item 1', value: 100 }],
|
||||
columns: [
|
||||
{
|
||||
id: 'name',
|
||||
header: 'Timestamp',
|
||||
accessorKey: 'name',
|
||||
enableRemove: false,
|
||||
cell: ({ value }): string => String(value),
|
||||
},
|
||||
{
|
||||
id: 'value',
|
||||
header: 'Body',
|
||||
accessorKey: 'value',
|
||||
enableRemove: false,
|
||||
cell: ({ value }): string => String(value),
|
||||
},
|
||||
{
|
||||
id: 'id',
|
||||
header: 'User',
|
||||
accessorKey: 'id',
|
||||
enableRemove: true,
|
||||
cell: ({ value }): string => String(value),
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Item 1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(hasSingleColumnFlagPresent()).toBe(false);
|
||||
});
|
||||
|
||||
it('does not count pinned columns toward the total', async () => {
|
||||
renderTanStackTable({
|
||||
props: {
|
||||
data: [{ id: '1', name: 'Item 1', value: 100 }],
|
||||
columns: [
|
||||
{
|
||||
id: 'stateIndicator',
|
||||
header: '',
|
||||
pin: 'left',
|
||||
accessorKey: 'id',
|
||||
cell: ({ value }): string => String(value),
|
||||
},
|
||||
{
|
||||
id: 'name',
|
||||
header: 'Name',
|
||||
accessorKey: 'name',
|
||||
cell: ({ value }): string => String(value),
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Item 1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 1 pinned + 1 non-pinned → only the non-pinned counts → single-column.
|
||||
expect(hasSingleColumnFlagPresent()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -168,53 +168,6 @@ describe('useColumnState', () => {
|
||||
'a',
|
||||
]);
|
||||
});
|
||||
|
||||
it('ignores stored columnOrder when respectColumnOrder=false', () => {
|
||||
const columns = [col('a'), col('b'), col('c')];
|
||||
|
||||
act(() => {
|
||||
useColumnStore.getState().initializeFromDefaults(TEST_KEY, columns);
|
||||
useColumnStore.getState().setColumnOrder(TEST_KEY, ['c', 'a', 'b']);
|
||||
});
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useColumnState({
|
||||
storageKey: TEST_KEY,
|
||||
columns,
|
||||
respectColumnOrder: false,
|
||||
}),
|
||||
);
|
||||
|
||||
// Falls through to natural columns-array order; stored order is ignored.
|
||||
expect(result.current.sortedColumns.map((c) => c.id)).toStrictEqual([
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
]);
|
||||
});
|
||||
|
||||
it('honors stored columnOrder when respectColumnOrder=true (default)', () => {
|
||||
const columns = [col('a'), col('b'), col('c')];
|
||||
|
||||
act(() => {
|
||||
useColumnStore.getState().initializeFromDefaults(TEST_KEY, columns);
|
||||
useColumnStore.getState().setColumnOrder(TEST_KEY, ['c', 'a', 'b']);
|
||||
});
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useColumnState({
|
||||
storageKey: TEST_KEY,
|
||||
columns,
|
||||
respectColumnOrder: true,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.current.sortedColumns.map((c) => c.id)).toStrictEqual([
|
||||
'c',
|
||||
'a',
|
||||
'b',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('actions', () => {
|
||||
|
||||
@@ -152,8 +152,6 @@ export type TanStackTableProps<TData> = {
|
||||
columns: TableColumnDef<TData>[];
|
||||
/** Storage key for column state persistence (visibility, sizing, ordering). When set, enables unified column management. */
|
||||
columnStorageKey?: string;
|
||||
/** When false, the table renders columns in their natural array order and the persisted columnOrder slot is ignored on read and skipped on write. Use when an external source (e.g. preferences.selectColumns) is the canonical order. Default true. */
|
||||
respectColumnOrder?: boolean;
|
||||
columnSizing?: ColumnSizingState;
|
||||
onColumnSizingChange?: Dispatch<SetStateAction<ColumnSizingState>>;
|
||||
onColumnOrderChange?: (cols: TableColumnDef<TData>[]) => void;
|
||||
|
||||
@@ -7,8 +7,6 @@ import { TableColumnDef } from './types';
|
||||
export interface UseColumnHandlersOptions<TData> {
|
||||
/** Storage key for persisting column state (enables store mode) */
|
||||
columnStorageKey?: string;
|
||||
/** When false, drag-reorder skips the persisted columnOrder write — order flows back via onColumnOrderChange only. */
|
||||
respectColumnOrder?: boolean;
|
||||
effectiveSizing: ColumnSizingState;
|
||||
storeSetSizing: (sizing: ColumnSizingState) => void;
|
||||
storeSetOrder: (columns: TableColumnDef<TData>[]) => void;
|
||||
@@ -30,7 +28,6 @@ export interface UseColumnHandlersResult<TData> {
|
||||
*/
|
||||
export function useColumnHandlers<TData>({
|
||||
columnStorageKey,
|
||||
respectColumnOrder = true,
|
||||
effectiveSizing,
|
||||
storeSetSizing,
|
||||
storeSetOrder,
|
||||
@@ -53,12 +50,12 @@ export function useColumnHandlers<TData>({
|
||||
|
||||
const handleColumnOrderChange = useCallback(
|
||||
(cols: TableColumnDef<TData>[]) => {
|
||||
if (columnStorageKey && respectColumnOrder) {
|
||||
if (columnStorageKey) {
|
||||
storeSetOrder(cols);
|
||||
}
|
||||
onColumnOrderChange?.(cols);
|
||||
},
|
||||
[columnStorageKey, respectColumnOrder, storeSetOrder, onColumnOrderChange],
|
||||
[columnStorageKey, storeSetOrder, onColumnOrderChange],
|
||||
);
|
||||
|
||||
const handleRemoveColumn = useCallback(
|
||||
|
||||
@@ -20,7 +20,6 @@ type UseColumnStateOptions<TData> = {
|
||||
storageKey?: string;
|
||||
columns: TableColumnDef<TData>[];
|
||||
isGrouped?: boolean;
|
||||
respectColumnOrder?: boolean;
|
||||
};
|
||||
|
||||
type UseColumnStateResult<TData> = {
|
||||
@@ -41,7 +40,6 @@ export function useColumnState<TData>({
|
||||
storageKey,
|
||||
columns,
|
||||
isGrouped = false,
|
||||
respectColumnOrder = true,
|
||||
}: UseColumnStateOptions<TData>): UseColumnStateResult<TData> {
|
||||
useEffect(() => {
|
||||
if (storageKey) {
|
||||
@@ -132,7 +130,7 @@ export function useColumnState<TData>({
|
||||
}, [hiddenColumnIds, columns, isGrouped]);
|
||||
|
||||
const sortedColumns = useMemo((): TableColumnDef<TData>[] => {
|
||||
if (!respectColumnOrder || columnOrder.length === 0) {
|
||||
if (columnOrder.length === 0) {
|
||||
return columns;
|
||||
}
|
||||
|
||||
@@ -146,7 +144,7 @@ export function useColumnState<TData>({
|
||||
});
|
||||
|
||||
return [...pinned, ...sortedRest];
|
||||
}, [columns, columnOrder, respectColumnOrder]);
|
||||
}, [columns, columnOrder]);
|
||||
|
||||
const hideColumn = useCallback(
|
||||
(columnId: string) => {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user