Compare commits

..

12 Commits

Author SHA1 Message Date
SagarRajput-7
1b7f13bf1d Merge branch 'SIG-6819' into SIG-6421 2025-05-27 11:48:55 +05:30
SagarRajput-7
395ddb44be Merge branch 'main' into SIG-6819 2025-05-27 11:48:46 +05:30
SagarRajput-7
9e45f181ac Merge branch 'main' into SIG-6819 2025-05-27 11:15:54 +05:30
SagarRajput-7
e974aa1a85 feat: allow custom color pallete in panel for legends 2025-05-26 10:25:30 +05:30
SagarRajput-7
e5c5ee5b1a feat: added test cases 2025-05-26 10:18:58 +05:30
SagarRajput-7
dd38d35e17 feat: allignment and fixes 2025-05-26 09:11:00 +05:30
SagarRajput-7
717c7e77a5 feat: added graph visibilty in panel edit mode also 2025-05-26 06:14:50 +05:30
SagarRajput-7
89c77676ee feat: row num adjustment 2025-05-26 06:00:45 +05:30
SagarRajput-7
7a8eb817f5 feat: removed histogram and pie from enhanced legends 2025-05-26 05:49:30 +05:30
SagarRajput-7
bb967f22af feat: created the legend marker as checkboxes 2025-05-26 05:42:59 +05:30
SagarRajput-7
66bba67799 feat: added option for right side legends 2025-05-26 05:06:15 +05:30
SagarRajput-7
522ee2f4a7 feat: added enhancements to legends in panel 2025-05-26 03:33:11 +05:30
317 changed files with 16170 additions and 28509 deletions

View File

@@ -40,7 +40,7 @@ services:
timeout: 5s
retries: 3
schema-migrator-sync:
image: signoz/signoz-schema-migrator:v0.111.42
image: signoz/signoz-schema-migrator:v0.111.41
container_name: schema-migrator-sync
command:
- sync
@@ -53,7 +53,7 @@ services:
condition: service_healthy
restart: on-failure
schema-migrator-async:
image: signoz/signoz-schema-migrator:v0.111.42
image: signoz/signoz-schema-migrator:v0.111.41
container_name: schema-migrator-async
command:
- async

View File

@@ -32,7 +32,9 @@ ex:
> Tag the relevant teams for review:
- frontend / backend / devops
- [ ] @SigNoz/frontend
- [ ] @SigNoz/backend
- [ ] @SigNoz/devops
---

View File

@@ -67,8 +67,9 @@ jobs:
echo 'TUNNEL_URL="${{ secrets.TUNNEL_URL }}"' >> frontend/.env
echo 'TUNNEL_DOMAIN="${{ secrets.TUNNEL_DOMAIN }}"' >> frontend/.env
echo 'POSTHOG_KEY="${{ secrets.POSTHOG_KEY }}"' >> frontend/.env
echo 'PYLON_APP_ID="${{ secrets.PYLON_APP_ID }}"' >> frontend/.env
echo 'APPCUES_APP_ID="${{ secrets.APPCUES_APP_ID }}"' >> frontend/.env
echo 'CUSTOMERIO_ID="${{ secrets.CUSTOMERIO_ID }}"' >> frontend/.env
echo 'CUSTOMERIO_SITE_ID="${{ secrets.CUSTOMERIO_SITE_ID }}"' >> frontend/.env
echo 'USERPILOT_KEY="${{ secrets.USERPILOT_KEY }}"' >> frontend/.env
- name: cache-dotenv
uses: actions/cache@v4
with:

View File

@@ -66,8 +66,7 @@ jobs:
echo 'CI=1' > frontend/.env
echo 'TUNNEL_URL="${{ secrets.NP_TUNNEL_URL }}"' >> frontend/.env
echo 'TUNNEL_DOMAIN="${{ secrets.NP_TUNNEL_DOMAIN }}"' >> frontend/.env
echo 'PYLON_APP_ID="${{ secrets.NP_PYLON_APP_ID }}"' >> frontend/.env
echo 'APPCUES_APP_ID="${{ secrets.NP_APPCUES_APP_ID }}"' >> frontend/.env
echo 'USERPILOT_KEY="${{ secrets.NP_USERPILOT_KEY }}"' >> frontend/.env
- name: cache-dotenv
uses: actions/cache@v4
with:

View File

@@ -33,8 +33,9 @@ jobs:
echo 'TUNNEL_URL="${{ secrets.TUNNEL_URL }}"' >> .env
echo 'TUNNEL_DOMAIN="${{ secrets.TUNNEL_DOMAIN }}"' >> .env
echo 'POSTHOG_KEY="${{ secrets.POSTHOG_KEY }}"' >> .env
echo 'PYLON_APP_ID="${{ secrets.PYLON_APP_ID }}"' >> .env
echo 'APPCUES_APP_ID="${{ secrets.APPCUES_APP_ID }}"' >> .env
echo 'CUSTOMERIO_ID="${{ secrets.CUSTOMERIO_ID }}"' >> .env
echo 'CUSTOMERIO_SITE_ID="${{ secrets.CUSTOMERIO_SITE_ID }}"' >> .env
echo 'USERPILOT_KEY="${{ secrets.USERPILOT_KEY }}"' >> .env
- name: build-frontend
run: make js-build
- name: upload-frontend-artifact

1
.gitignore vendored
View File

@@ -66,7 +66,6 @@ e2e/.auth
# go
vendor/
**/main/**
__debug_bin**
# git-town
.git-branches.toml

View File

@@ -207,11 +207,3 @@ emailing:
key_file_path:
# The path to the certificate file.
cert_file_path:
##################### Sharder (experimental) #####################
sharder:
# Specifies the sharder provider to use.
provider: noop
single:
# The org id to which this instance belongs to.
org_id: org_id

View File

@@ -174,7 +174,7 @@ services:
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
signoz:
!!merge <<: *db-depend
image: signoz/signoz:v0.85.3
image: signoz/signoz:v0.84.1
command:
- --config=/root/config/prometheus.yml
ports:
@@ -206,7 +206,7 @@ services:
retries: 3
otel-collector:
!!merge <<: *db-depend
image: signoz/signoz-otel-collector:v0.111.42
image: signoz/signoz-otel-collector:v0.111.41
command:
- --config=/etc/otel-collector-config.yaml
- --manager-config=/etc/manager-config.yaml
@@ -230,7 +230,7 @@ services:
- signoz
schema-migrator:
!!merge <<: *common
image: signoz/signoz-schema-migrator:v0.111.42
image: signoz/signoz-schema-migrator:v0.111.41
deploy:
restart_policy:
condition: on-failure

View File

@@ -110,7 +110,7 @@ services:
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
signoz:
!!merge <<: *db-depend
image: signoz/signoz:v0.85.3
image: signoz/signoz:v0.84.1
command:
- --config=/root/config/prometheus.yml
ports:
@@ -141,7 +141,7 @@ services:
retries: 3
otel-collector:
!!merge <<: *db-depend
image: signoz/signoz-otel-collector:v0.111.42
image: signoz/signoz-otel-collector:v0.111.41
command:
- --config=/etc/otel-collector-config.yaml
- --manager-config=/etc/manager-config.yaml
@@ -165,7 +165,7 @@ services:
- signoz
schema-migrator:
!!merge <<: *common
image: signoz/signoz-schema-migrator:v0.111.42
image: signoz/signoz-schema-migrator:v0.111.41
deploy:
restart_policy:
condition: on-failure

View File

@@ -177,7 +177,7 @@ services:
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
signoz:
!!merge <<: *db-depend
image: signoz/signoz:${VERSION:-v0.85.3}
image: signoz/signoz:${VERSION:-v0.84.1}
container_name: signoz
command:
- --config=/root/config/prometheus.yml
@@ -210,7 +210,7 @@ services:
# TODO: support otel-collector multiple replicas. Nginx/Traefik for loadbalancing?
otel-collector:
!!merge <<: *db-depend
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.111.42}
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.111.41}
container_name: signoz-otel-collector
command:
- --config=/etc/otel-collector-config.yaml
@@ -236,7 +236,7 @@ services:
condition: service_healthy
schema-migrator-sync:
!!merge <<: *common
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-v0.111.42}
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-v0.111.41}
container_name: schema-migrator-sync
command:
- sync
@@ -247,7 +247,7 @@ services:
condition: service_healthy
schema-migrator-async:
!!merge <<: *db-depend
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-v0.111.42}
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-v0.111.41}
container_name: schema-migrator-async
command:
- async

View File

@@ -110,7 +110,7 @@ services:
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
signoz:
!!merge <<: *db-depend
image: signoz/signoz:${VERSION:-v0.85.3}
image: signoz/signoz:${VERSION:-v0.84.1}
container_name: signoz
command:
- --config=/root/config/prometheus.yml
@@ -142,7 +142,7 @@ services:
retries: 3
otel-collector:
!!merge <<: *db-depend
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.111.42}
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.111.41}
container_name: signoz-otel-collector
command:
- --config=/etc/otel-collector-config.yaml
@@ -164,7 +164,7 @@ services:
condition: service_healthy
schema-migrator-sync:
!!merge <<: *common
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-v0.111.42}
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-v0.111.41}
container_name: schema-migrator-sync
command:
- sync
@@ -176,7 +176,7 @@ services:
restart: on-failure
schema-migrator-async:
!!merge <<: *db-depend
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-v0.111.42}
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-v0.111.41}
container_name: schema-migrator-async
command:
- async

View File

@@ -3,14 +3,12 @@ package httplicensing
import (
"context"
"encoding/json"
"github.com/SigNoz/signoz/ee/query-service/constants"
"time"
"github.com/SigNoz/signoz/ee/licensing/licensingstore/sqllicensingstore"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/licensing"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types/featuretypes"
"github.com/SigNoz/signoz/pkg/types/licensetypes"
@@ -20,31 +18,23 @@ import (
)
type provider struct {
store licensetypes.Store
zeus zeus.Zeus
config licensing.Config
settings factory.ScopedProviderSettings
orgGetter organization.Getter
stopChan chan struct{}
store licensetypes.Store
zeus zeus.Zeus
config licensing.Config
settings factory.ScopedProviderSettings
stopChan chan struct{}
}
func NewProviderFactory(store sqlstore.SQLStore, zeus zeus.Zeus, orgGetter organization.Getter) factory.ProviderFactory[licensing.Licensing, licensing.Config] {
func NewProviderFactory(store sqlstore.SQLStore, zeus zeus.Zeus) factory.ProviderFactory[licensing.Licensing, licensing.Config] {
return factory.NewProviderFactory(factory.MustNewName("http"), func(ctx context.Context, providerSettings factory.ProviderSettings, config licensing.Config) (licensing.Licensing, error) {
return New(ctx, providerSettings, config, store, zeus, orgGetter)
return New(ctx, providerSettings, config, store, zeus)
})
}
func New(ctx context.Context, ps factory.ProviderSettings, config licensing.Config, sqlstore sqlstore.SQLStore, zeus zeus.Zeus, orgGetter organization.Getter) (licensing.Licensing, error) {
func New(ctx context.Context, ps factory.ProviderSettings, config licensing.Config, sqlstore sqlstore.SQLStore, zeus zeus.Zeus) (licensing.Licensing, error) {
settings := factory.NewScopedProviderSettings(ps, "github.com/SigNoz/signoz/ee/licensing/httplicensing")
licensestore := sqllicensingstore.New(sqlstore)
return &provider{
store: licensestore,
zeus: zeus,
config: config,
settings: settings,
orgGetter: orgGetter,
stopChan: make(chan struct{}),
}, nil
return &provider{store: licensestore, zeus: zeus, config: config, settings: settings, stopChan: make(chan struct{})}, nil
}
func (provider *provider) Start(ctx context.Context) error {
@@ -76,13 +66,13 @@ func (provider *provider) Stop(ctx context.Context) error {
}
func (provider *provider) Validate(ctx context.Context) error {
organizations, err := provider.orgGetter.ListByOwnedKeyRange(ctx)
organizations, err := provider.store.ListOrganizations(ctx)
if err != nil {
return err
}
for _, organization := range organizations {
err := provider.Refresh(ctx, organization.ID)
for _, organizationID := range organizations {
err := provider.Refresh(ctx, organizationID)
if err != nil {
return err
}
@@ -261,13 +251,6 @@ func (provider *provider) GetFeatureFlags(ctx context.Context) ([]*featuretypes.
}
}
if constants.IsDotMetricsEnabled {
gettableFeatures = append(gettableFeatures, &featuretypes.GettableFeature{
Name: featuretypes.DotMetricsEnabled,
Active: true,
})
}
return gettableFeatures, nil
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/featuretypes"
"github.com/SigNoz/signoz/pkg/types/licensetypes"
"github.com/SigNoz/signoz/pkg/valuer"
@@ -81,6 +82,31 @@ func (store *store) Update(ctx context.Context, organizationID valuer.UUID, stor
return nil
}
func (store *store) ListOrganizations(ctx context.Context) ([]valuer.UUID, error) {
orgIDStrs := make([]string, 0)
err := store.sqlstore.
BunDB().
NewSelect().
Model(new(types.Organization)).
Column("id").
Scan(ctx, &orgIDStrs)
if err != nil {
return nil, err
}
orgIDs := make([]valuer.UUID, len(orgIDStrs))
for idx, orgIDStr := range orgIDStrs {
orgID, err := valuer.NewUUID(orgIDStr)
if err != nil {
return nil, err
}
orgIDs[idx] = orgID
}
return orgIDs, nil
}
func (store *store) CreateFeature(ctx context.Context, storableFeature *featuretypes.StorableFeature) error {
_, err := store.
sqlstore.

View File

@@ -9,7 +9,9 @@ import (
"go.uber.org/zap"
"github.com/SigNoz/signoz/pkg/http/render"
"github.com/SigNoz/signoz/pkg/query-service/constants"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/valuer"
)
@@ -23,11 +25,29 @@ func handleSsoError(w http.ResponseWriter, r *http.Request, redirectURL string)
// receiveSAML completes a SAML request and gets user logged in
func (ah *APIHandler) receiveSAML(w http.ResponseWriter, r *http.Request) {
claims, err := authtypes.ClaimsFromContext(r.Context())
if err != nil {
render.Error(w, err)
return
}
orgID, err := valuer.NewUUID(claims.OrgID)
if err != nil {
render.Error(w, err)
return
}
// this is the source url that initiated the login request
redirectUri := constants.GetDefaultSiteURL()
ctx := context.Background()
err := r.ParseForm()
_, err = ah.Signoz.Licensing.GetActive(ctx, orgID)
if err != nil {
zap.L().Error("[receiveSAML] sso requested but feature unavailable in org domain")
http.Redirect(w, r, fmt.Sprintf("%s?ssoerror=%s", redirectUri, "feature unavailable, please upgrade your billing plan to access this feature"), http.StatusMovedPermanently)
return
}
err = r.ParseForm()
if err != nil {
zap.L().Error("[receiveSAML] failed to process response - invalid response from IDP", zap.Error(err), zap.Any("request", r))
handleSsoError(w, r, redirectUri)
@@ -56,19 +76,6 @@ func (ah *APIHandler) receiveSAML(w http.ResponseWriter, r *http.Request) {
return
}
orgID, err := valuer.NewUUID(domain.OrgID)
if err != nil {
handleSsoError(w, r, redirectUri)
return
}
_, err = ah.Signoz.Licensing.GetActive(ctx, orgID)
if err != nil {
zap.L().Error("[receiveSAML] sso requested but feature unavailable in org domain")
http.Redirect(w, r, fmt.Sprintf("%s?ssoerror=%s", redirectUri, "feature unavailable, please upgrade your billing plan to access this feature"), http.StatusMovedPermanently)
return
}
sp, err := domain.PrepareSamlRequest(parsedState)
if err != nil {
zap.L().Error("[receiveSAML] failed to prepare saml request for domain", zap.String("domain", domain.String()), zap.Error(err))

View File

@@ -20,7 +20,6 @@ import (
"github.com/SigNoz/signoz/pkg/alertmanager"
"github.com/SigNoz/signoz/pkg/cache"
"github.com/SigNoz/signoz/pkg/http/middleware"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/prometheus"
"github.com/SigNoz/signoz/pkg/signoz"
"github.com/SigNoz/signoz/pkg/sqlstore"
@@ -114,7 +113,6 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
serverOptions.SigNoz.SQLStore,
serverOptions.SigNoz.TelemetryStore,
serverOptions.SigNoz.Prometheus,
serverOptions.SigNoz.Modules.OrgGetter,
)
if err != nil {
@@ -159,7 +157,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
}
// start the usagemanager
usageManager, err := usage.New(serverOptions.SigNoz.Licensing, serverOptions.SigNoz.TelemetryStore.ClickhouseDB(), serverOptions.SigNoz.Zeus, serverOptions.SigNoz.Modules.OrgGetter)
usageManager, err := usage.New(serverOptions.SigNoz.Licensing, serverOptions.SigNoz.TelemetryStore.ClickhouseDB(), serverOptions.SigNoz.Zeus, serverOptions.SigNoz.Modules.Organization)
if err != nil {
return nil, err
}
@@ -227,7 +225,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
&opAmpModel.AllAgents, agentConfMgr,
)
orgs, err := apiHandler.Signoz.Modules.OrgGetter.ListByOwnedKeyRange(context.Background())
orgs, err := apiHandler.Signoz.Modules.Organization.GetAll(context.Background())
if err != nil {
return nil, err
}
@@ -242,10 +240,11 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
}
func (s *Server) createPrivateServer(apiHandler *api.APIHandler) (*http.Server, error) {
r := baseapp.NewRouter()
r.Use(middleware.NewAuth(s.serverOptions.Jwt, []string{"Authorization", "Sec-WebSocket-Protocol"}, s.serverOptions.SigNoz.Sharder, s.serverOptions.SigNoz.Instrumentation.Logger()).Wrap)
r.Use(middleware.NewAPIKey(s.serverOptions.SigNoz.SQLStore, []string{"SIGNOZ-API-KEY"}, s.serverOptions.SigNoz.Instrumentation.Logger(), s.serverOptions.SigNoz.Sharder).Wrap)
r.Use(middleware.NewAuth(s.serverOptions.Jwt, []string{"Authorization", "Sec-WebSocket-Protocol"}).Wrap)
r.Use(middleware.NewAPIKey(s.serverOptions.SigNoz.SQLStore, []string{"SIGNOZ-API-KEY"}, s.serverOptions.SigNoz.Instrumentation.Logger()).Wrap)
r.Use(middleware.NewTimeout(s.serverOptions.SigNoz.Instrumentation.Logger(),
s.serverOptions.Config.APIServer.Timeout.ExcludedRoutes,
s.serverOptions.Config.APIServer.Timeout.Default,
@@ -276,8 +275,8 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*h
r := baseapp.NewRouter()
am := middleware.NewAuthZ(s.serverOptions.SigNoz.Instrumentation.Logger())
r.Use(middleware.NewAuth(s.serverOptions.Jwt, []string{"Authorization", "Sec-WebSocket-Protocol"}, s.serverOptions.SigNoz.Sharder, s.serverOptions.SigNoz.Instrumentation.Logger()).Wrap)
r.Use(middleware.NewAPIKey(s.serverOptions.SigNoz.SQLStore, []string{"SIGNOZ-API-KEY"}, s.serverOptions.SigNoz.Instrumentation.Logger(), s.serverOptions.SigNoz.Sharder).Wrap)
r.Use(middleware.NewAuth(s.serverOptions.Jwt, []string{"Authorization", "Sec-WebSocket-Protocol"}).Wrap)
r.Use(middleware.NewAPIKey(s.serverOptions.SigNoz.SQLStore, []string{"SIGNOZ-API-KEY"}, s.serverOptions.SigNoz.Instrumentation.Logger()).Wrap)
r.Use(middleware.NewTimeout(s.serverOptions.SigNoz.Instrumentation.Logger(),
s.serverOptions.Config.APIServer.Timeout.ExcludedRoutes,
s.serverOptions.Config.APIServer.Timeout.Default,
@@ -298,7 +297,6 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*h
apiHandler.RegisterMessagingQueuesRoutes(r, am)
apiHandler.RegisterThirdPartyApiRoutes(r, am)
apiHandler.MetricExplorerRoutes(r, am)
apiHandler.RegisterTraceFunnelsRoutes(r, am)
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
@@ -452,7 +450,6 @@ func makeRulesManager(
sqlstore sqlstore.SQLStore,
telemetryStore telemetrystore.TelemetryStore,
prometheus prometheus.Prometheus,
orgGetter organization.Getter,
) (*baserules.Manager, error) {
// create manager opts
managerOpts := &baserules.ManagerOptions{
@@ -468,7 +465,6 @@ func makeRulesManager(
PrepareTestRuleFunc: rules.TestNotification,
Alertmanager: alertmanager,
SQLStore: sqlstore,
OrgGetter: orgGetter,
}
// create Manager

View File

@@ -4,10 +4,6 @@ import (
"os"
)
const (
DefaultSiteURL = "https://localhost:8080"
)
var LicenseSignozIo = "https://license.signoz.io/api/v1"
var LicenseAPIKey = GetOrDefaultEnv("SIGNOZ_LICENSE_API_KEY", "")
var SaasSegmentKey = GetOrDefaultEnv("SIGNOZ_SAAS_SEGMENT_KEY", "")
@@ -24,22 +20,3 @@ func GetOrDefaultEnv(key string, fallback string) string {
}
return v
}
// constant functions that override env vars
// GetDefaultSiteURL returns default site url, primarily
// used to send saml request and allowing backend to
// handle http redirect
func GetDefaultSiteURL() string {
return GetOrDefaultEnv("SIGNOZ_SITE_URL", DefaultSiteURL)
}
const DotMetricsEnabled = "DOT_METRICS_ENABLED"
var IsDotMetricsEnabled = false
func init() {
if GetOrDefaultEnv(DotMetricsEnabled, "false") == "true" {
IsDotMetricsEnabled = true
}
}

View File

@@ -17,7 +17,6 @@ import (
"github.com/SigNoz/signoz/pkg/config/fileprovider"
"github.com/SigNoz/signoz/pkg/factory"
pkglicensing "github.com/SigNoz/signoz/pkg/licensing"
"github.com/SigNoz/signoz/pkg/modules/organization"
baseconst "github.com/SigNoz/signoz/pkg/query-service/constants"
"github.com/SigNoz/signoz/pkg/signoz"
"github.com/SigNoz/signoz/pkg/sqlstore"
@@ -134,8 +133,8 @@ func main() {
zeus.Config(),
httpzeus.NewProviderFactory(),
licensing.Config(24*time.Hour, 3),
func(sqlstore sqlstore.SQLStore, zeus pkgzeus.Zeus, orgGetter organization.Getter) factory.ProviderFactory[pkglicensing.Licensing, pkglicensing.Config] {
return httplicensing.NewProviderFactory(sqlstore, zeus, orgGetter)
func(sqlstore sqlstore.SQLStore, zeus pkgzeus.Zeus) factory.ProviderFactory[pkglicensing.Licensing, pkglicensing.Config] {
return httplicensing.NewProviderFactory(sqlstore, zeus)
},
signoz.NewEmailingProviderFactories(),
signoz.NewCacheProviderFactories(),

View File

@@ -41,16 +41,16 @@ type Manager struct {
zeus zeus.Zeus
orgGetter organization.Getter
organizationModule organization.Module
}
func New(licenseService licensing.Licensing, clickhouseConn clickhouse.Conn, zeus zeus.Zeus, orgGetter organization.Getter) (*Manager, error) {
func New(licenseService licensing.Licensing, clickhouseConn clickhouse.Conn, zeus zeus.Zeus, organizationModule organization.Module) (*Manager, error) {
m := &Manager{
clickhouseConn: clickhouseConn,
licenseService: licenseService,
scheduler: gocron.NewScheduler(time.UTC).Every(1).Day().At("00:00"), // send usage every at 00:00 UTC
zeus: zeus,
orgGetter: orgGetter,
clickhouseConn: clickhouseConn,
licenseService: licenseService,
scheduler: gocron.NewScheduler(time.UTC).Every(1).Day().At("00:00"), // send usage every at 00:00 UTC
zeus: zeus,
organizationModule: organizationModule,
}
return m, nil
}
@@ -74,7 +74,8 @@ func (lm *Manager) Start(ctx context.Context) error {
return nil
}
func (lm *Manager) UploadUsage(ctx context.Context) {
organizations, err := lm.orgGetter.ListByOwnedKeyRange(ctx)
organizations, err := lm.organizationModule.GetAll(context.Background())
if err != nil {
zap.L().Error("failed to get organizations", zap.Error(err))
return

View File

@@ -1,7 +1,6 @@
NODE_ENV="development"
BUNDLE_ANALYSER="true"
FRONTEND_API_ENDPOINT="http://localhost:8080/"
PYLON_APP_ID="pylon-app-id"
APPCUES_APP_ID="appcess-app-id"
INTERCOM_APP_ID="intercom-app-id"
CI="1"

View File

@@ -1,3 +1,3 @@
{
"rps_over_100": "You are sending data at more than 100 RPS, your ingestion may be rate limited. Please reach out to us via chat support or "
"rps_over_100": "You are sending data at more than 100 RPS, your ingestion may be rate limited. Please reach out to us via Intercom support or "
}

View File

@@ -1,3 +1,3 @@
{
"rps_over_100": "You are sending data at more than 100 RPS, your ingestion may be rate limited. Please reach out to us via chat support or "
"rps_over_100": "You are sending data at more than 100 RPS, your ingestion may be rate limited. Please reach out to us via Intercom support or "
}

View File

@@ -103,20 +103,6 @@ function App(): JSX.Element {
if (domain) {
logEvent('Domain Identified', groupTraits, 'group');
}
if (window && window.Appcues) {
window.Appcues.identify(email, {
name: displayName,
tenant_id: hostNameParts[0],
data_region: hostNameParts[1],
tenant_url: hostname,
company_domain: domain,
companyName: orgName,
email,
paidUser: !!trialInfo?.trialConvertedToSubscription,
});
}
Userpilot.identify(email, {
email,
@@ -151,6 +137,18 @@ function App(): JSX.Element {
source: 'signoz-ui',
isPaidUser: !!trialInfo?.trialConvertedToSubscription,
});
if (
window.cioanalytics &&
typeof window.cioanalytics.identify === 'function'
) {
window.cioanalytics.reset();
window.cioanalytics.identify(email, {
name: user.displayName,
email,
role: user.role,
});
}
}
},
[
@@ -215,13 +213,13 @@ function App(): JSX.Element {
useEffect(() => {
if (pathname === ROUTES.ONBOARDING) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.Pylon('hideChatBubble');
window.Intercom('update', {
hide_default_launcher: true,
});
} else {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.Pylon('showChatBubble');
window.Intercom('update', {
hide_default_launcher: false,
});
}
}, [pathname]);
@@ -256,13 +254,11 @@ function App(): JSX.Element {
!showAddCreditCardModal &&
(isCloudUser || isEnterpriseSelfHostedUser)
) {
window.pylon = {
chat_settings: {
app_id: process.env.PYLON_APP_ID,
email: user.email,
name: user.displayName,
},
};
window.Intercom('boot', {
app_id: process.env.INTERCOM_APP_ID,
email: user?.email || '',
name: user?.displayName || '',
});
}
}
}, [
@@ -324,6 +320,10 @@ function App(): JSX.Element {
} else {
posthog.reset();
Sentry.close();
if (window.cioanalytics && typeof window.cioanalytics.reset === 'function') {
window.cioanalytics.reset();
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isCloudUser, isEnterpriseSelfHostedUser]);

View File

@@ -4,11 +4,7 @@
import getLocalStorageApi from 'api/browser/localstorage/get';
import loginApi from 'api/v1/login/login';
import afterLogin from 'AppRoutes/utils';
import axios, {
AxiosError,
AxiosResponse,
InternalAxiosRequestConfig,
} from 'axios';
import axios, { AxiosResponse, InternalAxiosRequestConfig } from 'axios';
import { ENVIRONMENT } from 'constants/env';
import { Events } from 'constants/events';
import { LOCALSTORAGE } from 'constants/localStorage';
@@ -87,27 +83,24 @@ const interceptorRejected = async (
true,
);
try {
const reResponse = await axios(
`${value.config.baseURL}${value.config.url?.substring(1)}`,
{
method: value.config.method,
headers: {
...value.config.headers,
Authorization: `Bearer ${response.data.accessJwt}`,
},
data: {
...JSON.parse(value.config.data || '{}'),
},
const reResponse = await axios(
`${value.config.baseURL}${value.config.url?.substring(1)}`,
{
method: value.config.method,
headers: {
...value.config.headers,
Authorization: `Bearer ${response.data.accessJwt}`,
},
);
data: {
...JSON.parse(value.config.data || '{}'),
},
},
);
if (reResponse.status === 200) {
return await Promise.resolve(reResponse);
} catch (error) {
if ((error as AxiosError)?.response?.status === 401) {
Logout();
}
}
Logout();
return await Promise.reject(reResponse);
} catch (error) {
Logout();
}

View File

@@ -5,8 +5,6 @@ import { ErrorResponse, SuccessResponse } from 'types/api';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { UnderscoreToDotMap } from '../utils';
export interface K8sClustersListPayload {
filters: TagFilter;
groupBy?: BaseAutocompleteData[];
@@ -42,80 +40,23 @@ export interface K8sClustersListResponse {
};
}
export const clustersMetaMap = [
{ dot: 'k8s.cluster.name', under: 'k8s_cluster_name' },
{ dot: 'k8s.cluster.uid', under: 'k8s_cluster_uid' },
] as const;
export function mapClustersMeta(
raw: Record<string, unknown>,
): K8sClustersData['meta'] {
const out: Record<string, unknown> = { ...raw };
clustersMetaMap.forEach(({ dot, under }) => {
if (dot in raw) {
const v = raw[dot];
out[under] = typeof v === 'string' ? v : raw[under];
}
});
return out as K8sClustersData['meta'];
}
export const getK8sClustersList = async (
props: K8sClustersListPayload,
signal?: AbortSignal,
headers?: Record<string, string>,
dotMetricsEnabled = false,
): Promise<SuccessResponse<K8sClustersListResponse> | ErrorResponse> => {
try {
const requestProps =
dotMetricsEnabled && Array.isArray(props.filters?.items)
? {
...props,
filters: {
...props.filters,
items: props.filters.items.reduce<typeof props.filters.items>(
(acc, item) => {
if (item.value === undefined) return acc;
if (
item.key &&
typeof item.key === 'object' &&
'key' in item.key &&
typeof item.key.key === 'string'
) {
const mappedKey = UnderscoreToDotMap[item.key.key] ?? item.key.key;
acc.push({
...item,
key: { ...item.key, key: mappedKey },
});
} else {
acc.push(item);
}
return acc;
},
[] as typeof props.filters.items,
),
},
}
: props;
const response = await axios.post('/clusters/list', requestProps, {
const response = await axios.post('/clusters/list', props, {
signal,
headers,
});
const payload: K8sClustersListResponse = response.data;
// one-liner meta mapping
payload.data.records = payload.data.records.map((record) => ({
...record,
meta: mapClustersMeta(record.meta as Record<string, unknown>),
}));
return {
statusCode: 200,
error: null,
message: 'Success',
payload,
params: requestProps,
payload: response.data,
params: props,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);

View File

@@ -5,8 +5,6 @@ import { ErrorResponse, SuccessResponse } from 'types/api';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { UnderscoreToDotMap } from '../utils';
export interface K8sDaemonSetsListPayload {
filters: TagFilter;
groupBy?: BaseAutocompleteData[];
@@ -48,82 +46,23 @@ export interface K8sDaemonSetsListResponse {
};
}
export const daemonSetsMetaMap = [
{ dot: 'k8s.namespace.name', under: 'k8s_namespace_name' },
{ dot: 'k8s.daemonset.name', under: 'k8s_daemonset_name' },
{ dot: 'k8s.cluster.name', under: 'k8s_cluster_name' },
] as const;
export function mapDaemonSetsMeta(
raw: Record<string, unknown>,
): K8sDaemonSetsData['meta'] {
const out: Record<string, unknown> = { ...raw };
daemonSetsMetaMap.forEach(({ dot, under }) => {
if (dot in raw) {
const v = raw[dot];
out[under] = typeof v === 'string' ? v : raw[under];
}
});
return out as K8sDaemonSetsData['meta'];
}
export const getK8sDaemonSetsList = async (
props: K8sDaemonSetsListPayload,
signal?: AbortSignal,
headers?: Record<string, string>,
dotMetricsEnabled = false,
): Promise<SuccessResponse<K8sDaemonSetsListResponse> | ErrorResponse> => {
try {
// filter prep (unchanged)…
const requestProps =
dotMetricsEnabled && Array.isArray(props.filters?.items)
? {
...props,
filters: {
...props.filters,
items: props.filters.items.reduce<typeof props.filters.items>(
(acc, item) => {
if (item.value === undefined) return acc;
if (
item.key &&
typeof item.key === 'object' &&
'key' in item.key &&
typeof item.key.key === 'string'
) {
const mappedKey = UnderscoreToDotMap[item.key.key] ?? item.key.key;
acc.push({
...item,
key: { ...item.key, key: mappedKey },
});
} else {
acc.push(item);
}
return acc;
},
[] as typeof props.filters.items,
),
},
}
: props;
const response = await axios.post('/daemonsets/list', requestProps, {
const response = await axios.post('/daemonsets/list', props, {
signal,
headers,
});
const payload: K8sDaemonSetsListResponse = response.data;
// single-line meta mapping
payload.data.records = payload.data.records.map((record) => ({
...record,
meta: mapDaemonSetsMeta(record.meta as Record<string, unknown>),
}));
return {
statusCode: 200,
error: null,
message: 'Success',
payload,
params: requestProps,
payload: response.data,
params: props,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);

View File

@@ -5,8 +5,6 @@ import { ErrorResponse, SuccessResponse } from 'types/api';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { UnderscoreToDotMap } from '../utils';
export interface K8sDeploymentsListPayload {
filters: TagFilter;
groupBy?: BaseAutocompleteData[];
@@ -48,81 +46,23 @@ export interface K8sDeploymentsListResponse {
};
}
export const deploymentsMetaMap = [
{ dot: 'k8s.cluster.name', under: 'k8s_cluster_name' },
{ dot: 'k8s.deployment.name', under: 'k8s_deployment_name' },
{ dot: 'k8s.namespace.name', under: 'k8s_namespace_name' },
] as const;
export function mapDeploymentsMeta(
raw: Record<string, unknown>,
): K8sDeploymentsData['meta'] {
const out: Record<string, unknown> = { ...raw };
deploymentsMetaMap.forEach(({ dot, under }) => {
if (dot in raw) {
const v = raw[dot];
out[under] = typeof v === 'string' ? v : raw[under];
}
});
return out as K8sDeploymentsData['meta'];
}
export const getK8sDeploymentsList = async (
props: K8sDeploymentsListPayload,
signal?: AbortSignal,
headers?: Record<string, string>,
dotMetricsEnabled = false,
): Promise<SuccessResponse<K8sDeploymentsListResponse> | ErrorResponse> => {
try {
const requestProps =
dotMetricsEnabled && Array.isArray(props.filters?.items)
? {
...props,
filters: {
...props.filters,
items: props.filters.items.reduce<typeof props.filters.items>(
(acc, item) => {
if (item.value === undefined) return acc;
if (
item.key &&
typeof item.key === 'object' &&
'key' in item.key &&
typeof item.key.key === 'string'
) {
const mappedKey = UnderscoreToDotMap[item.key.key] ?? item.key.key;
acc.push({
...item,
key: { ...item.key, key: mappedKey },
});
} else {
acc.push(item);
}
return acc;
},
[] as typeof props.filters.items,
),
},
}
: props;
const response = await axios.post('/deployments/list', requestProps, {
const response = await axios.post('/deployments/list', props, {
signal,
headers,
});
const payload: K8sDeploymentsListResponse = response.data;
// single-line mapping
payload.data.records = payload.data.records.map((record) => ({
...record,
meta: mapDeploymentsMeta(record.meta as Record<string, unknown>),
}));
return {
statusCode: 200,
error: null,
message: 'Success',
payload,
params: requestProps,
payload: response.data,
params: props,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);

View File

@@ -5,8 +5,6 @@ import { ErrorResponse, SuccessResponse } from 'types/api';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { UnderscoreToDotMap } from '../utils';
export interface K8sJobsListPayload {
filters: TagFilter;
groupBy?: BaseAutocompleteData[];
@@ -50,79 +48,23 @@ export interface K8sJobsListResponse {
};
}
export const jobsMetaMap = [
{ dot: 'k8s.cluster.name', under: 'k8s_cluster_name' },
{ dot: 'k8s.job.name', under: 'k8s_job_name' },
{ dot: 'k8s.namespace.name', under: 'k8s_namespace_name' },
] as const;
export function mapJobsMeta(raw: Record<string, unknown>): K8sJobsData['meta'] {
const out: Record<string, unknown> = { ...raw };
jobsMetaMap.forEach(({ dot, under }) => {
if (dot in raw) {
const v = raw[dot];
out[under] = typeof v === 'string' ? v : raw[under];
}
});
return out as K8sJobsData['meta'];
}
export const getK8sJobsList = async (
props: K8sJobsListPayload,
signal?: AbortSignal,
headers?: Record<string, string>,
dotMetricsEnabled = false,
): Promise<SuccessResponse<K8sJobsListResponse> | ErrorResponse> => {
try {
const requestProps =
dotMetricsEnabled && Array.isArray(props.filters?.items)
? {
...props,
filters: {
...props.filters,
items: props.filters.items.reduce<typeof props.filters.items>(
(acc, item) => {
if (item.value === undefined) return acc;
if (
item.key &&
typeof item.key === 'object' &&
'key' in item.key &&
typeof item.key.key === 'string'
) {
const mappedKey = UnderscoreToDotMap[item.key.key] ?? item.key.key;
acc.push({
...item,
key: { ...item.key, key: mappedKey },
});
} else {
acc.push(item);
}
return acc;
},
[] as typeof props.filters.items,
),
},
}
: props;
const response = await axios.post('/jobs/list', requestProps, {
const response = await axios.post('/jobs/list', props, {
signal,
headers,
});
const payload: K8sJobsListResponse = response.data;
// one-liner meta mapping
payload.data.records = payload.data.records.map((record) => ({
...record,
meta: mapJobsMeta(record.meta as Record<string, unknown>),
}));
return {
statusCode: 200,
error: null,
message: 'Success',
payload,
params: requestProps,
payload: response.data,
params: props,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);

View File

@@ -5,8 +5,6 @@ import { ErrorResponse, SuccessResponse } from 'types/api';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { UnderscoreToDotMap } from '../utils';
export interface K8sNamespacesListPayload {
filters: TagFilter;
groupBy?: BaseAutocompleteData[];
@@ -40,79 +38,23 @@ export interface K8sNamespacesListResponse {
};
}
export const namespacesMetaMap = [
{ dot: 'k8s.cluster.name', under: 'k8s_cluster_name' },
{ dot: 'k8s.namespace.name', under: 'k8s_namespace_name' },
] as const;
export function mapNamespacesMeta(
raw: Record<string, unknown>,
): K8sNamespacesData['meta'] {
const out: Record<string, unknown> = { ...raw };
namespacesMetaMap.forEach(({ dot, under }) => {
if (dot in raw) {
const v = raw[dot];
out[under] = typeof v === 'string' ? v : raw[under];
}
});
return out as K8sNamespacesData['meta'];
}
export const getK8sNamespacesList = async (
props: K8sNamespacesListPayload,
signal?: AbortSignal,
headers?: Record<string, string>,
dotMetricsEnabled = false,
): Promise<SuccessResponse<K8sNamespacesListResponse> | ErrorResponse> => {
try {
const requestProps =
dotMetricsEnabled && Array.isArray(props.filters?.items)
? {
...props,
filters: {
...props.filters,
items: props.filters.items.reduce<typeof props.filters.items>(
(acc, item) => {
if (item.value === undefined) return acc;
if (
item.key &&
typeof item.key === 'object' &&
'key' in item.key &&
typeof item.key.key === 'string'
) {
const mappedKey = UnderscoreToDotMap[item.key.key] ?? item.key.key;
acc.push({
...item,
key: { ...item.key, key: mappedKey },
});
} else {
acc.push(item);
}
return acc;
},
[] as typeof props.filters.items,
),
},
}
: props;
const response = await axios.post('/namespaces/list', requestProps, {
const response = await axios.post('/namespaces/list', props, {
signal,
headers,
});
const payload: K8sNamespacesListResponse = response.data;
payload.data.records = payload.data.records.map((record) => ({
...record,
meta: mapNamespacesMeta(record.meta as Record<string, unknown>),
}));
return {
statusCode: 200,
error: null,
message: 'Success',
payload,
params: requestProps,
payload: response.data,
params: props,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);

View File

@@ -5,8 +5,6 @@ import { ErrorResponse, SuccessResponse } from 'types/api';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { UnderscoreToDotMap } from '../utils';
export interface K8sNodesListPayload {
filters: TagFilter;
groupBy?: BaseAutocompleteData[];
@@ -43,81 +41,23 @@ export interface K8sNodesListResponse {
};
}
export const nodesMetaMap = [
{ dot: 'k8s.node.name', under: 'k8s_node_name' },
{ dot: 'k8s.node.uid', under: 'k8s_node_uid' },
{ dot: 'k8s.cluster.name', under: 'k8s_cluster_name' },
] as const;
export function mapNodesMeta(
raw: Record<string, unknown>,
): K8sNodesData['meta'] {
const out: Record<string, unknown> = { ...raw };
nodesMetaMap.forEach(({ dot, under }) => {
if (dot in raw) {
const v = raw[dot];
out[under] = typeof v === 'string' ? v : raw[under];
}
});
return out as K8sNodesData['meta'];
}
export const getK8sNodesList = async (
props: K8sNodesListPayload,
signal?: AbortSignal,
headers?: Record<string, string>,
dotMetricsEnabled = false,
): Promise<SuccessResponse<K8sNodesListResponse> | ErrorResponse> => {
try {
const requestProps =
dotMetricsEnabled && Array.isArray(props.filters?.items)
? {
...props,
filters: {
...props.filters,
items: props.filters.items.reduce<typeof props.filters.items>(
(acc, item) => {
if (item.value === undefined) return acc;
if (
item.key &&
typeof item.key === 'object' &&
'key' in item.key &&
typeof item.key.key === 'string'
) {
const mappedKey = UnderscoreToDotMap[item.key.key] ?? item.key.key;
acc.push({
...item,
key: { ...item.key, key: mappedKey },
});
} else {
acc.push(item);
}
return acc;
},
[] as typeof props.filters.items,
),
},
}
: props;
const response = await axios.post('/nodes/list', requestProps, {
const response = await axios.post('/nodes/list', props, {
signal,
headers,
});
const payload: K8sNodesListResponse = response.data;
// one-liner to map dot→underscore
payload.data.records = payload.data.records.map((record) => ({
...record,
meta: mapNodesMeta(record.meta as Record<string, unknown>),
}));
return {
statusCode: 200,
error: null,
message: 'Success',
payload,
params: requestProps,
payload: response.data,
params: props,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);

View File

@@ -5,8 +5,6 @@ import { ErrorResponse, SuccessResponse } from 'types/api';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { UnderscoreToDotMap } from '../utils';
export interface K8sPodsListPayload {
filters: TagFilter;
groupBy?: BaseAutocompleteData[];
@@ -71,87 +69,23 @@ export interface K8sPodsListResponse {
};
}
export const podsMetaMap = [
{ dot: 'k8s.cronjob.name', under: 'k8s_cronjob_name' },
{ dot: 'k8s.daemonset.name', under: 'k8s_daemonset_name' },
{ dot: 'k8s.deployment.name', under: 'k8s_deployment_name' },
{ dot: 'k8s.job.name', under: 'k8s_job_name' },
{ dot: 'k8s.namespace.name', under: 'k8s_namespace_name' },
{ dot: 'k8s.node.name', under: 'k8s_node_name' },
{ dot: 'k8s.pod.name', under: 'k8s_pod_name' },
{ dot: 'k8s.pod.uid', under: 'k8s_pod_uid' },
{ dot: 'k8s.statefulset.name', under: 'k8s_statefulset_name' },
{ dot: 'k8s.cluster.name', under: 'k8s_cluster_name' },
] as const;
export function mapPodsMeta(raw: Record<string, unknown>): K8sPodsData['meta'] {
// clone everything
const out: Record<string, unknown> = { ...raw };
// overlay only the dot→under mappings
podsMetaMap.forEach(({ dot, under }) => {
if (dot in raw) {
const v = raw[dot];
out[under] = typeof v === 'string' ? v : raw[under];
}
});
return out as K8sPodsData['meta'];
}
// getK8sPodsList
export const getK8sPodsList = async (
props: K8sPodsListPayload,
signal?: AbortSignal,
headers?: Record<string, string>,
dotMetricsEnabled = false,
): Promise<SuccessResponse<K8sPodsListResponse> | ErrorResponse> => {
try {
const requestProps =
dotMetricsEnabled && Array.isArray(props.filters?.items)
? {
...props,
filters: {
...props.filters,
items: props.filters.items.reduce<typeof props.filters.items>(
(acc, item) => {
if (item.value === undefined) return acc;
if (
item.key &&
typeof item.key === 'object' &&
'key' in item.key &&
typeof item.key.key === 'string'
) {
const mappedKey = UnderscoreToDotMap[item.key.key] ?? item.key.key;
acc.push({
...item,
key: { ...item.key, key: mappedKey },
});
} else {
acc.push(item);
}
return acc;
},
[] as typeof props.filters.items,
),
},
}
: props;
const response = await axios.post('/pods/list', requestProps, {
const response = await axios.post('/pods/list', props, {
signal,
headers,
});
const payload: K8sPodsListResponse = response.data;
payload.data.records = payload.data.records.map((record) => ({
...record,
meta: mapPodsMeta(record.meta as Record<string, unknown>),
}));
return {
statusCode: 200,
error: null,
message: 'Success',
payload,
params: requestProps,
payload: response.data,
params: props,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);

View File

@@ -5,8 +5,6 @@ import { ErrorResponse, SuccessResponse } from 'types/api';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { UnderscoreToDotMap } from '../utils';
export interface K8sVolumesListPayload {
filters: TagFilter;
groupBy?: BaseAutocompleteData[];
@@ -49,92 +47,23 @@ export interface K8sVolumesListResponse {
};
}
export const volumesMetaMap: Array<{
dot: keyof Record<string, unknown>;
under: keyof K8sVolumesData['meta'];
}> = [
{ dot: 'k8s.cluster.name', under: 'k8s_cluster_name' },
{ dot: 'k8s.namespace.name', under: 'k8s_namespace_name' },
{ dot: 'k8s.node.name', under: 'k8s_node_name' },
{
dot: 'k8s.persistentvolumeclaim.name',
under: 'k8s_persistentvolumeclaim_name',
},
{ dot: 'k8s.pod.name', under: 'k8s_pod_name' },
{ dot: 'k8s.pod.uid', under: 'k8s_pod_uid' },
{ dot: 'k8s.statefulset.name', under: 'k8s_statefulset_name' },
];
export function mapVolumesMeta(
rawMeta: Record<string, unknown>,
): K8sVolumesData['meta'] {
// start with everything that was already there
const out: Record<string, unknown> = { ...rawMeta };
// for each dot→under rule, if the raw has the dot, overwrite the underscore
volumesMetaMap.forEach(({ dot, under }) => {
if (dot in rawMeta) {
const val = rawMeta[dot];
out[under] = typeof val === 'string' ? val : rawMeta[under];
}
});
return out as K8sVolumesData['meta'];
}
export const getK8sVolumesList = async (
props: K8sVolumesListPayload,
signal?: AbortSignal,
headers?: Record<string, string>,
dotMetricsEnabled = false,
): Promise<SuccessResponse<K8sVolumesListResponse> | ErrorResponse> => {
try {
// Prepare filters
const requestProps =
dotMetricsEnabled && Array.isArray(props.filters?.items)
? {
...props,
filters: {
...props.filters,
items: props.filters.items.reduce<typeof props.filters.items>(
(acc, item) => {
if (item.value === undefined) return acc;
if (
item.key &&
typeof item.key === 'object' &&
'key' in item.key &&
typeof item.key.key === 'string'
) {
const mappedKey = UnderscoreToDotMap[item.key.key] ?? item.key.key;
acc.push({ ...item, key: { ...item.key, key: mappedKey } });
} else {
acc.push(item);
}
return acc;
},
[] as typeof props.filters.items,
),
},
}
: props;
const response = await axios.post('/pvcs/list', requestProps, {
const response = await axios.post('/pvcs/list', props, {
signal,
headers,
});
const payload: K8sVolumesListResponse = response.data;
payload.data.records = payload.data.records.map((record) => ({
...record,
meta: mapVolumesMeta(record.meta as Record<string, unknown>),
}));
return {
statusCode: 200,
error: null,
message: 'Success',
payload,
params: requestProps,
payload: response.data,
params: props,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);

View File

@@ -5,8 +5,6 @@ import { ErrorResponse, SuccessResponse } from 'types/api';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { UnderscoreToDotMap } from '../utils';
export interface K8sStatefulSetsListPayload {
filters: TagFilter;
groupBy?: BaseAutocompleteData[];
@@ -47,78 +45,23 @@ export interface K8sStatefulSetsListResponse {
};
}
export const statefulSetsMetaMap = [
{ dot: 'k8s.statefulset.name', under: 'k8s_statefulset_name' },
{ dot: 'k8s.namespace.name', under: 'k8s_namespace_name' },
] as const;
export function mapStatefulSetsMeta(
raw: Record<string, unknown>,
): K8sStatefulSetsData['meta'] {
const out: Record<string, unknown> = { ...raw };
statefulSetsMetaMap.forEach(({ dot, under }) => {
if (dot in raw) {
const v = raw[dot];
out[under] = typeof v === 'string' ? v : raw[under];
}
});
return out as K8sStatefulSetsData['meta'];
}
export const getK8sStatefulSetsList = async (
props: K8sStatefulSetsListPayload,
signal?: AbortSignal,
headers?: Record<string, string>,
dotMetricsEnabled = false,
): Promise<SuccessResponse<K8sStatefulSetsListResponse> | ErrorResponse> => {
try {
// Prepare filters
const requestProps =
dotMetricsEnabled && Array.isArray(props.filters?.items)
? {
...props,
filters: {
...props.filters,
items: props.filters.items.reduce<typeof props.filters.items>(
(acc, item) => {
if (item.value === undefined) return acc;
if (
item.key &&
typeof item.key === 'object' &&
'key' in item.key &&
typeof item.key.key === 'string'
) {
const mappedKey = UnderscoreToDotMap[item.key.key] ?? item.key.key;
acc.push({ ...item, key: { ...item.key, key: mappedKey } });
} else {
acc.push(item);
}
return acc;
},
[] as typeof props.filters.items,
),
},
}
: props;
const response = await axios.post('/statefulsets/list', requestProps, {
const response = await axios.post('/statefulsets/list', props, {
signal,
headers,
});
const payload: K8sStatefulSetsListResponse = response.data;
// apply our helper
payload.data.records = payload.data.records.map((record) => ({
...record,
meta: mapStatefulSetsMeta(record.meta as Record<string, unknown>),
}));
return {
statusCode: 200,
error: null,
message: 'Success',
payload,
params: requestProps,
payload: response.data,
params: props,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);

View File

@@ -167,8 +167,8 @@ interface UpdateFunnelDescriptionPayload {
export const saveFunnelDescription = async (
payload: UpdateFunnelDescriptionPayload,
): Promise<SuccessResponse<FunnelData> | ErrorResponse> => {
const response: AxiosResponse = await axios.put(
`${FUNNELS_BASE_PATH}/${payload.funnel_id}`,
const response: AxiosResponse = await axios.post(
`${FUNNELS_BASE_PATH}/save`,
payload,
);

View File

@@ -15,21 +15,13 @@ export const Logout = (): void => {
deleteLocalStorageKey(LOCALSTORAGE.QUICK_FILTERS_SETTINGS_ANNOUNCEMENT);
window.dispatchEvent(new CustomEvent('LOGOUT'));
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (window && window.Intercom) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.Intercom('shutdown');
}
history.push(ROUTES.LOGIN);
};
export const UnderscoreToDotMap: Record<string, string> = {
k8s_cluster_name: 'k8s.cluster.name',
k8s_cluster_uid: 'k8s.cluster.uid',
k8s_namespace_name: 'k8s.namespace.name',
k8s_node_name: 'k8s.node.name',
k8s_node_uid: 'k8s.node.uid',
k8s_pod_name: 'k8s.pod.name',
k8s_pod_uid: 'k8s.pod.uid',
k8s_deployment_name: 'k8s.deployment.name',
k8s_daemonset_name: 'k8s.daemonset.name',
k8s_statefulset_name: 'k8s.statefulset.name',
k8s_cronjob_name: 'k8s.cronjob.name',
k8s_job_name: 'k8s.job.name',
k8s_persistentvolumeclaim_name: 'k8s.persistentvolumeclaim.name',
};

View File

@@ -93,7 +93,7 @@
padding: 4px;
margin: 0;
// this is to offset chat support icon
// this is to offset intercom icon
padding-right: 72px;
.ant-pagination-item {

View File

@@ -2,7 +2,7 @@ import { Button, Modal, Typography } from 'antd';
import logEvent from 'api/common/logEvent';
import updateCreditCardApi from 'api/v1/checkout/create';
import { useNotifications } from 'hooks/useNotifications';
import { CreditCard, MessageSquareText, X } from 'lucide-react';
import { CreditCard, X } from 'lucide-react';
import { useState } from 'react';
import { useMutation } from 'react-query';
import { useLocation } from 'react-router-dom';
@@ -49,7 +49,7 @@ export default function ChatSupportGateway(): JSX.Element {
const handleAddCreditCard = (): void => {
logEvent('Add Credit card modal: Clicked', {
source: `chat support icon`,
source: `intercom icon`,
page: pathname,
});
@@ -65,14 +65,20 @@ export default function ChatSupportGateway(): JSX.Element {
className="chat-support-gateway-btn"
onClick={(): void => {
logEvent('Disabled Chat Support: Clicked', {
source: `chat support icon`,
source: `intercom icon`,
page: pathname,
});
setIsAddCreditCardModalOpen(true);
}}
>
<MessageSquareText size={24} />
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 28 32"
className="chat-support-gateway-btn-icon"
>
<path d="M28 32s-4.714-1.855-8.527-3.34H3.437C1.54 28.66 0 27.026 0 25.013V3.644C0 1.633 1.54 0 3.437 0h21.125c1.898 0 3.437 1.632 3.437 3.645v18.404H28V32zm-4.139-11.982a.88.88 0 00-1.292-.105c-.03.026-3.015 2.681-8.57 2.681-5.486 0-8.517-2.636-8.571-2.684a.88.88 0 00-1.29.107 1.01 1.01 0 00-.219.708.992.992 0 00.318.664c.142.128 3.537 3.15 9.762 3.15 6.226 0 9.621-3.022 9.763-3.15a.992.992 0 00.317-.664 1.01 1.01 0 00-.218-.707z" />
</svg>
</Button>
</div>

View File

@@ -7,11 +7,9 @@ import logEvent from 'api/common/logEvent';
import cx from 'classnames';
import { DateTimeRangeType } from 'container/TopNav/CustomDateTimeModal';
import {
CustomTimeType,
FixedDurationSuggestionOptions,
Options,
RelativeDurationSuggestionOptions,
Time,
} from 'container/TopNav/DateTimeSelectionV2/config';
import dayjs from 'dayjs';
import { isValidTimeFormat } from 'lib/getMinMax';
@@ -58,10 +56,6 @@ interface CustomTimePickerProps {
setCustomDTPickerVisible?: Dispatch<SetStateAction<boolean>>;
onCustomDateHandler?: (dateTimeRange: DateTimeRangeType) => void;
handleGoLive?: () => void;
onTimeChange?: (
interval: Time | CustomTimeType,
dateTimeRange?: [number, number],
) => void;
}
function CustomTimePicker({
@@ -79,7 +73,6 @@ function CustomTimePicker({
setCustomDTPickerVisible,
onCustomDateHandler,
handleGoLive,
onTimeChange,
}: CustomTimePickerProps): JSX.Element {
const [
selectedTimePlaceholderValue,
@@ -343,7 +336,6 @@ function CustomTimePicker({
setActiveView={setActiveView}
setIsOpenedFromFooter={setIsOpenedFromFooter}
isOpenedFromFooter={isOpenedFromFooter}
onTimeChange={onTimeChange}
/>
) : (
content
@@ -413,5 +405,4 @@ CustomTimePicker.defaultProps = {
onCustomDateHandler: noop,
handleGoLive: noop,
onCustomTimeStatusUpdate: noop,
onTimeChange: undefined,
};

View File

@@ -7,11 +7,9 @@ import cx from 'classnames';
import ROUTES from 'constants/routes';
import { DateTimeRangeType } from 'container/TopNav/CustomDateTimeModal';
import {
CustomTimeType,
LexicalContext,
Option,
RelativeDurationSuggestionOptions,
Time,
} from 'container/TopNav/DateTimeSelectionV2/config';
import { Clock, PenLine } from 'lucide-react';
import { useTimezone } from 'providers/Timezone';
@@ -37,10 +35,6 @@ interface CustomTimePickerPopoverContentProps {
setActiveView: Dispatch<SetStateAction<'datetime' | 'timezone'>>;
isOpenedFromFooter: boolean;
setIsOpenedFromFooter: Dispatch<SetStateAction<boolean>>;
onTimeChange?: (
interval: Time | CustomTimeType,
dateTimeRange?: [number, number],
) => void;
}
// eslint-disable-next-line sonarjs/cognitive-complexity
@@ -57,7 +51,6 @@ function CustomTimePickerPopoverContent({
setActiveView,
isOpenedFromFooter,
setIsOpenedFromFooter,
onTimeChange,
}: CustomTimePickerPopoverContentProps): JSX.Element {
const { pathname } = useLocation();
@@ -150,7 +143,6 @@ function CustomTimePickerPopoverContent({
setIsOpen={setIsOpen}
onCustomDateHandler={onCustomDateHandler}
selectedTime={selectedTime}
onTimeChange={onTimeChange}
/>
) : (
<div className="relative-times-container">
@@ -189,8 +181,4 @@ function CustomTimePickerPopoverContent({
);
}
CustomTimePickerPopoverContent.defaultProps = {
onTimeChange: undefined,
};
export default CustomTimePickerPopoverContent;

View File

@@ -1,14 +1,9 @@
/* eslint-disable react/jsx-props-no-spreading */
import './RangePickerModal.styles.scss';
import { DatePicker } from 'antd';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { DateTimeRangeType } from 'container/TopNav/CustomDateTimeModal';
import {
CustomTimeType,
LexicalContext,
Time,
} from 'container/TopNav/DateTimeSelectionV2/config';
import { LexicalContext } from 'container/TopNav/DateTimeSelectionV2/config';
import dayjs, { Dayjs } from 'dayjs';
import { useTimezone } from 'providers/Timezone';
import { Dispatch, SetStateAction, useMemo } from 'react';
@@ -24,10 +19,6 @@ interface RangePickerModalProps {
lexicalContext?: LexicalContext | undefined,
) => void;
selectedTime: string;
onTimeChange?: (
interval: Time | CustomTimeType,
dateTimeRange?: [number, number],
) => void;
}
function RangePickerModal(props: RangePickerModalProps): JSX.Element {
@@ -36,7 +27,6 @@ function RangePickerModal(props: RangePickerModalProps): JSX.Element {
setIsOpen,
onCustomDateHandler,
selectedTime,
onTimeChange,
} = props;
const { RangePicker } = DatePicker;
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
@@ -84,22 +74,13 @@ function RangePickerModal(props: RangePickerModalProps): JSX.Element {
date.tz(timezone.value).format(DATE_TIME_FORMATS.ISO_DATETIME)
}
onOk={onModalOkHandler}
{...(selectedTime === 'custom' &&
!onTimeChange && {
value: rangeValue,
})}
// use default value if onTimeChange is provided
{...(selectedTime === 'custom' &&
onTimeChange && {
defaultValue: rangeValue,
})}
// eslint-disable-next-line react/jsx-props-no-spreading
{...(selectedTime === 'custom' && {
value: rangeValue,
})}
/>
</div>
);
}
RangePickerModal.defaultProps = {
onTimeChange: undefined,
};
export default RangePickerModal;

View File

@@ -26,7 +26,6 @@ import { useQuery } from 'react-query';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource } from 'types/common/queryBuilder';
import { VIEWS } from '../constants';
import { getHostTracesQueryPayload, selectedColumns } from './constants';
import { getListColumns } from './utils';
@@ -40,10 +39,7 @@ interface Props {
interval: Time | CustomTimeType,
dateTimeRange?: [number, number],
) => void;
handleChangeTracesFilters: (
value: IBuilderQuery['filters'],
view: VIEWS,
) => void;
handleChangeTracesFilters: (value: IBuilderQuery['filters']) => void;
tracesFilters: IBuilderQuery['filters'];
selectedInterval: Time;
}
@@ -74,16 +70,14 @@ function HostMetricTraces({
...currentQuery.builder.queryData[0].aggregateAttribute,
},
filters: {
items: tracesFilters.items.filter(
(item) => item.key?.key !== 'host.name',
),
items: [],
op: 'AND',
},
},
],
},
}),
[currentQuery, tracesFilters.items],
[currentQuery],
);
const query = updatedCurrentQuery?.builder?.queryData[0] || null;
@@ -159,16 +153,14 @@ function HostMetricTraces({
{query && (
<QueryBuilderSearch
query={query}
onChange={(value): void =>
handleChangeTracesFilters(value, VIEWS.TRACES)
}
onChange={handleChangeTracesFilters}
disableNavigationShortcuts
/>
)}
</div>
<div className="datetime-section">
<DateTimeSelectionV2
showAutoRefresh
showAutoRefresh={false}
showRefreshText={false}
hideShareModal
isModalTimeSelection={isModalTimeSelection}

View File

@@ -19,8 +19,6 @@ import {
initialQueryState,
} from 'constants/queryBuilder';
import ROUTES from 'constants/routes';
import { getFiltersFromParams } from 'container/InfraMonitoringK8s/commonUtils';
import { INFRA_MONITORING_K8S_PARAMS_KEYS } from 'container/InfraMonitoringK8s/constants';
import {
CustomTimeType,
Time,
@@ -95,18 +93,8 @@ function HostMetricsDetails({
);
const isDarkMode = useIsDarkMode();
const initialFilters = useMemo(() => {
const urlView = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW);
const queryKey =
urlView === VIEW_TYPES.LOGS
? INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS
: INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS;
const filters = getFiltersFromParams(searchParams, queryKey);
if (filters) {
return filters;
}
return {
const initialFilters = useMemo(
() => ({
op: 'AND',
items: [
{
@@ -123,8 +111,9 @@ function HostMetricsDetails({
value: host?.hostName || '',
},
],
};
}, [host?.hostName, searchParams]);
}),
[host?.hostName],
);
const [logFilters, setLogFilters] = useState<IBuilderQuery['filters']>(
initialFilters,
@@ -165,13 +154,7 @@ function HostMetricsDetails({
const handleTabChange = (e: RadioChangeEvent): void => {
setSelectedView(e.target.value);
if (host?.hostName) {
setSelectedView(e.target.value);
setSearchParams({
...Object.fromEntries(searchParams.entries()),
[INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: e.target.value,
[INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS]: JSON.stringify(null),
[INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS]: JSON.stringify(null),
});
setSearchParams({ hostName: host?.hostName, view: e.target.value });
}
logEvent(InfraMonitoringEvents.TabChanged, {
entity: InfraMonitoringEvents.HostEntity,
@@ -208,7 +191,7 @@ function HostMetricsDetails({
);
const handleChangeLogFilters = useCallback(
(value: IBuilderQuery['filters'], view: VIEWS) => {
(value: IBuilderQuery['filters']) => {
setLogFilters((prevFilters) => {
const hostNameFilter = prevFilters.items.find(
(item) => item.key?.key === 'host.name',
@@ -226,7 +209,7 @@ function HostMetricsDetails({
});
}
const updatedFilters = {
return {
op: 'AND',
items: [
hostNameFilter,
@@ -234,15 +217,6 @@ function HostMetricsDetails({
...(paginationFilter ? [paginationFilter] : []),
].filter((item): item is TagFilterItem => item !== undefined),
};
setSearchParams({
...Object.fromEntries(searchParams.entries()),
[INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS]: JSON.stringify(
updatedFilters,
),
[INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view,
});
return updatedFilters;
});
},
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -250,7 +224,7 @@ function HostMetricsDetails({
);
const handleChangeTracesFilters = useCallback(
(value: IBuilderQuery['filters'], view: VIEWS) => {
(value: IBuilderQuery['filters']) => {
setTracesFilters((prevFilters) => {
const hostNameFilter = prevFilters.items.find(
(item) => item.key?.key === 'host.name',
@@ -264,23 +238,13 @@ function HostMetricsDetails({
});
}
const updatedFilters = {
return {
op: 'AND',
items: [
hostNameFilter,
...value.items.filter((item) => item.key?.key !== 'host.name'),
].filter((item): item is TagFilterItem => item !== undefined),
};
setSearchParams({
...Object.fromEntries(searchParams.entries()),
[INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS]: JSON.stringify(
updatedFilters,
),
[INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view,
});
return updatedFilters;
});
},
// eslint-disable-next-line react-hooks/exhaustive-deps

View File

@@ -11,7 +11,6 @@ import { useMemo } from 'react';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource } from 'types/common/queryBuilder';
import { VIEWS } from '../constants';
import HostMetricsLogs from './HostMetricsLogs';
interface Props {
@@ -24,7 +23,7 @@ interface Props {
interval: Time | CustomTimeType,
dateTimeRange?: [number, number],
) => void;
handleChangeLogFilters: (value: IBuilderQuery['filters'], view: VIEWS) => void;
handleChangeLogFilters: (value: IBuilderQuery['filters']) => void;
logFilters: IBuilderQuery['filters'];
selectedInterval: Time;
}
@@ -52,14 +51,14 @@ function HostMetricLogsDetailedView({
...currentQuery.builder.queryData[0].aggregateAttribute,
},
filters: {
items: logFilters.items.filter((item) => item.key?.key !== 'host.name'),
items: [],
op: 'AND',
},
},
],
},
}),
[currentQuery, logFilters.items],
[currentQuery],
);
const query = updatedCurrentQuery?.builder?.queryData[0] || null;
@@ -71,14 +70,14 @@ function HostMetricLogsDetailedView({
{query && (
<QueryBuilderSearch
query={query}
onChange={(value): void => handleChangeLogFilters(value, VIEWS.LOGS)}
onChange={handleChangeLogFilters}
disableNavigationShortcuts
/>
)}
</div>
<div className="datetime-section">
<DateTimeSelectionV2
showAutoRefresh
showAutoRefresh={false}
showRefreshText={false}
hideShareModal
isModalTimeSelection={isModalTimeSelection}

View File

@@ -18,14 +18,11 @@ import { useResizeObserver } from 'hooks/useDimensions';
import { GetMetricQueryRange } from 'lib/dashboard/getQueryResults';
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useMemo, useRef } from 'react';
import { useQueries, UseQueryResult } from 'react-query';
import { SuccessResponse } from 'types/api';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
interface MetricsTabProps {
timeRange: {
startTime: number;
@@ -48,20 +45,9 @@ function Metrics({
handleTimeChange,
isModalTimeSelection,
}: MetricsTabProps): JSX.Element {
const { featureFlags } = useAppContext();
const dotMetricsEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
?.active || false;
const queryPayloads = useMemo(
() =>
getHostQueryPayload(
hostName,
timeRange.startTime,
timeRange.endTime,
dotMetricsEnabled,
),
[hostName, timeRange.startTime, timeRange.endTime, dotMetricsEnabled],
() => getHostQueryPayload(hostName, timeRange.startTime, timeRange.endTime),
[hostName, timeRange.startTime, timeRange.endTime],
);
const queries = useQueries(
@@ -82,45 +68,6 @@ function Metrics({
[queries],
);
const [graphTimeIntervals, setGraphTimeIntervals] = useState<
{
start: number;
end: number;
}[]
>(
new Array(queries.length).fill({
start: timeRange.startTime,
end: timeRange.endTime,
}),
);
useEffect(() => {
setGraphTimeIntervals(
new Array(queries.length).fill({
start: timeRange.startTime,
end: timeRange.endTime,
}),
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [timeRange]);
const onDragSelect = useCallback(
(start: number, end: number, graphIndex: number) => {
const startTimestamp = Math.trunc(start);
const endTimestamp = Math.trunc(end);
setGraphTimeIntervals((prev) => {
const newIntervals = [...prev];
newIntervals[graphIndex] = {
start: Math.floor(startTimestamp / 1000),
end: Math.floor(endTimestamp / 1000),
};
return newIntervals;
});
},
[],
);
const options = useMemo(
() =>
queries.map(({ data }, idx) =>
@@ -131,12 +78,12 @@ function Metrics({
yAxisUnit: hostWidgetInfo[idx].yAxisUnit,
softMax: null,
softMin: null,
minTimeScale: graphTimeIntervals[idx].start,
maxTimeScale: graphTimeIntervals[idx].end,
onDragSelect: (start, end) => onDragSelect(start, end, idx),
minTimeScale: timeRange.startTime,
maxTimeScale: timeRange.endTime,
enableZoom: true,
}),
),
[queries, isDarkMode, dimensions, graphTimeIntervals, onDragSelect],
[queries, isDarkMode, dimensions, timeRange.startTime, timeRange.endTime],
);
const renderCardContent = (

View File

@@ -24,7 +24,7 @@ export interface LaunchChatSupportProps {
buttonText?: string;
className?: string;
onHoverText?: string;
chatMessageDisabled?: boolean;
intercomMessageDisabled?: boolean;
}
// eslint-disable-next-line sonarjs/cognitive-complexity
@@ -35,7 +35,7 @@ function LaunchChatSupport({
buttonText = '',
className = '',
onHoverText = '',
chatMessageDisabled = false,
intercomMessageDisabled = false,
}: LaunchChatSupportProps): JSX.Element | null {
const { isCloudUser: isCloudUserVal } = useGetTenantLicense();
const { notifications } = useNotifications();
@@ -111,10 +111,8 @@ function LaunchChatSupport({
setIsAddCreditCardModalOpen(true);
} else {
logEvent(eventName, attributes);
if (window.pylon && !chatMessageDisabled) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.Pylon('showNewMessage', defaultTo(message, ''));
if (window.Intercom && !intercomMessageDisabled) {
window.Intercom('showNewMessage', defaultTo(message, ''));
}
}
};
@@ -222,7 +220,7 @@ LaunchChatSupport.defaultProps = {
buttonText: '',
className: '',
onHoverText: '',
chatMessageDisabled: false,
intercomMessageDisabled: false,
};
export default LaunchChatSupport;

View File

@@ -16,7 +16,6 @@ import JSONView from 'container/LogDetailedView/JsonView';
import Overview from 'container/LogDetailedView/Overview';
import {
aggregateAttributesResourcesToString,
escapeHtml,
removeEscapeCharacters,
unescapeString,
} from 'container/LogDetailedView/utils';
@@ -119,7 +118,7 @@ function LogDetail({
const htmlBody = useMemo(
() => ({
__html: convert.toHtml(
dompurify.sanitize(unescapeString(escapeHtml(log?.body || '')), {
dompurify.sanitize(unescapeString(log?.body || ''), {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}),
),

View File

@@ -7,7 +7,7 @@ import cx from 'classnames';
import LogDetail from 'components/LogDetail';
import { VIEW_TYPES } from 'components/LogDetail/constants';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { escapeHtml, unescapeString } from 'container/LogDetailedView/utils';
import { unescapeString } from 'container/LogDetailedView/utils';
import { FontSize } from 'container/OptionsMenu/types';
import dompurify from 'dompurify';
import { useActiveLog } from 'hooks/logs/useActiveLog';
@@ -58,7 +58,7 @@ function LogGeneralField({
const html = useMemo(
() => ({
__html: convert.toHtml(
dompurify.sanitize(unescapeString(escapeHtml(fieldValue)), {
dompurify.sanitize(unescapeString(fieldValue), {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}),
),

View File

@@ -5,7 +5,7 @@ import { DrawerProps } from 'antd';
import LogDetail from 'components/LogDetail';
import { VIEW_TYPES, VIEWS } from 'components/LogDetail/constants';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { escapeHtml, unescapeString } from 'container/LogDetailedView/utils';
import { unescapeString } from 'container/LogDetailedView/utils';
import LogsExplorerContext from 'container/LogsExplorerContext';
import dompurify from 'dompurify';
import { useActiveLog } from 'hooks/logs/useActiveLog';
@@ -177,7 +177,7 @@ function RawLogView({
const html = useMemo(
() => ({
__html: convert.toHtml(
dompurify.sanitize(unescapeString(escapeHtml(text)), {
dompurify.sanitize(unescapeString(text), {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}),
),

View File

@@ -30,7 +30,6 @@
.right-action {
display: flex;
align-items: center;
min-width: 48px;
.clear-all {
font-size: 12px;
@@ -53,14 +52,10 @@
.checkbox-value-section {
display: flex;
align-items: center;
gap: 4px;
justify-content: space-between;
width: calc(100% - 24px);
cursor: pointer;
.value-string {
width: 100%;
}
&.filter-disabled {
cursor: not-allowed;
@@ -79,6 +74,9 @@
}
}
.value-string {
}
.only-btn {
display: none;
}
@@ -179,17 +177,3 @@
}
}
}
.label-false {
width: 2px;
height: 11px;
border-radius: 2px;
background: var(--bg-cherry-500);
}
.label-true {
width: 2px;
height: 11px;
border-radius: 2px;
background: var(--bg-forest-500);
}

View File

@@ -504,7 +504,6 @@ export default function CheckboxFilter(props: ICheckboxProps): JSX.Element {
onChange(value, currentFilterState[value], true);
}}
>
<div className={`${filter.title} label-${value}`} />
{filter.customRendererForValue ? (
filter.customRendererForValue(value)
) : (
@@ -512,7 +511,7 @@ export default function CheckboxFilter(props: ICheckboxProps): JSX.Element {
className="value-string"
ellipsis={{ tooltip: { placement: 'right' } }}
>
{String(value)}
{value}
</Typography.Text>
)}
<Button type="text" className="only-btn">

View File

@@ -1,174 +0,0 @@
.collapseContainer {
background-color: var(--bg-ink-500);
border-bottom: 1px solid var(--bg-slate-400);
.ant-collapse-header {
padding: 12px !important;
align-items: center !important;
}
.ant-collapse-expand-icon {
padding-right: 9px !important;
}
.ant-collapse-header-text {
color: var(--bg-vanilla-400);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 18px;
letter-spacing: -0.07px;
text-transform: capitalize;
}
.duration-inputs {
display: grid;
gap: 12px;
.min-max-input {
.ant-input-group-addon {
color: var(--bg-vanilla-400);
font-family: 'Space Mono', monospace;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 16px;
letter-spacing: 0.48px;
padding: 0 6px;
}
.ant-input {
padding: 4px 6px;
color: var(--bg-vanilla-400);
font-family: 'Space Mono', monospace;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 16px;
letter-spacing: 0.48px;
}
}
}
}
.divider {
background-color: var(--bg-slate-400);
margin: 0;
border-color: var(--bg-slate-400);
}
.filter-header {
padding: 16px 8px 16px 12px;
.filter-title {
display: flex;
gap: 6px;
.ant-typography {
color: var(--bg-vanilla-400);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 18px;
letter-spacing: -0.07px;
}
}
.sync-icon {
background-color: var(--bg-ink-500);
border: 0;
box-shadow: none;
padding: 8px;
}
.arrow-icon {
background-color: var(--bg-ink-500);
border: 0;
box-shadow: none;
padding-top: 8px;
.anticon-vertical-align-top {
svg {
width: 16px;
height: 16px;
}
}
}
}
.section-body-header {
display: flex;
> button {
position: absolute;
right: 4px;
padding-top: 13px;
}
.ant-collapse {
width: 100%;
}
}
.section-card {
background-color: var(--bg-ink-500);
.ant-card-body {
padding: 0;
display: flex;
flex-direction: column;
max-height: 500px;
overflow-x: hidden;
overflow-y: auto;
}
width: 240px;
}
.lightMode {
.collapseContainer {
background-color: var(--bg-vanilla-100);
border-bottom: 1px solid var(--bg-vanilla-300);
.ant-collapse-header-text {
color: var(--bg-slate-100);
}
.duration-inputs {
.min-max-input {
.ant-input-group-addon {
color: var(--bg-slate-100);
}
.ant-input {
color: var(--bg-slate-100);
}
}
}
}
.divider {
background-color: var(--bg-vanilla-100);
border-color: var(--bg-vanilla-200);
}
.filter-header {
.filter-title {
.ant-typography {
color: var(--bg-slate-100);
}
}
.arrow-icon {
background-color: var(--bg-vanilla-100);
}
.sync-icon {
background-color: var(--bg-vanilla-100);
}
}
.section-card {
background-color: var(--bg-vanilla-100);
box-shadow: none;
}
}

View File

@@ -1,281 +0,0 @@
/* eslint-disable react-hooks/exhaustive-deps */
import './Duration.styles.scss';
import { Button, Collapse } from 'antd';
import { IQuickFiltersConfig } from 'components/QuickFilters/types';
import { getMs } from 'container/Trace/Filters/Panel/PanelBody/Duration/util';
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { cloneDeep, isArray, isEqual, isFunction } from 'lodash-es';
import { DurationSection } from 'pages/TracesExplorer/Filter/DurationSection';
import {
AllTraceFilterKeys,
AllTraceFilterKeyValue,
HandleRunProps,
unionTagFilterItems,
} from 'pages/TracesExplorer/Filter/filterUtils';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { Query, TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
import { v4 as uuid } from 'uuid';
export type FilterType = Record<
AllTraceFilterKeys,
{ values: string[] | string; keys: BaseAutocompleteData }
>;
function Duration({
filter,
onFilterChange,
}: {
filter: IQuickFiltersConfig;
onFilterChange?: (query: Query) => void;
}): JSX.Element {
const [selectedFilters, setSelectedFilters] = useState<
Record<
AllTraceFilterKeys,
{ values: string[] | string; keys: BaseAutocompleteData }
>
>();
const [activeKeys, setActiveKeys] = useState<string[]>([
filter.defaultOpen ? 'durationNano' : '',
]);
const { currentQuery, redirectWithQueryBuilderData } = useQueryBuilder();
const compositeQuery = useGetCompositeQueryParam();
// eslint-disable-next-line sonarjs/cognitive-complexity
const syncSelectedFilters = useMemo((): FilterType => {
const filters = compositeQuery?.builder.queryData?.[0].filters;
if (!filters) {
return {} as FilterType;
}
return (filters.items || [])
.filter((item) =>
Object.keys(AllTraceFilterKeyValue).includes(item.key?.key as string),
)
.filter(
(item) =>
(item.op === 'in' && item.key?.key !== 'durationNano') ||
(item.key?.key === 'durationNano' && ['>=', '<='].includes(item.op)),
)
.reduce((acc, item) => {
const keys = item.key as BaseAutocompleteData;
const attributeName = item.key?.key || '';
const values = item.value as string[];
if ((attributeName as AllTraceFilterKeys) === 'durationNano') {
if (item.op === '>=') {
acc.durationNanoMin = {
values: getMs(String(values)),
keys,
};
} else {
acc.durationNanoMax = {
values: getMs(String(values)),
keys,
};
}
return acc;
}
if (attributeName) {
if (acc[attributeName as AllTraceFilterKeys]) {
const existingValue = acc[attributeName as AllTraceFilterKeys];
acc[attributeName as AllTraceFilterKeys] = {
values: [...existingValue.values, ...values],
keys,
};
} else {
acc[attributeName as AllTraceFilterKeys] = { values, keys };
}
}
return acc;
}, {} as FilterType);
}, [compositeQuery]);
useEffect(() => {
if (!isEqual(syncSelectedFilters, selectedFilters)) {
setSelectedFilters(syncSelectedFilters);
}
}, [syncSelectedFilters]);
// eslint-disable-next-line sonarjs/cognitive-complexity
const preparePostData = (): TagFilterItem[] => {
if (!selectedFilters) {
return [];
}
const items = Object.keys(selectedFilters)?.flatMap((attribute) => {
const { keys, values } = selectedFilters[attribute as AllTraceFilterKeys];
if (
['durationNanoMax', 'durationNanoMin', 'durationNano'].includes(
attribute as AllTraceFilterKeys,
)
) {
if (!values || !values.length) {
return [];
}
let minValue = '';
let maxValue = '';
const durationItems: TagFilterItem[] = [];
if (isArray(values)) {
minValue = values?.[0];
maxValue = values?.[1];
const minItems: TagFilterItem = {
id: uuid().slice(0, 8),
op: '>=',
key: keys,
value: Number(minValue) * 1000000,
};
const maxItems: TagFilterItem = {
id: uuid().slice(0, 8),
op: '<=',
key: keys,
value: Number(maxValue) * 1000000,
};
return maxValue ? [minItems, maxItems] : [minItems];
}
if (attribute === 'durationNanoMin') {
durationItems.push({
id: uuid().slice(0, 8),
op: '>=',
key: keys,
value: Number(values) * 1000000,
});
} else {
durationItems.push({
id: uuid().slice(0, 8),
op: '<=',
key: keys,
value: Number(values) * 1000000,
});
}
return durationItems;
}
return {
id: uuid().slice(0, 8),
key: keys,
op: 'in',
value: values,
};
});
return items as TagFilterItem[];
};
const removeFilterItemIds = (query: Query): Query => {
const clonedQuery = cloneDeep(query);
clonedQuery.builder.queryData = clonedQuery.builder.queryData.map((data) => ({
...data,
filters: {
...data.filters,
items: data.filters?.items?.map((item) => ({
...item,
id: '',
})),
},
}));
return clonedQuery;
};
const handleRun = useCallback(
(props?: HandleRunProps): void => {
const preparedQuery: Query = {
...currentQuery,
builder: {
...currentQuery.builder,
queryData: currentQuery.builder.queryData.map((item) => ({
...item,
filters: {
...item.filters,
items: props?.resetAll
? []
: (unionTagFilterItems(item.filters?.items, preparePostData())
.map((item) =>
item.key?.key === props?.clearByType ? undefined : item,
)
.filter((i) => i) as TagFilterItem[]),
},
})),
},
};
const currentQueryWithoutIds = removeFilterItemIds(currentQuery);
const preparedQueryWithoutIds = removeFilterItemIds(preparedQuery);
if (
isEqual(currentQueryWithoutIds, preparedQueryWithoutIds) &&
!props?.resetAll
) {
return;
}
if (onFilterChange && isFunction(onFilterChange)) {
onFilterChange(preparedQuery);
} else {
redirectWithQueryBuilderData(preparedQuery);
}
},
[currentQuery, redirectWithQueryBuilderData, selectedFilters],
);
useEffect(() => {
handleRun();
}, [selectedFilters]);
const onClearHandler = (e: React.MouseEvent): void => {
e.stopPropagation();
e.preventDefault();
if (selectedFilters?.durationNanoMin || selectedFilters?.durationNanoMax) {
handleRun({ clearByType: 'durationNano' });
}
};
return (
<div className="section-body-header" data-testid="collapse-duration">
<Collapse
bordered={false}
className="collapseContainer"
activeKey={activeKeys}
onChange={(keys): void => setActiveKeys(keys as string[])}
items={[
{
key: 'durationNano',
children: (
<DurationSection
setSelectedFilters={setSelectedFilters}
selectedFilters={selectedFilters}
/>
),
label: 'Duration',
},
]}
/>
{activeKeys.includes('durationNano') && (
<Button
type="link"
onClick={onClearHandler}
data-testid="collapse-duration-clearBtn"
>
Clear All
</Button>
)}
</div>
);
}
Duration.defaultProps = {
onFilterChange: (): void => {},
};
export default Duration;

View File

@@ -5,24 +5,19 @@ import {
SyncOutlined,
VerticalAlignTopOutlined,
} from '@ant-design/icons';
import { Skeleton, Switch, Tooltip, Typography } from 'antd';
import { Skeleton, Tooltip, Typography } from 'antd';
import getLocalStorageKey from 'api/browser/localstorage/get';
import setLocalStorageKey from 'api/browser/localstorage/set';
import logEvent from 'api/common/logEvent';
import classNames from 'classnames';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import { LOCALSTORAGE } from 'constants/localStorage';
import { useApiMonitoringParams } from 'container/ApiMonitoring/queryParams';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { cloneDeep, isFunction, isNull } from 'lodash-es';
import { Settings2 as SettingsIcon } from 'lucide-react';
import { useAppContext } from 'providers/App/App';
import { useMemo, useState } from 'react';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { USER_ROLES } from 'types/roles';
import Checkbox from './FilterRenderers/Checkbox/Checkbox';
import Duration from './FilterRenderers/Duration/Duration';
import Slider from './FilterRenderers/Slider/Slider';
import useFilterConfig from './hooks/useFilterConfig';
import AnnouncementTooltip from './QuickFiltersSettings/AnnouncementTooltip';
@@ -37,14 +32,8 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
source,
onFilterChange,
signal,
showFilterCollapse = true,
showQueryName = true,
} = props;
const { user } = useAppContext();
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
const isAdmin = user.role === USER_ROLES.ADMIN;
const [params, setParams] = useApiMonitoringParams();
const showIP = params.showIP ?? true;
const {
filterConfig,
@@ -106,33 +95,36 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
};
const lastQueryName =
showQueryName &&
currentQuery.builder.queryData?.[lastUsedQuery || 0]?.queryName;
return (
<div className="quick-filters-container">
<div className="quick-filters">
{source !== QuickFiltersSource.INFRA_MONITORING && (
<section className="header">
<section className="left-actions">
<FilterOutlined />
<Typography.Text className="text">
{lastQueryName ? 'Filters for' : 'Filters'}
</Typography.Text>
{lastQueryName && (
<Tooltip title={`Filter currently in sync with query ${lastQueryName}`}>
<Typography.Text className="sync-tag">{lastQueryName}</Typography.Text>
</Tooltip>
)}
</section>
{source !== QuickFiltersSource.INFRA_MONITORING &&
source !== QuickFiltersSource.API_MONITORING && (
<section className="header">
<section className="left-actions">
<FilterOutlined />
<Typography.Text className="text">
{lastQueryName ? 'Filters for' : 'Filters'}
</Typography.Text>
{lastQueryName && (
<Tooltip
title={`Filter currently in sync with query ${lastQueryName}`}
>
<Typography.Text className="sync-tag">
{lastQueryName}
</Typography.Text>
</Tooltip>
)}
</section>
<section className="right-actions">
<Tooltip title="Reset All">
<div className="right-action-icon-container">
<SyncOutlined className="sync-icon" onClick={handleReset} />
</div>
</Tooltip>
{showFilterCollapse && (
<section className="right-actions">
<Tooltip title="Reset All">
<div className="right-action-icon-container">
<SyncOutlined className="sync-icon" onClick={handleReset} />
</div>
</Tooltip>
<Tooltip title="Collapse Filters">
<div className="right-action-icon-container">
<VerticalAlignTopOutlined
@@ -141,39 +133,38 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
/>
</div>
</Tooltip>
)}
{isDynamicFilters && isAdmin && (
<Tooltip title="Settings">
<div
className={classNames('right-action-icon-container', {
active: isSettingsOpen,
})}
>
<SettingsIcon
className="settings-icon"
data-testid="settings-icon"
width={14}
height={14}
onClick={(): void => setIsSettingsOpen(true)}
/>
<AnnouncementTooltip
show={showAnnouncementTooltip}
position={{ top: -5, left: 15 }}
title="Edit your quick filters"
message="You can now customize and re-arrange your quick filters panel. Select the quick filters youd need and hide away the rest for faster exploration."
onClose={(): void => {
setLocalStorageKey(
LOCALSTORAGE.QUICK_FILTERS_SETTINGS_ANNOUNCEMENT,
'false',
);
}}
/>
</div>
</Tooltip>
)}
{isDynamicFilters && (
<Tooltip title="Settings">
<div
className={classNames('right-action-icon-container', {
active: isSettingsOpen,
})}
>
<SettingsIcon
className="settings-icon"
data-testid="settings-icon"
width={14}
height={14}
onClick={(): void => setIsSettingsOpen(true)}
/>
<AnnouncementTooltip
show={showAnnouncementTooltip}
position={{ top: -5, left: 15 }}
title="Edit your quick filters"
message="You can now customize and re-arrange your quick filters panel. Select the quick filters youd need and hide away the rest for faster exploration."
onClose={(): void => {
setLocalStorageKey(
LOCALSTORAGE.QUICK_FILTERS_SETTINGS_ANNOUNCEMENT,
'false',
);
}}
/>
</div>
</Tooltip>
)}
</section>
</section>
</section>
)}
)}
{isCustomFiltersLoading ? (
<div className="quick-filters-skeleton">
@@ -188,51 +179,31 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
</div>
) : (
<OverlayScrollbar>
<>
{source === QuickFiltersSource.API_MONITORING && (
<div className="api-quick-filters-header">
<Typography.Text>Show IP addresses</Typography.Text>
<Switch
size="small"
style={{ marginLeft: 'auto' }}
checked={showIP ?? true}
onClick={(): void => {
logEvent('API Monitoring: Show IP addresses clicked', {
showIP: !(showIP ?? true),
});
setParams({ showIP });
}}
/>
</div>
)}
<section className="filters">
{filterConfig.map((filter) => {
switch (filter.type) {
case FiltersType.CHECKBOX:
return (
<Checkbox
source={source}
filter={filter}
onFilterChange={onFilterChange}
/>
);
case FiltersType.DURATION:
return <Duration filter={filter} onFilterChange={onFilterChange} />;
case FiltersType.SLIDER:
return <Slider filter={filter} />;
// eslint-disable-next-line sonarjs/no-duplicated-branches
default:
return (
<Checkbox
source={source}
filter={filter}
onFilterChange={onFilterChange}
/>
);
}
})}
</section>
</>
<section className="filters">
{filterConfig.map((filter) => {
switch (filter.type) {
case FiltersType.CHECKBOX:
return (
<Checkbox
source={source}
filter={filter}
onFilterChange={onFilterChange}
/>
);
case FiltersType.SLIDER:
return <Slider filter={filter} />;
// eslint-disable-next-line sonarjs/no-duplicated-branches
default:
return (
<Checkbox
source={source}
filter={filter}
onFilterChange={onFilterChange}
/>
);
}
})}
</section>
</OverlayScrollbar>
)}
</div>
@@ -264,6 +235,4 @@ QuickFilters.defaultProps = {
onFilterChange: null,
signal: '',
config: [],
showFilterCollapse: true,
showQueryName: true,
};

View File

@@ -3,12 +3,10 @@ import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import { SIGNAL_DATA_SOURCE_MAP } from 'components/QuickFilters/QuickFiltersSettings/constants';
import { SignalType } from 'components/QuickFilters/types';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys';
import { useGetAttributeSuggestions } from 'hooks/queryBuilder/useGetAttributeSuggestions';
import { useMemo } from 'react';
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { Filter as FilterType } from 'types/api/quickFilters/getCustomFilters';
import { DataSource } from 'types/common/queryBuilder';
function OtherFiltersSkeleton(): JSX.Element {
return (
@@ -36,11 +34,6 @@ function OtherFilters({
addedFilters: FilterType[];
setAddedFilters: React.Dispatch<React.SetStateAction<FilterType[]>>;
}): JSX.Element {
const isLogDataSource = useMemo(
() => SIGNAL_DATA_SOURCE_MAP[signal as SignalType] === DataSource.LOGS,
[signal],
);
const {
data: suggestionsData,
isFetching: isFetchingSuggestions,
@@ -52,39 +45,18 @@ function OtherFilters({
},
{
queryKey: [REACT_QUERY_KEY.GET_OTHER_FILTERS, inputValue],
enabled: !!signal && isLogDataSource,
enabled: !!signal,
},
);
const {
data: aggregateKeysData,
isFetching: isFetchingAggregateKeys,
} = useGetAggregateKeys(
{
searchText: inputValue,
dataSource: SIGNAL_DATA_SOURCE_MAP[signal as SignalType],
aggregateOperator: 'noop',
aggregateAttribute: '',
tagType: '',
},
{
queryKey: [REACT_QUERY_KEY.GET_OTHER_FILTERS, inputValue],
enabled: !!signal && !isLogDataSource,
},
const otherFilters = useMemo(
() =>
suggestionsData?.payload?.attributes?.filter(
(attr) => !addedFilters.some((filter) => filter.key === attr.key),
),
[suggestionsData, addedFilters],
);
const otherFilters = useMemo(() => {
let filterAttributes;
if (isLogDataSource) {
filterAttributes = suggestionsData?.payload?.attributes || [];
} else {
filterAttributes = aggregateKeysData?.payload?.attributeKeys || [];
}
return filterAttributes?.filter(
(attr) => !addedFilters.some((filter) => filter.key === attr.key),
);
}, [suggestionsData, aggregateKeysData, addedFilters, isLogDataSource]);
const handleAddFilter = (filter: FilterType): void => {
setAddedFilters((prev) => [
...prev,
@@ -99,8 +71,7 @@ function OtherFilters({
};
const renderFilters = (): React.ReactNode => {
const isLoading = isFetchingSuggestions || isFetchingAggregateKeys;
if (isLoading) return <OtherFiltersSkeleton />;
if (isFetchingSuggestions) return <OtherFiltersSkeleton />;
if (!otherFilters?.length)
return <div className="no-values-found">No values found</div>;

View File

@@ -7,7 +7,6 @@
background: var(--bg-slate-500);
transition: width 0.05s ease-in-out;
overflow: hidden;
color: var(--bg-vanilla-100);
&.qf-logs-explorer {
height: calc(100vh - 45px);
@@ -17,14 +16,6 @@
height: 100vh;
}
&.qf-api-monitoring {
height: calc(100vh - 45px);
}
&.qf-traces-explorer {
height: calc(100vh - 45px);
}
&.hidden {
width: 0;
}
@@ -181,7 +172,6 @@
.lightMode {
.quick-filters-settings {
background: var(--bg-vanilla-100);
color: var(--bg-slate-500);
.search {
.ant-input {
background-color: var(--bg-vanilla-100);

View File

@@ -1,4 +1,3 @@
import logEvent from 'api/common/logEvent';
import updateCustomFiltersAPI from 'api/quickFilters/updateCustomFilters';
import axios, { AxiosError } from 'axios';
import { SignalType } from 'components/QuickFilters/types';
@@ -47,9 +46,6 @@ const useQuickFilterSettings = ({
onSuccess: () => {
setIsSettingsOpen(false);
setIsStale(true);
logEvent('Quick Filters Settings: changes saved', {
addedFilters,
});
notifications.success({
message: 'Quick filters updated successfully',
placement: 'bottomRight',

View File

@@ -33,7 +33,7 @@ const useFilterConfig = ({
const isDynamicFilters = useMemo(() => customFilters.length > 0, [
customFilters,
]);
const { isFetching: isCustomFiltersLoading } = useQuery<
const { isLoading: isCustomFiltersLoading } = useQuery<
SuccessResponse<PayloadProps> | ErrorResponse,
Error
>(
@@ -49,10 +49,10 @@ const useFilterConfig = ({
enabled: !!signal && isStale,
},
);
const filterConfig = useMemo(
() => getFilterConfig(signal, customFilters, config),
[config, customFilters, signal],
);
const filterConfig = useMemo(() => getFilterConfig(customFilters, config), [
config,
customFilters,
]);
return {
filterConfig,

View File

@@ -1,7 +1,6 @@
import '@testing-library/jest-dom';
import {
act,
cleanup,
fireEvent,
render,
@@ -9,7 +8,6 @@ import {
waitFor,
} from '@testing-library/react';
import { ENVIRONMENT } from 'constants/env';
import ROUTES from 'constants/routes';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import {
otherFiltersResponse,
@@ -19,7 +17,6 @@ import {
import { server } from 'mocks-server/server';
import { rest } from 'msw';
import MockQueryClientProvider from 'providers/test/MockQueryClientProvider';
import { USER_ROLES } from 'types/roles';
import QuickFilters from '../QuickFilters';
import { IQuickFiltersConfig, QuickFiltersSource, SignalType } from '../types';
@@ -29,21 +26,6 @@ jest.mock('hooks/queryBuilder/useQueryBuilder', () => ({
useQueryBuilder: jest.fn(),
}));
// eslint-disable-next-line sonarjs/no-duplicate-string
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: (): { pathname: string } => ({
pathname: `${process.env.FRONTEND_API_ENDPOINT}/${ROUTES.TRACES_EXPLORER}/`,
}),
}));
const userRole = USER_ROLES.ADMIN;
// mock useAppContext
jest.mock('providers/App/App', () => ({
useAppContext: jest.fn(() => ({ user: { role: userRole } })),
}));
const handleFilterVisibilityChange = jest.fn();
const redirectWithQueryBuilderData = jest.fn();
const putHandler = jest.fn();
@@ -181,9 +163,7 @@ describe('Quick Filters with custom filters', () => {
expect(screen.getByText('Filters for')).toBeInTheDocument();
expect(screen.getByText(QUERY_NAME)).toBeInTheDocument();
await screen.findByText(FILTER_SERVICE_NAME);
const allByText = await screen.findAllByText('otel-demo');
// since 2 filter collapse are open, there are 2 filter items visible
expect(allByText).toHaveLength(2);
await screen.findByText('otel-demo');
const icon = await screen.findByTestId(SETTINGS_ICON_TEST_ID);
fireEvent.click(icon);
@@ -305,59 +285,4 @@ describe('Quick Filters with custom filters', () => {
);
expect(requestBody.signal).toBe(SIGNAL);
});
// render duration filter
it('should render duration slider for duration_nono filter', async () => {
// Set up fake timers **before rendering**
jest.useFakeTimers();
const { getByTestId } = render(<TestQuickFilters signal={SIGNAL} />);
await screen.findByText(FILTER_SERVICE_NAME);
expect(screen.getByText('Duration')).toBeInTheDocument();
// click to open the duration filter
fireEvent.click(screen.getByText('Duration'));
const minDuration = getByTestId('min-input') as HTMLInputElement;
const maxDuration = getByTestId('max-input') as HTMLInputElement;
expect(minDuration).toHaveValue(null);
expect(minDuration).toHaveProperty('placeholder', '0');
expect(maxDuration).toHaveValue(null);
expect(maxDuration).toHaveProperty('placeholder', '100000000');
await act(async () => {
// set values
fireEvent.change(minDuration, { target: { value: '10000' } });
fireEvent.change(maxDuration, { target: { value: '20000' } });
jest.advanceTimersByTime(2000);
});
await waitFor(() => {
expect(redirectWithQueryBuilderData).toHaveBeenCalledWith(
expect.objectContaining({
builder: {
queryData: expect.arrayContaining([
expect.objectContaining({
filters: expect.objectContaining({
items: expect.arrayContaining([
expect.objectContaining({
key: expect.objectContaining({ key: 'durationNano' }),
op: '>=',
value: 10000000000,
}),
expect.objectContaining({
key: expect.objectContaining({ key: 'durationNano' }),
op: '<=',
value: 20000000000,
}),
]),
}),
}),
]),
},
}),
);
});
jest.useRealTimers(); // Clean up
});
});

View File

@@ -5,7 +5,6 @@ import { DataSource } from 'types/common/queryBuilder';
export enum FiltersType {
SLIDER = 'SLIDER',
CHECKBOX = 'CHECKBOX',
DURATION = 'DURATION', // ALIAS FOR DURATION_NANO
}
export enum MinMax {
@@ -43,8 +42,6 @@ export interface IQuickFiltersProps {
onFilterChange?: (query: Query) => void;
signal?: SignalType;
className?: string;
showFilterCollapse?: boolean;
showQueryName?: boolean;
}
export enum QuickFiltersSource {

View File

@@ -1,53 +1,30 @@
import { SIGNAL_DATA_SOURCE_MAP } from 'components/QuickFilters/QuickFiltersSettings/constants';
import { Filter as FilterType } from 'types/api/quickFilters/getCustomFilters';
import { FiltersType, IQuickFiltersConfig, SignalType } from './types';
import { FiltersType, IQuickFiltersConfig } from './types';
const FILTER_TITLE_MAP: Record<string, string> = {
duration_nano: 'Duration',
hasError: 'Has Error (Status)',
};
const FILTER_TYPE_MAP: Record<string, FiltersType> = {
duration_nano: FiltersType.DURATION,
};
const getFilterName = (str: string): string => {
if (FILTER_TITLE_MAP[str]) {
return FILTER_TITLE_MAP[str];
}
const getFilterName = (str: string): string =>
// replace . and _ with space
// capitalize the first letter of each word
return str
str
.replace(/\./g, ' ')
.replace(/_/g, ' ')
.split(' ')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
};
const getFilterType = (att: FilterType): FiltersType => {
if (FILTER_TYPE_MAP[att.key]) {
return FILTER_TYPE_MAP[att.key];
}
return FiltersType.CHECKBOX;
};
export const getFilterConfig = (
signal?: SignalType,
customFilters?: FilterType[],
config?: IQuickFiltersConfig[],
): IQuickFiltersConfig[] => {
if (!customFilters?.length || !signal) {
if (!customFilters?.length) {
return config || [];
}
return customFilters.map(
(att, index) =>
({
type: getFilterType(att),
type: FiltersType.CHECKBOX,
title: getFilterName(att.key),
dataSource: SIGNAL_DATA_SOURCE_MAP[signal],
attributeKey: {
id: att.key,
key: att.key,
@@ -56,7 +33,7 @@ export const getFilterConfig = (
isColumn: att.isColumn,
isJSON: att.isJSON,
},
defaultOpen: index < 2,
defaultOpen: index === 0,
} as IQuickFiltersConfig),
);
};

View File

@@ -10,5 +10,4 @@ export enum FeatureKeys {
ONBOARDING_V3 = 'ONBOARDING_V3',
THIRD_PARTY_API = 'THIRD_PARTY_API',
TRACE_FUNNELS = 'TRACE_FUNNELS',
DOT_METRICS_ENABLED = 'DOT_METRICS_ENABLED',
}

View File

@@ -21,10 +21,7 @@ import ROUTES from 'constants/routes';
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
import { useNotifications } from 'hooks/useNotifications';
import useResourceAttribute from 'hooks/useResourceAttribute';
import {
convertCompositeQueryToTraceSelectedTags,
getResourceDeploymentKeys,
} from 'hooks/useResourceAttribute/utils';
import { convertCompositeQueryToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
import { TimestampInput } from 'hooks/useTimezoneFormatter/useTimezoneFormatter';
import useUrlQuery from 'hooks/useUrlQuery';
import createQueryParams from 'lib/createQueryParams';
@@ -41,8 +38,6 @@ import { ErrorResponse, SuccessResponse } from 'types/api';
import { Exception, PayloadProps } from 'types/api/errors/getAll';
import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../constants/features';
import { useAppContext } from '../../providers/App/App';
import { FilterDropdownExtendsProps } from './types';
import {
extractFilterValues,
@@ -410,11 +405,6 @@ function AllErrors(): JSX.Element {
},
];
const { featureFlags } = useAppContext();
const dotMetricsEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
?.active || false;
const onChangeHandler: TableProps<Exception>['onChange'] = useCallback(
(
paginations: TablePaginationConfig,
@@ -448,7 +438,7 @@ function AllErrors(): JSX.Element {
useEffect(() => {
if (!isUndefined(errorCountResponse.data?.payload)) {
const selectedEnvironments = queries.find(
(val) => val.tagKey === getResourceDeploymentKeys(dotMetricsEnabled),
(val) => val.tagKey === 'resource_deployment_environment',
)?.tagValue;
logEvent('Exception: List page visited', {

View File

@@ -10,8 +10,6 @@ import { Provider, useSelector } from 'react-redux';
import { MemoryRouter } from 'react-router-dom';
import store from 'store';
import * as appContextHooks from '../../../providers/App/App';
import { LicenseEvent } from '../../../types/api/licensesV3/getActive';
import AllErrors from '../index';
import {
INIT_URL_WITH_COMMON_QUERY,
@@ -30,30 +28,6 @@ jest.mock('react-redux', () => ({
useSelector: jest.fn(),
}));
jest.spyOn(appContextHooks, 'useAppContext').mockReturnValue({
user: {
role: 'admin',
},
activeLicenseV3: {
event_queue: {
created_at: '0',
event: LicenseEvent.NO_EVENT,
scheduled_at: '0',
status: '',
updated_at: '0',
},
license: {
license_key: 'test-license-key',
license_type: 'trial',
org_id: 'test-org-id',
plan_id: 'test-plan-id',
plan_name: 'test-plan-name',
plan_type: 'trial',
plan_version: 'test-plan-version',
},
},
} as any);
function Exceptions({ initUrl }: { initUrl?: string[] }): JSX.Element {
return (
<MemoryRouter initialEntries={initUrl ?? ['/exceptions']}>

View File

@@ -1,16 +1,23 @@
import './Explorer.styles.scss';
import { FilterOutlined } from '@ant-design/icons';
import * as Sentry from '@sentry/react';
import { Switch, Typography } from 'antd';
import logEvent from 'api/common/logEvent';
import cx from 'classnames';
import QuickFilters from 'components/QuickFilters/QuickFilters';
import { QuickFiltersSource, SignalType } from 'components/QuickFilters/types';
import { QuickFiltersSource } from 'components/QuickFilters/types';
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
import { useEffect } from 'react';
import { useApiMonitoringParams } from '../queryParams';
import { ApiMonitoringQuickFiltersConfig } from '../utils';
import DomainList from './Domains/DomainList';
function Explorer(): JSX.Element {
const [params, setParams] = useApiMonitoringParams();
const showIP = params.showIP ?? true;
useEffect(() => {
logEvent('API Monitoring: Landing page visited', {});
}, []);
@@ -19,12 +26,29 @@ function Explorer(): JSX.Element {
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
<div className={cx('api-monitoring-page', 'filter-visible')}>
<section className="api-quick-filter-left-section">
<div className="api-quick-filters-header">
<FilterOutlined />
<Typography.Text>Filters</Typography.Text>
</div>
<div className="api-quick-filters-header">
<Typography.Text>Show IP addresses</Typography.Text>
<Switch
size="small"
style={{ marginLeft: 'auto' }}
checked={showIP ?? true}
onClick={(): void => {
logEvent('API Monitoring: Show IP addresses clicked', {
showIP: !(showIP ?? true),
});
setParams({ showIP });
}}
/>
</div>
<QuickFilters
className="qf-api-monitoring"
source={QuickFiltersSource.API_MONITORING}
signal={SignalType.API_MONITORING}
showFilterCollapse={false}
showQueryName={false}
config={ApiMonitoringQuickFiltersConfig}
handleFilterVisibilityChange={(): void => {}}
/>
</section>

View File

@@ -9,7 +9,7 @@ export default function GeneralSettingsCloud(): JSX.Element {
<Info size={16} />
<Typography.Text>
Please <a href="mailto:cloud-support@signoz.io"> email us </a> or connect
with us via chat support to change the retention period.
with us via intercom support to change the retention period.
</Typography.Text>
</Card>
);

View File

@@ -191,9 +191,8 @@ function GridCardGraph({
const isLogsQuery = useMemo(
() =>
requestData?.query?.builder?.queryData?.length > 0 &&
requestData?.query?.builder?.queryData?.every(
(query) => query?.dataSource === DataSource.LOGS,
requestData.query.builder.queryData.every(
(query) => query.dataSource === DataSource.LOGS,
),
[requestData.query],
);
@@ -204,7 +203,7 @@ function GridCardGraph({
variables: getDashboardVariables(variables),
selectedTime: widget.timePreferance || 'GLOBAL_TIME',
globalSelectedInterval:
widget?.panelTypes === PANEL_TYPES.LIST && isLogsQuery
widget.panelTypes === PANEL_TYPES.LIST && isLogsQuery
? 'custom'
: globalSelectedInterval,
start: customTimeRange?.startTime || start,

View File

@@ -10,16 +10,15 @@ import getAllUserPreferences from 'api/preferences/getAllUserPreference';
import updateUserPreferenceAPI from 'api/preferences/updateUserPreference';
import Header from 'components/Header/Header';
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
import { FeatureKeys } from 'constants/features';
import { LOCALSTORAGE } from 'constants/localStorage';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import ROUTES from 'constants/routes';
import { getHostListsQuery } from 'container/InfraMonitoringHosts/utils';
import { useGetDeploymentsData } from 'hooks/CustomDomain/useGetDeploymentsData';
import { useGetHostList } from 'hooks/infraMonitoring/useGetHostList';
import { useGetK8sPodsList } from 'hooks/infraMonitoring/useGetK8sPodsList';
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
import history from 'lib/history';
import cloneDeep from 'lodash-es/cloneDeep';
import { CompassIcon, DotIcon, HomeIcon, Plus, Wrench, X } from 'lucide-react';
@@ -29,6 +28,7 @@ import Card from 'periscope/components/Card/Card';
import { useAppContext } from 'providers/App/App';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useMutation, useQuery } from 'react-query';
import { LicensePlatform } from 'types/api/licensesV3/getActive';
import { DataSource } from 'types/common/queryBuilder';
import { UserPreference } from 'types/reducer/app';
import { USER_ROLES } from 'types/roles';
@@ -54,8 +54,6 @@ export default function Home(): JSX.Element {
const [updatingUserPreferences, setUpdatingUserPreferences] = useState(false);
const [loadingUserPreferences, setLoadingUserPreferences] = useState(true);
const { isCommunityUser, isCommunityEnterpriseUser } = useGetTenantLicense();
const [checklistItems, setChecklistItems] = useState<ChecklistItem[]>(
defaultChecklistItemsState,
);
@@ -162,20 +160,10 @@ export default function Home(): JSX.Element {
enabled: !!query,
});
const { featureFlags } = useAppContext();
const dotMetricsEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
?.active || false;
const { data: k8sPodsData } = useGetK8sPodsList(
query as K8sPodsListPayload,
{
queryKey: ['K8sPodsList', query],
enabled: !!query,
},
undefined,
dotMetricsEnabled,
);
const { data: k8sPodsData } = useGetK8sPodsList(query as K8sPodsListPayload, {
queryKey: ['K8sPodsList', query],
enabled: !!query,
});
const [isLogsIngestionActive, setIsLogsIngestionActive] = useState(false);
const [isTracesIngestionActive, setIsTracesIngestionActive] = useState(false);
@@ -312,7 +300,19 @@ export default function Home(): JSX.Element {
}
}, [hostData, k8sPodsData, handleUpdateChecklistDoneItem]);
const { isCloudUser, isEnterpriseSelfHostedUser } = useGetTenantLicense();
const { activeLicense, isFetchingActiveLicense } = useAppContext();
const [isEnabled, setIsEnabled] = useState(false);
useEffect(() => {
if (isFetchingActiveLicense) {
setIsEnabled(false);
return;
}
setIsEnabled(Boolean(activeLicense?.platform === LicensePlatform.CLOUD));
}, [activeLicense, isFetchingActiveLicense]);
const { data: deploymentsData } = useGetDeploymentsData(isEnabled);
useEffect(() => {
logEvent('Homepage: Visited', {});
@@ -323,27 +323,22 @@ export default function Home(): JSX.Element {
setIsBannerDismissed(true);
};
const showBanner = useMemo(
() => !isBannerDismissed && (isCommunityUser || isCommunityEnterpriseUser),
[isBannerDismissed, isCommunityUser, isCommunityEnterpriseUser],
);
return (
<div className="home-container">
<div className="sticky-header">
{showBanner && (
{!isBannerDismissed && (
<div className="home-container-banner">
<div className="home-container-banner-content">
Big News: SigNoz Community Edition now available with SSO (Google OAuth)
and API keys -
Big news: SigNoz Cloud Teams plan now starting at just $49/Month -
<a
href="https://signoz.io/blog/open-source-signoz-now-available-with-sso-and-api-keys/"
href="https://signoz.io/blog/cloud-teams-plan-now-at-49usd/"
target="_blank"
rel="noreferrer"
className="home-container-banner-link"
>
<i>read more</i>
</a>
🥳🎉
</div>
<div className="home-container-banner-close">
@@ -698,7 +693,7 @@ export default function Home(): JSX.Element {
)}
</div>
<div className="home-right-content">
{(isCloudUser || isEnterpriseSelfHostedUser) && (
{deploymentsData?.data?.data?.cluster?.region?.name === 'in' && (
<div className="home-notifications-container">
<div className="notification">
<Alert

View File

@@ -30,7 +30,6 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { Tags } from 'types/reducer/trace';
import { USER_ROLES } from 'types/roles';
import { FeatureKeys } from '../../../constants/features';
import { DOCS_LINKS } from '../constants';
import { columns, TIME_PICKER_OPTIONS } from './constants';
@@ -211,11 +210,6 @@ function ServiceMetrics({
const topLevelOperations = useMemo(() => Object.entries(data || {}), [data]);
const { featureFlags } = useAppContext();
const dotMetricsEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
?.active || false;
const queryRangeRequestData = useMemo(
() =>
getQueryRangeRequestData({
@@ -223,14 +217,12 @@ function ServiceMetrics({
minTime: timeRange.startTime * 1e6,
maxTime: timeRange.endTime * 1e6,
globalSelectedInterval,
dotMetricsEnabled,
}),
[
globalSelectedInterval,
timeRange.endTime,
timeRange.startTime,
topLevelOperations,
dotMetricsEnabled,
],
);

View File

@@ -25,11 +25,9 @@ import { AppState } from 'store/reducers';
import { IBuilderQuery, Query } from 'types/api/queryBuilder/queryBuilderData';
import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../constants/features';
import { useAppContext } from '../../providers/App/App';
import HostsListControls from './HostsListControls';
import HostsListTable from './HostsListTable';
import { getHostListsQuery, GetHostsQuickFiltersConfig } from './utils';
import { getHostListsQuery, HostsQuickFiltersConfig } from './utils';
// eslint-disable-next-line sonarjs/cognitive-complexity
function HostsList(): JSX.Element {
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
@@ -116,11 +114,6 @@ function HostsList(): JSX.Element {
entityVersion: '',
});
const { featureFlags } = useAppContext();
const dotMetricsEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
?.active || false;
const handleFiltersChange = useCallback(
(value: IBuilderQuery['filters']): void => {
const isNewFilterAdded = value.items.length !== filters.items.length;
@@ -189,7 +182,7 @@ function HostsList(): JSX.Element {
</div>
<QuickFilters
source={QuickFiltersSource.INFRA_MONITORING}
config={GetHostsQuickFiltersConfig(dotMetricsEnabled)}
config={HostsQuickFiltersConfig}
handleFilterVisibilityChange={handleFilterVisibilityChange}
onFilterChange={handleQuickFiltersChange}
/>

View File

@@ -58,7 +58,7 @@ function HostsListControls({
<div className="time-selector">
<DateTimeSelectionV2
showAutoRefresh
showAutoRefresh={false}
showRefreshText={false}
hideShareModal
/>

View File

@@ -201,7 +201,7 @@
padding: 16px;
margin: 0;
// this is to offset chat support icon till we improve the design
// this is to offset intercom icon till we improve the design
right: 20px;
.ant-pagination-item {

View File

@@ -198,48 +198,3 @@ export const HostsQuickFiltersConfig: IQuickFiltersConfig[] = [
defaultOpen: true,
},
];
export function GetHostsQuickFiltersConfig(
dotMetricsEnabled: boolean,
): IQuickFiltersConfig[] {
// These keys dont change with dotMetricsEnabled
const hostNameKey = dotMetricsEnabled ? 'host.name' : 'host_name';
const osTypeKey = dotMetricsEnabled ? 'os.type' : 'os_type';
// This metric stays the same regardless of notation
const metricName = dotMetricsEnabled
? 'system.cpu.load_average.15m'
: 'system_cpu_load_average_15m';
return [
{
type: FiltersType.CHECKBOX,
title: 'Host Name',
attributeKey: {
key: hostNameKey,
dataType: DataTypes.String,
type: 'resource',
isColumn: false,
isJSON: false,
},
aggregateOperator: 'noop',
aggregateAttribute: metricName,
dataSource: DataSource.METRICS,
defaultOpen: true,
},
{
type: FiltersType.CHECKBOX,
title: 'OS Type',
attributeKey: {
key: osTypeKey,
dataType: DataTypes.String,
type: 'resource',
isColumn: false,
isJSON: false,
},
aggregateOperator: 'noop',
aggregateAttribute: metricName,
dataSource: DataSource.METRICS,
defaultOpen: true,
},
];
}

View File

@@ -28,13 +28,11 @@ import { AppState } from 'store/reducers';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import { getOrderByFromParams } from '../commonUtils';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
K8sCategory,
K8sEntityToAggregateAttributeMapping,
} from '../constants';
import K8sHeader from '../K8sHeader';
import LoadingContainer from '../LoadingContainer';
@@ -61,25 +59,10 @@ function K8sClustersList({
(state) => state.globalTime,
);
const [searchParams, setSearchParams] = useSearchParams();
const [currentPage, setCurrentPage] = useState(1);
const [currentPage, setCurrentPage] = useState(() => {
const page = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.CURRENT_PAGE);
if (page) {
return parseInt(page, 10);
}
return 1;
});
const [filtersInitialised, setFiltersInitialised] = useState(false);
const [expandedRowKeys, setExpandedRowKeys] = useState<string[]>([]);
useEffect(() => {
setSearchParams({
...Object.fromEntries(searchParams.entries()),
[INFRA_MONITORING_K8S_PARAMS_KEYS.CURRENT_PAGE]: currentPage.toString(),
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentPage]);
const [searchParams, setSearchParams] = useSearchParams();
const [orderBy, setOrderBy] = useState<{
columnName: string;
@@ -132,16 +115,9 @@ function K8sClustersList({
// Reset pagination every time quick filters are changed
useEffect(() => {
if (quickFiltersLastUpdated !== -1) {
setCurrentPage(1);
}
setCurrentPage(1);
}, [quickFiltersLastUpdated]);
const { featureFlags } = useAppContext();
const dotMetricsEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
?.active || false;
const createFiltersForSelectedRowData = (
selectedRowData: K8sClustersRowData,
groupBy: IBuilderQuery['groupBy'],
@@ -201,8 +177,6 @@ function K8sClustersList({
queryKey: ['clusterList', fetchGroupedByRowDataQuery],
enabled: !!fetchGroupedByRowDataQuery && !!selectedRowData,
},
undefined,
dotMetricsEnabled,
);
const {
@@ -211,10 +185,8 @@ function K8sClustersList({
} = useGetAggregateKeys(
{
dataSource: currentQuery.builder.queryData[0].dataSource,
aggregateAttribute: GetK8sEntityToAggregateAttribute(
K8sCategory.CLUSTERS,
dotMetricsEnabled,
),
aggregateAttribute:
K8sEntityToAggregateAttributeMapping[K8sCategory.CLUSTERS],
aggregateOperator: 'noop',
searchText: '',
tagType: '',
@@ -260,8 +232,6 @@ function K8sClustersList({
queryKey: ['clusterList', query],
enabled: !!query,
},
undefined,
dotMetricsEnabled,
);
const clustersData = useMemo(() => data?.payload?.data?.records || [], [data]);
@@ -339,11 +309,7 @@ function K8sClustersList({
const handleFiltersChange = useCallback(
(value: IBuilderQuery['filters']): void => {
handleChangeQueryData('filters', value);
if (filtersInitialised) {
setCurrentPage(1);
} else {
setFiltersInitialised(true);
}
setCurrentPage(1);
if (value.items.length > 0) {
logEvent(InfraMonitoringEvents.FilterApplied, {
@@ -353,8 +319,7 @@ function K8sClustersList({
});
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[],
[handleChangeQueryData],
);
useEffect(() => {

View File

@@ -136,11 +136,6 @@ export const getK8sClustersListColumns = (
return columnsConfig as ColumnType<K8sClustersRowData>[];
};
const dotToUnder: Record<string, keyof K8sClustersData['meta']> = {
'k8s.cluster.name': 'k8s_cluster_name',
'k8s.cluster.uid': 'k8s_cluster_uid',
};
const getGroupByEle = (
cluster: K8sClustersData,
groupBy: IBuilderQuery['groupBy'],
@@ -148,13 +143,7 @@ const getGroupByEle = (
const groupByValues: string[] = [];
groupBy.forEach((group) => {
const rawKey = group.key as string;
// Choose mapped key if present, otherwise use rawKey
const metaKey = (dotToUnder[rawKey] ?? rawKey) as keyof typeof cluster.meta;
const value = cluster.meta[metaKey];
groupByValues.push(value);
groupByValues.push(cluster.meta[group.key as keyof typeof cluster.meta]);
});
return (

View File

@@ -29,13 +29,11 @@ import { AppState } from 'store/reducers';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import { getOrderByFromParams } from '../commonUtils';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
K8sCategory,
K8sEntityToAggregateAttributeMapping,
} from '../constants';
import K8sHeader from '../K8sHeader';
import LoadingContainer from '../LoadingContainer';
@@ -63,26 +61,11 @@ function K8sDaemonSetsList({
(state) => state.globalTime,
);
const [currentPage, setCurrentPage] = useState(1);
const [searchParams, setSearchParams] = useSearchParams();
const [currentPage, setCurrentPage] = useState(() => {
const page = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.CURRENT_PAGE);
if (page) {
return parseInt(page, 10);
}
return 1;
});
const [filtersInitialised, setFiltersInitialised] = useState(false);
const [expandedRowKeys, setExpandedRowKeys] = useState<string[]>([]);
useEffect(() => {
setSearchParams({
...Object.fromEntries(searchParams.entries()),
[INFRA_MONITORING_K8S_PARAMS_KEYS.CURRENT_PAGE]: currentPage.toString(),
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentPage]);
const [orderBy, setOrderBy] = useState<{
columnName: string;
order: 'asc' | 'desc';
@@ -134,16 +117,9 @@ function K8sDaemonSetsList({
// Reset pagination every time quick filters are changed
useEffect(() => {
if (quickFiltersLastUpdated !== -1) {
setCurrentPage(1);
}
setCurrentPage(1);
}, [quickFiltersLastUpdated]);
const { featureFlags } = useAppContext();
const dotMetricsEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
?.active || false;
const createFiltersForSelectedRowData = (
selectedRowData: K8sDaemonSetsRowData,
groupBy: IBuilderQuery['groupBy'],
@@ -203,8 +179,6 @@ function K8sDaemonSetsList({
queryKey: ['daemonSetList', fetchGroupedByRowDataQuery],
enabled: !!fetchGroupedByRowDataQuery && !!selectedRowData,
},
undefined,
dotMetricsEnabled,
);
const {
@@ -213,10 +187,8 @@ function K8sDaemonSetsList({
} = useGetAggregateKeys(
{
dataSource: currentQuery.builder.queryData[0].dataSource,
aggregateAttribute: GetK8sEntityToAggregateAttribute(
K8sCategory.DAEMONSETS,
dotMetricsEnabled,
),
aggregateAttribute:
K8sEntityToAggregateAttributeMapping[K8sCategory.DAEMONSETS],
aggregateOperator: 'noop',
searchText: '',
tagType: '',
@@ -257,8 +229,6 @@ function K8sDaemonSetsList({
queryKey: ['daemonSetList', query],
enabled: !!query,
},
undefined,
dotMetricsEnabled,
);
const daemonSetsData = useMemo(() => data?.payload?.data?.records || [], [
@@ -343,11 +313,7 @@ function K8sDaemonSetsList({
const handleFiltersChange = useCallback(
(value: IBuilderQuery['filters']): void => {
handleChangeQueryData('filters', value);
if (filtersInitialised) {
setCurrentPage(1);
} else {
setFiltersInitialised(true);
}
setCurrentPage(1);
if (value.items.length > 0) {
logEvent(InfraMonitoringEvents.FilterApplied, {
@@ -357,8 +323,7 @@ function K8sDaemonSetsList({
});
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[],
[handleChangeQueryData],
);
useEffect(() => {

View File

@@ -236,12 +236,6 @@ export const getK8sDaemonSetsListColumns = (
return columnsConfig as ColumnType<K8sDaemonSetsRowData>[];
};
const dotToUnder: Record<string, keyof K8sDaemonSetsData['meta']> = {
'k8s.daemonset.name': 'k8s_daemonset_name',
'k8s.namespace.name': 'k8s_namespace_name',
'k8s.cluster.name': 'k8s_cluster_name',
};
const getGroupByEle = (
daemonSet: K8sDaemonSetsData,
groupBy: IBuilderQuery['groupBy'],
@@ -249,13 +243,7 @@ const getGroupByEle = (
const groupByValues: string[] = [];
groupBy.forEach((group) => {
const rawKey = group.key as string;
// Choose mapped key if present, otherwise use rawKey
const metaKey = (dotToUnder[rawKey] ?? rawKey) as keyof typeof daemonSet.meta;
const value = daemonSet.meta[metaKey];
groupByValues.push(value);
groupByValues.push(daemonSet.meta[group.key as keyof typeof daemonSet.meta]);
});
return (

View File

@@ -29,13 +29,11 @@ import { AppState } from 'store/reducers';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import { getOrderByFromParams } from '../commonUtils';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
K8sCategory,
K8sEntityToAggregateAttributeMapping,
} from '../constants';
import K8sHeader from '../K8sHeader';
import LoadingContainer from '../LoadingContainer';
@@ -63,26 +61,10 @@ function K8sDeploymentsList({
(state) => state.globalTime,
);
const [searchParams, setSearchParams] = useSearchParams();
const [currentPage, setCurrentPage] = useState(() => {
const page = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.CURRENT_PAGE);
if (page) {
return parseInt(page, 10);
}
return 1;
});
const [filtersInitialised, setFiltersInitialised] = useState(false);
useEffect(() => {
setSearchParams({
...Object.fromEntries(searchParams.entries()),
[INFRA_MONITORING_K8S_PARAMS_KEYS.CURRENT_PAGE]: currentPage.toString(),
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentPage]);
const [currentPage, setCurrentPage] = useState(1);
const [expandedRowKeys, setExpandedRowKeys] = useState<string[]>([]);
const [searchParams, setSearchParams] = useSearchParams();
const [orderBy, setOrderBy] = useState<{
columnName: string;
@@ -135,16 +117,9 @@ function K8sDeploymentsList({
// Reset pagination every time quick filters are changed
useEffect(() => {
if (quickFiltersLastUpdated !== -1) {
setCurrentPage(1);
}
setCurrentPage(1);
}, [quickFiltersLastUpdated]);
const { featureFlags } = useAppContext();
const dotMetricsEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
?.active || false;
const createFiltersForSelectedRowData = (
selectedRowData: K8sDeploymentsRowData,
groupBy: IBuilderQuery['groupBy'],
@@ -204,8 +179,6 @@ function K8sDeploymentsList({
queryKey: ['deploymentList', fetchGroupedByRowDataQuery],
enabled: !!fetchGroupedByRowDataQuery && !!selectedRowData,
},
undefined,
dotMetricsEnabled,
);
const {
@@ -214,10 +187,8 @@ function K8sDeploymentsList({
} = useGetAggregateKeys(
{
dataSource: currentQuery.builder.queryData[0].dataSource,
aggregateAttribute: GetK8sEntityToAggregateAttribute(
K8sCategory.DEPLOYMENTS,
dotMetricsEnabled,
),
aggregateAttribute:
K8sEntityToAggregateAttributeMapping[K8sCategory.DEPLOYMENTS],
aggregateOperator: 'noop',
searchText: '',
tagType: '',
@@ -258,8 +229,6 @@ function K8sDeploymentsList({
queryKey: ['deploymentList', query],
enabled: !!query,
},
undefined,
dotMetricsEnabled,
);
const deploymentsData = useMemo(() => data?.payload?.data?.records || [], [
@@ -346,11 +315,7 @@ function K8sDeploymentsList({
const handleFiltersChange = useCallback(
(value: IBuilderQuery['filters']): void => {
handleChangeQueryData('filters', value);
if (filtersInitialised) {
setCurrentPage(1);
} else {
setFiltersInitialised(true);
}
setCurrentPage(1);
if (value.items.length > 0) {
logEvent(InfraMonitoringEvents.FilterApplied, {
@@ -360,8 +325,7 @@ function K8sDeploymentsList({
});
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[],
[handleChangeQueryData],
);
useEffect(() => {

View File

@@ -226,12 +226,6 @@ export const getK8sDeploymentsListColumns = (
return columnsConfig as ColumnType<K8sDeploymentsRowData>[];
};
const dotToUnder: Record<string, keyof K8sDeploymentsData['meta']> = {
'k8s.deployment.name': 'k8s_deployment_name',
'k8s.namespace.name': 'k8s_namespace_name',
'k8s.cluster.name': 'k8s_cluster_name',
};
const getGroupByEle = (
deployment: K8sDeploymentsData,
groupBy: IBuilderQuery['groupBy'],
@@ -239,14 +233,9 @@ const getGroupByEle = (
const groupByValues: string[] = [];
groupBy.forEach((group) => {
const rawKey = group.key as string;
// Choose mapped key if present, otherwise use rawKey
const metaKey = (dotToUnder[rawKey] ??
rawKey) as keyof typeof deployment.meta;
const value = deployment.meta[metaKey];
groupByValues.push(value);
groupByValues.push(
deployment.meta[group.key as keyof typeof deployment.meta],
);
});
return (

View File

@@ -259,7 +259,7 @@ export default function Events({
</div>
<div className="datetime-section">
<DateTimeSelectionV2
showAutoRefresh
showAutoRefresh={false}
showRefreshText={false}
hideShareModal
isModalTimeSelection={isModalTimeSelection}

View File

@@ -173,7 +173,7 @@
padding: 16px;
margin: 0;
// this is to offset chat support icon till we improve the design
// this is to offset intercom icon till we improve the design
padding-right: 72px;
.ant-pagination-item {

View File

@@ -86,7 +86,7 @@ function EntityLogsDetailedView({
</div>
<div className="datetime-section">
<DateTimeSelectionV2
showAutoRefresh
showAutoRefresh={false}
showRefreshText={false}
hideShareModal
isModalTimeSelection={isModalTimeSelection}

View File

@@ -23,15 +23,12 @@ import {
} from 'lib/dashboard/getQueryResults';
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useMemo, useRef } from 'react';
import { useQueries, UseQueryResult } from 'react-query';
import { SuccessResponse } from 'types/api';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { Options } from 'uplot';
import { FeatureKeys } from '../../../../constants/features';
import { useAppContext } from '../../../../providers/App/App';
interface EntityMetricsProps<T> {
timeRange: {
startTime: number;
@@ -52,7 +49,6 @@ interface EntityMetricsProps<T> {
node: T,
start: number,
end: number,
dotMetricsEnabled: boolean,
) => GetQueryResultsProps[];
queryKey: string;
category: K8sCategory;
@@ -69,25 +65,9 @@ function EntityMetrics<T>({
queryKey,
category,
}: EntityMetricsProps<T>): JSX.Element {
const { featureFlags } = useAppContext();
const dotMetricsEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
?.active || false;
const queryPayloads = useMemo(
() =>
getEntityQueryPayload(
entity,
timeRange.startTime,
timeRange.endTime,
dotMetricsEnabled,
),
[
getEntityQueryPayload,
entity,
timeRange.startTime,
timeRange.endTime,
dotMetricsEnabled,
],
() => getEntityQueryPayload(entity, timeRange.startTime, timeRange.endTime),
[getEntityQueryPayload, entity, timeRange.startTime, timeRange.endTime],
);
const queries = useQueries(
@@ -114,45 +94,6 @@ function EntityMetrics<T>({
[queries],
);
const [graphTimeIntervals, setGraphTimeIntervals] = useState<
{
start: number;
end: number;
}[]
>(
new Array(queries.length).fill({
start: timeRange.startTime,
end: timeRange.endTime,
}),
);
useEffect(() => {
setGraphTimeIntervals(
new Array(queries.length).fill({
start: timeRange.startTime,
end: timeRange.endTime,
}),
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [timeRange]);
const onDragSelect = useCallback(
(start: number, end: number, graphIndex: number) => {
const startTimestamp = Math.trunc(start);
const endTimestamp = Math.trunc(end);
setGraphTimeIntervals((prev) => {
const newIntervals = [...prev];
newIntervals[graphIndex] = {
start: Math.floor(startTimestamp / 1000),
end: Math.floor(endTimestamp / 1000),
};
return newIntervals;
});
},
[],
);
const options = useMemo(
() =>
queries.map(({ data }, idx) => {
@@ -167,9 +108,9 @@ function EntityMetrics<T>({
yAxisUnit: entityWidgetInfo[idx].yAxisUnit,
softMax: null,
softMin: null,
minTimeScale: graphTimeIntervals[idx].start,
maxTimeScale: graphTimeIntervals[idx].end,
onDragSelect: (start, end) => onDragSelect(start, end, idx),
minTimeScale: timeRange.startTime,
maxTimeScale: timeRange.endTime,
enableZoom: true,
});
}),
[
@@ -177,8 +118,8 @@ function EntityMetrics<T>({
isDarkMode,
dimensions,
entityWidgetInfo,
graphTimeIntervals,
onDragSelect,
timeRange.startTime,
timeRange.endTime,
],
);

View File

@@ -177,7 +177,7 @@ function EntityTraces({
</div>
<div className="datetime-section">
<DateTimeSelectionV2
showAutoRefresh
showAutoRefresh={false}
showRefreshText={false}
hideShareModal
isModalTimeSelection={isModalTimeSelection}

View File

@@ -447,7 +447,7 @@
padding: 16px;
margin: 0;
// this is to offset chat support icon till we improve the design
// this is to offset intercom icon till we improve the design
padding-right: 72px;
.ant-pagination-item {

View File

@@ -26,21 +26,19 @@ import { useState } from 'react';
import { useSearchParams } from 'react-router-dom-v5-compat';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { FeatureKeys } from '../../constants/features';
import { useAppContext } from '../../providers/App/App';
import K8sClustersList from './Clusters/K8sClustersList';
import {
GetClustersQuickFiltersConfig,
GetDaemonsetsQuickFiltersConfig,
GetDeploymentsQuickFiltersConfig,
GetJobsQuickFiltersConfig,
GetNamespaceQuickFiltersConfig,
GetNodesQuickFiltersConfig,
GetPodsQuickFiltersConfig,
GetStatefulsetsQuickFiltersConfig,
GetVolumesQuickFiltersConfig,
ClustersQuickFiltersConfig,
DaemonSetsQuickFiltersConfig,
DeploymentsQuickFiltersConfig,
INFRA_MONITORING_K8S_PARAMS_KEYS,
JobsQuickFiltersConfig,
K8sCategories,
NamespaceQuickFiltersConfig,
NodesQuickFiltersConfig,
PodsQuickFiltersConfig,
StatefulsetsQuickFiltersConfig,
VolumesQuickFiltersConfig,
} from './constants';
import K8sDaemonSetsList from './DaemonSets/K8sDaemonSetsList';
import K8sDeploymentsList from './Deployments/K8sDeploymentsList';
@@ -76,11 +74,6 @@ export default function InfraMonitoringK8s(): JSX.Element {
entityVersion: '',
});
const { featureFlags } = useAppContext();
const dotMetricsEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
?.active || false;
const handleFilterChange = (query: Query): void => {
// update the current query with the new filters
// in infra monitoring k8s, we are using only one query, hence updating the 0th index of queryData
@@ -116,7 +109,7 @@ export default function InfraMonitoringK8s(): JSX.Element {
children: (
<QuickFilters
source={QuickFiltersSource.INFRA_MONITORING}
config={GetPodsQuickFiltersConfig(dotMetricsEnabled)}
config={PodsQuickFiltersConfig}
handleFilterVisibilityChange={handleFilterVisibilityChange}
onFilterChange={handleFilterChange}
/>
@@ -136,7 +129,7 @@ export default function InfraMonitoringK8s(): JSX.Element {
children: (
<QuickFilters
source={QuickFiltersSource.INFRA_MONITORING}
config={GetNodesQuickFiltersConfig(dotMetricsEnabled)}
config={NodesQuickFiltersConfig}
handleFilterVisibilityChange={handleFilterVisibilityChange}
onFilterChange={handleFilterChange}
/>
@@ -159,7 +152,7 @@ export default function InfraMonitoringK8s(): JSX.Element {
children: (
<QuickFilters
source={QuickFiltersSource.INFRA_MONITORING}
config={GetNamespaceQuickFiltersConfig(dotMetricsEnabled)}
config={NamespaceQuickFiltersConfig}
handleFilterVisibilityChange={handleFilterVisibilityChange}
onFilterChange={handleFilterChange}
/>
@@ -179,7 +172,7 @@ export default function InfraMonitoringK8s(): JSX.Element {
children: (
<QuickFilters
source={QuickFiltersSource.INFRA_MONITORING}
config={GetClustersQuickFiltersConfig(dotMetricsEnabled)}
config={ClustersQuickFiltersConfig}
handleFilterVisibilityChange={handleFilterVisibilityChange}
onFilterChange={handleFilterChange}
/>
@@ -199,7 +192,7 @@ export default function InfraMonitoringK8s(): JSX.Element {
children: (
<QuickFilters
source={QuickFiltersSource.INFRA_MONITORING}
config={GetDeploymentsQuickFiltersConfig(dotMetricsEnabled)}
config={DeploymentsQuickFiltersConfig}
handleFilterVisibilityChange={handleFilterVisibilityChange}
onFilterChange={handleFilterChange}
/>
@@ -219,7 +212,7 @@ export default function InfraMonitoringK8s(): JSX.Element {
children: (
<QuickFilters
source={QuickFiltersSource.INFRA_MONITORING}
config={GetJobsQuickFiltersConfig(dotMetricsEnabled)}
config={JobsQuickFiltersConfig}
handleFilterVisibilityChange={handleFilterVisibilityChange}
onFilterChange={handleFilterChange}
/>
@@ -239,7 +232,7 @@ export default function InfraMonitoringK8s(): JSX.Element {
children: (
<QuickFilters
source={QuickFiltersSource.INFRA_MONITORING}
config={GetDaemonsetsQuickFiltersConfig(dotMetricsEnabled)}
config={DaemonSetsQuickFiltersConfig}
handleFilterVisibilityChange={handleFilterVisibilityChange}
onFilterChange={handleFilterChange}
/>
@@ -262,7 +255,7 @@ export default function InfraMonitoringK8s(): JSX.Element {
children: (
<QuickFilters
source={QuickFiltersSource.INFRA_MONITORING}
config={GetStatefulsetsQuickFiltersConfig(dotMetricsEnabled)}
config={StatefulsetsQuickFiltersConfig}
handleFilterVisibilityChange={handleFilterVisibilityChange}
onFilterChange={handleFilterChange}
/>
@@ -282,7 +275,7 @@ export default function InfraMonitoringK8s(): JSX.Element {
children: (
<QuickFilters
source={QuickFiltersSource.INFRA_MONITORING}
config={GetVolumesQuickFiltersConfig(dotMetricsEnabled)}
config={VolumesQuickFiltersConfig}
handleFilterVisibilityChange={handleFilterVisibilityChange}
onFilterChange={handleFilterChange}
/>

View File

@@ -30,415 +30,395 @@ export const getJobMetricsQueryPayload = (
job: K8sJobsData,
start: number,
end: number,
dotMetricsEnabled: boolean,
): GetQueryResultsProps[] => {
const k8sPodCpuUtilizationKey = dotMetricsEnabled
? 'k8s.pod.cpu.utilization'
: 'k8s_pod_cpu_utilization';
const k8sPodMemoryUsageKey = dotMetricsEnabled
? 'k8s.pod.memory.usage'
: 'k8s_pod_memory_usage';
const k8sPodNetworkIoKey = dotMetricsEnabled
? 'k8s.pod.network.io'
: 'k8s_pod_network_io';
const k8sPodNetworkErrorsKey = dotMetricsEnabled
? 'k8s.pod.network.errors'
: 'k8s_pod_network_errors';
const k8sJobNameKey = dotMetricsEnabled ? 'k8s.job.name' : 'k8s_job_name';
const k8sNamespaceNameKey = dotMetricsEnabled
? 'k8s.namespace.name'
: 'k8s_namespace_name';
return [
{
selectedTime: 'GLOBAL_TIME',
graphType: PANEL_TYPES.TIME_SERIES,
query: {
builder: {
queryData: [
{
aggregateAttribute: {
dataType: DataTypes.Float64,
id: 'k8s_pod_cpu_utilization--float64--Gauge--true',
isColumn: true,
isJSON: false,
key: k8sPodCpuUtilizationKey,
type: 'Gauge',
},
aggregateOperator: 'avg',
dataSource: DataSource.METRICS,
disabled: false,
expression: 'A',
filters: {
items: [
{
id: '6b59b690',
key: {
dataType: DataTypes.String,
id: 'k8s_job_name--string--tag--false',
isColumn: false,
isJSON: false,
key: k8sJobNameKey,
type: 'tag',
},
op: '=',
value: job.jobName,
},
{
id: '47b3adae',
key: {
dataType: DataTypes.String,
id: 'k8s_namespace_name--string--tag--false',
isColumn: false,
isJSON: false,
key: k8sNamespaceNameKey,
type: 'tag',
},
op: '=',
value: job.meta.k8s_namespace_name,
},
],
op: 'AND',
},
functions: [],
groupBy: [],
having: [],
legend: 'usage',
limit: null,
orderBy: [],
queryName: 'A',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: 60,
timeAggregation: 'avg',
): GetQueryResultsProps[] => [
{
selectedTime: 'GLOBAL_TIME',
graphType: PANEL_TYPES.TIME_SERIES,
query: {
builder: {
queryData: [
{
aggregateAttribute: {
dataType: DataTypes.Float64,
id: 'k8s_pod_cpu_utilization--float64--Gauge--true',
isColumn: true,
isJSON: false,
key: 'k8s_pod_cpu_utilization',
type: 'Gauge',
},
],
queryFormulas: [],
},
clickhouse_sql: [
{
aggregateOperator: 'avg',
dataSource: DataSource.METRICS,
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
id: v4(),
promql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
queryType: EQueryType.QUERY_BUILDER,
},
variables: {},
formatForWeb: false,
start,
end,
},
{
selectedTime: 'GLOBAL_TIME',
graphType: PANEL_TYPES.TIME_SERIES,
query: {
builder: {
queryData: [
{
aggregateAttribute: {
dataType: DataTypes.Float64,
id: 'k8s_pod_memory_usage--float64--Gauge--true',
isColumn: true,
isJSON: false,
key: k8sPodMemoryUsageKey,
type: 'Gauge',
},
aggregateOperator: 'avg',
dataSource: DataSource.METRICS,
disabled: false,
expression: 'A',
filters: {
items: [
{
id: '8c217f4d',
key: {
dataType: DataTypes.String,
id: 'k8s_job_name--string--tag--false',
isColumn: false,
isJSON: false,
key: k8sJobNameKey,
type: 'tag',
},
op: '=',
value: job.jobName,
},
{
id: '47b3adae',
key: {
dataType: DataTypes.String,
id: 'k8s_namespace_name--string--tag--false',
isColumn: false,
isJSON: false,
key: k8sNamespaceNameKey,
type: 'tag',
},
op: '=',
value: job.meta.k8s_namespace_name,
},
],
op: 'AND',
},
functions: [],
groupBy: [],
having: [],
legend: 'usage',
limit: null,
orderBy: [],
queryName: 'A',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: 60,
timeAggregation: 'avg',
},
],
queryFormulas: [],
},
clickhouse_sql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
id: v4(),
promql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
queryType: EQueryType.QUERY_BUILDER,
},
variables: {},
formatForWeb: false,
start,
end,
},
{
selectedTime: 'GLOBAL_TIME',
graphType: PANEL_TYPES.TIME_SERIES,
query: {
builder: {
queryData: [
{
aggregateAttribute: {
dataType: DataTypes.Float64,
id: 'k8s_pod_network_io--float64--Sum--true',
isColumn: true,
isJSON: false,
key: k8sPodNetworkIoKey,
type: 'Sum',
},
aggregateOperator: 'rate',
dataSource: DataSource.METRICS,
disabled: false,
expression: 'A',
filters: {
items: [
{
id: '2bbf9d0c',
key: {
dataType: DataTypes.String,
id: 'k8s_job_name--string--tag--false',
isColumn: false,
isJSON: false,
key: k8sJobNameKey,
type: 'tag',
},
op: '=',
value: job.jobName,
},
{
id: '47b3adae',
key: {
dataType: DataTypes.String,
id: 'k8s_namespace_name--string--tag--false',
isColumn: false,
isJSON: false,
key: k8sNamespaceNameKey,
type: 'tag',
},
op: '=',
value: job.meta.k8s_namespace_name,
},
],
op: 'AND',
},
functions: [],
groupBy: [
expression: 'A',
filters: {
items: [
{
dataType: DataTypes.String,
id: 'direction--string--tag--false',
isColumn: false,
isJSON: false,
key: 'direction',
type: 'tag',
id: '6b59b690',
key: {
dataType: DataTypes.String,
id: 'k8s_job_name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'k8s_job_name',
type: 'tag',
},
op: '=',
value: job.jobName,
},
{
dataType: DataTypes.String,
id: 'interface--string--tag--false',
isColumn: false,
isJSON: false,
key: 'interface',
type: 'tag',
id: '47b3adae',
key: {
dataType: DataTypes.String,
id: 'k8s_namespace_name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'k8s_namespace_name',
type: 'tag',
},
op: '=',
value: job.meta.k8s_namespace_name,
},
],
having: [],
legend: '{{direction}} :: {{interface}}',
limit: null,
orderBy: [],
queryName: 'A',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: 60,
timeAggregation: 'rate',
op: 'AND',
},
],
queryFormulas: [],
},
clickhouse_sql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
functions: [],
groupBy: [],
having: [],
legend: 'usage',
limit: null,
orderBy: [],
queryName: 'A',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: 60,
timeAggregation: 'avg',
},
],
id: v4(),
promql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
queryType: EQueryType.QUERY_BUILDER,
queryFormulas: [],
},
variables: {},
formatForWeb: false,
start,
end,
clickhouse_sql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
id: v4(),
promql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
queryType: EQueryType.QUERY_BUILDER,
},
{
selectedTime: 'GLOBAL_TIME',
graphType: PANEL_TYPES.TIME_SERIES,
query: {
builder: {
queryData: [
{
aggregateAttribute: {
dataType: DataTypes.Float64,
id: 'k8s_pod_network_errors--float64--Sum--true',
isColumn: true,
isJSON: false,
key: k8sPodNetworkErrorsKey,
type: 'Sum',
},
aggregateOperator: 'increase',
dataSource: DataSource.METRICS,
disabled: false,
expression: 'A',
filters: {
items: [
{
id: '448e6cf7',
key: {
dataType: DataTypes.String,
id: 'k8s_job_name--string--tag--false',
isColumn: false,
isJSON: false,
key: k8sJobNameKey,
type: 'tag',
},
op: '=',
value: job.jobName,
},
{
id: '47b3adae',
key: {
dataType: DataTypes.String,
id: 'k8s_namespace_name--string--tag--false',
isColumn: false,
isJSON: false,
key: k8sNamespaceNameKey,
type: 'tag',
},
op: '=',
value: job.meta.k8s_namespace_name,
},
],
op: 'AND',
},
functions: [],
groupBy: [
variables: {},
formatForWeb: false,
start,
end,
},
{
selectedTime: 'GLOBAL_TIME',
graphType: PANEL_TYPES.TIME_SERIES,
query: {
builder: {
queryData: [
{
aggregateAttribute: {
dataType: DataTypes.Float64,
id: 'k8s_pod_memory_usage--float64--Gauge--true',
isColumn: true,
isJSON: false,
key: 'k8s_pod_memory_usage',
type: 'Gauge',
},
aggregateOperator: 'avg',
dataSource: DataSource.METRICS,
disabled: false,
expression: 'A',
filters: {
items: [
{
dataType: DataTypes.String,
id: 'direction--string--tag--false',
isColumn: false,
isJSON: false,
key: 'direction',
type: 'tag',
id: '8c217f4d',
key: {
dataType: DataTypes.String,
id: 'k8s_job_name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'k8s_job_name',
type: 'tag',
},
op: '=',
value: job.jobName,
},
{
dataType: DataTypes.String,
id: 'interface--string--tag--false',
isColumn: false,
isJSON: false,
key: 'interface',
type: 'tag',
id: '47b3adae',
key: {
dataType: DataTypes.String,
id: 'k8s_namespace_name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'k8s_namespace_name',
type: 'tag',
},
op: '=',
value: job.meta.k8s_namespace_name,
},
],
having: [],
legend: '{{direction}} :: {{interface}}',
limit: null,
orderBy: [],
queryName: 'A',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: 60,
timeAggregation: 'increase',
op: 'AND',
},
],
queryFormulas: [],
},
clickhouse_sql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
functions: [],
groupBy: [],
having: [],
legend: 'usage',
limit: null,
orderBy: [],
queryName: 'A',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: 60,
timeAggregation: 'avg',
},
],
id: v4(),
promql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
queryType: EQueryType.QUERY_BUILDER,
queryFormulas: [],
},
variables: {},
formatForWeb: false,
start,
end,
clickhouse_sql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
id: v4(),
promql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
queryType: EQueryType.QUERY_BUILDER,
},
];
};
variables: {},
formatForWeb: false,
start,
end,
},
{
selectedTime: 'GLOBAL_TIME',
graphType: PANEL_TYPES.TIME_SERIES,
query: {
builder: {
queryData: [
{
aggregateAttribute: {
dataType: DataTypes.Float64,
id: 'k8s_pod_network_io--float64--Sum--true',
isColumn: true,
isJSON: false,
key: 'k8s_pod_network_io',
type: 'Sum',
},
aggregateOperator: 'rate',
dataSource: DataSource.METRICS,
disabled: false,
expression: 'A',
filters: {
items: [
{
id: '2bbf9d0c',
key: {
dataType: DataTypes.String,
id: 'k8s_job_name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'k8s_job_name',
type: 'tag',
},
op: '=',
value: job.jobName,
},
{
id: '47b3adae',
key: {
dataType: DataTypes.String,
id: 'k8s_namespace_name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'k8s_namespace_name',
type: 'tag',
},
op: '=',
value: job.meta.k8s_namespace_name,
},
],
op: 'AND',
},
functions: [],
groupBy: [
{
dataType: DataTypes.String,
id: 'direction--string--tag--false',
isColumn: false,
isJSON: false,
key: 'direction',
type: 'tag',
},
{
dataType: DataTypes.String,
id: 'interface--string--tag--false',
isColumn: false,
isJSON: false,
key: 'interface',
type: 'tag',
},
],
having: [],
legend: '{{direction}} :: {{interface}}',
limit: null,
orderBy: [],
queryName: 'A',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: 60,
timeAggregation: 'rate',
},
],
queryFormulas: [],
},
clickhouse_sql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
id: v4(),
promql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
queryType: EQueryType.QUERY_BUILDER,
},
variables: {},
formatForWeb: false,
start,
end,
},
{
selectedTime: 'GLOBAL_TIME',
graphType: PANEL_TYPES.TIME_SERIES,
query: {
builder: {
queryData: [
{
aggregateAttribute: {
dataType: DataTypes.Float64,
id: 'k8s_pod_network_errors--float64--Sum--true',
isColumn: true,
isJSON: false,
key: 'k8s_pod_network_errors',
type: 'Sum',
},
aggregateOperator: 'increase',
dataSource: DataSource.METRICS,
disabled: false,
expression: 'A',
filters: {
items: [
{
id: '448e6cf7',
key: {
dataType: DataTypes.String,
id: 'k8s_job_name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'k8s_job_name',
type: 'tag',
},
op: '=',
value: job.jobName,
},
{
id: '47b3adae',
key: {
dataType: DataTypes.String,
id: 'k8s_namespace_name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'k8s_namespace_name',
type: 'tag',
},
op: '=',
value: job.meta.k8s_namespace_name,
},
],
op: 'AND',
},
functions: [],
groupBy: [
{
dataType: DataTypes.String,
id: 'direction--string--tag--false',
isColumn: false,
isJSON: false,
key: 'direction',
type: 'tag',
},
{
dataType: DataTypes.String,
id: 'interface--string--tag--false',
isColumn: false,
isJSON: false,
key: 'interface',
type: 'tag',
},
],
having: [],
legend: '{{direction}} :: {{interface}}',
limit: null,
orderBy: [],
queryName: 'A',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: 60,
timeAggregation: 'increase',
},
],
queryFormulas: [],
},
clickhouse_sql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
id: v4(),
promql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
queryType: EQueryType.QUERY_BUILDER,
},
variables: {},
formatForWeb: false,
start,
end,
},
];

View File

@@ -29,13 +29,11 @@ import { AppState } from 'store/reducers';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import { getOrderByFromParams } from '../commonUtils';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
K8sCategory,
K8sEntityToAggregateAttributeMapping,
} from '../constants';
import K8sHeader from '../K8sHeader';
import LoadingContainer from '../LoadingContainer';
@@ -62,26 +60,11 @@ function K8sJobsList({
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const [searchParams, setSearchParams] = useSearchParams();
const [currentPage, setCurrentPage] = useState(() => {
const page = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.CURRENT_PAGE);
if (page) {
return parseInt(page, 10);
}
return 1;
});
const [filtersInitialised, setFiltersInitialised] = useState(false);
useEffect(() => {
setSearchParams({
...Object.fromEntries(searchParams.entries()),
[INFRA_MONITORING_K8S_PARAMS_KEYS.CURRENT_PAGE]: currentPage.toString(),
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentPage]);
const [currentPage, setCurrentPage] = useState(1);
const [expandedRowKeys, setExpandedRowKeys] = useState<string[]>([]);
const [searchParams, setSearchParams] = useSearchParams();
const [orderBy, setOrderBy] = useState<{
columnName: string;
@@ -129,16 +112,9 @@ function K8sJobsList({
// Reset pagination every time quick filters are changed
useEffect(() => {
if (quickFiltersLastUpdated !== -1) {
setCurrentPage(1);
}
setCurrentPage(1);
}, [quickFiltersLastUpdated]);
const { featureFlags } = useAppContext();
const dotMetricsEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
?.active || false;
const createFiltersForSelectedRowData = (
selectedRowData: K8sJobsRowData,
groupBy: IBuilderQuery['groupBy'],
@@ -192,15 +168,10 @@ function K8sJobsList({
isLoading: isLoadingGroupedByRowData,
isError: isErrorGroupedByRowData,
refetch: fetchGroupedByRowData,
} = useGetK8sJobsList(
fetchGroupedByRowDataQuery as K8sJobsListPayload,
{
queryKey: ['jobList', fetchGroupedByRowDataQuery],
enabled: !!fetchGroupedByRowDataQuery && !!selectedRowData,
},
undefined,
dotMetricsEnabled,
);
} = useGetK8sJobsList(fetchGroupedByRowDataQuery as K8sJobsListPayload, {
queryKey: ['jobList', fetchGroupedByRowDataQuery],
enabled: !!fetchGroupedByRowDataQuery && !!selectedRowData,
});
const {
data: groupByFiltersData,
@@ -208,10 +179,7 @@ function K8sJobsList({
} = useGetAggregateKeys(
{
dataSource: currentQuery.builder.queryData[0].dataSource,
aggregateAttribute: GetK8sEntityToAggregateAttribute(
K8sCategory.JOBS,
dotMetricsEnabled,
),
aggregateAttribute: K8sEntityToAggregateAttributeMapping[K8sCategory.JOBS],
aggregateOperator: 'noop',
searchText: '',
tagType: '',
@@ -257,8 +225,6 @@ function K8sJobsList({
queryKey: ['jobList', query],
enabled: !!query,
},
undefined,
dotMetricsEnabled,
);
const jobsData = useMemo(() => data?.payload?.data?.records || [], [data]);
@@ -334,11 +300,7 @@ function K8sJobsList({
const handleFiltersChange = useCallback(
(value: IBuilderQuery['filters']): void => {
handleChangeQueryData('filters', value);
if (filtersInitialised) {
setCurrentPage(1);
} else {
setFiltersInitialised(true);
}
setCurrentPage(1);
if (value.items.length > 0) {
logEvent(InfraMonitoringEvents.FilterApplied, {
@@ -348,8 +310,7 @@ function K8sJobsList({
});
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[],
[handleChangeQueryData],
);
useEffect(() => {

View File

@@ -263,12 +263,6 @@ export const getK8sJobsListColumns = (
return columnsConfig as ColumnType<K8sJobsRowData>[];
};
const dotToUnder: Record<string, keyof K8sJobsData['meta']> = {
'k8s.job.name': 'k8s_job_name',
'k8s.namespace.name': 'k8s_namespace_name',
'k8s.cluster.name': 'k8s_cluster_name',
};
const getGroupByEle = (
job: K8sJobsData,
groupBy: IBuilderQuery['groupBy'],
@@ -276,13 +270,7 @@ const getGroupByEle = (
const groupByValues: string[] = [];
groupBy.forEach((group) => {
const rawKey = group.key as string;
// Choose mapped key if present, otherwise use rawKey
const metaKey = (dotToUnder[rawKey] ?? rawKey) as keyof typeof job.meta;
const value = job.meta[metaKey];
groupByValues.push(value);
groupByValues.push(job.meta[group.key as keyof typeof job.meta]);
});
return (

View File

@@ -136,7 +136,7 @@ function K8sHeader({
<div className="k8s-list-controls-right">
<DateTimeSelectionV2
showAutoRefresh
showAutoRefresh={false}
showRefreshText={false}
hideShareModal
/>

View File

@@ -28,13 +28,11 @@ import { AppState } from 'store/reducers';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import { getOrderByFromParams } from '../commonUtils';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
K8sCategory,
K8sEntityToAggregateAttributeMapping,
} from '../constants';
import K8sHeader from '../K8sHeader';
import LoadingContainer from '../LoadingContainer';
@@ -62,26 +60,11 @@ function K8sNamespacesList({
(state) => state.globalTime,
);
const [currentPage, setCurrentPage] = useState(1);
const [expandedRowKeys, setExpandedRowKeys] = useState<string[]>([]);
const [searchParams, setSearchParams] = useSearchParams();
const [currentPage, setCurrentPage] = useState(() => {
const page = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.CURRENT_PAGE);
if (page) {
return parseInt(page, 10);
}
return 1;
});
const [filtersInitialised, setFiltersInitialised] = useState(false);
useEffect(() => {
setSearchParams({
...Object.fromEntries(searchParams.entries()),
[INFRA_MONITORING_K8S_PARAMS_KEYS.CURRENT_PAGE]: currentPage.toString(),
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentPage]);
const [orderBy, setOrderBy] = useState<{
columnName: string;
order: 'asc' | 'desc';
@@ -133,16 +116,9 @@ function K8sNamespacesList({
// Reset pagination every time quick filters are changed
useEffect(() => {
if (quickFiltersLastUpdated !== -1) {
setCurrentPage(1);
}
setCurrentPage(1);
}, [quickFiltersLastUpdated]);
const { featureFlags } = useAppContext();
const dotMetricsEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
?.active || false;
const createFiltersForSelectedRowData = (
selectedRowData: K8sNamespacesRowData,
groupBy: IBuilderQuery['groupBy'],
@@ -202,8 +178,6 @@ function K8sNamespacesList({
queryKey: ['namespaceList', fetchGroupedByRowDataQuery],
enabled: !!fetchGroupedByRowDataQuery && !!selectedRowData,
},
undefined,
dotMetricsEnabled,
);
const {
@@ -212,10 +186,8 @@ function K8sNamespacesList({
} = useGetAggregateKeys(
{
dataSource: currentQuery.builder.queryData[0].dataSource,
aggregateAttribute: GetK8sEntityToAggregateAttribute(
K8sCategory.NAMESPACES,
dotMetricsEnabled,
),
aggregateAttribute:
K8sEntityToAggregateAttributeMapping[K8sCategory.NAMESPACES],
aggregateOperator: 'noop',
searchText: '',
tagType: '',
@@ -256,8 +228,6 @@ function K8sNamespacesList({
queryKey: ['namespaceList', query],
enabled: !!query,
},
undefined,
dotMetricsEnabled,
);
const namespacesData = useMemo(() => data?.payload?.data?.records || [], [
@@ -342,11 +312,7 @@ function K8sNamespacesList({
const handleFiltersChange = useCallback(
(value: IBuilderQuery['filters']): void => {
handleChangeQueryData('filters', value);
if (filtersInitialised) {
setCurrentPage(1);
} else {
setFiltersInitialised(true);
}
setCurrentPage(1);
if (value.items.length > 0) {
logEvent(InfraMonitoringEvents.FilterApplied, {
@@ -356,8 +322,7 @@ function K8sNamespacesList({
});
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[],
[handleChangeQueryData],
);
useEffect(() => {

View File

@@ -122,11 +122,6 @@ export const getK8sNamespacesListColumns = (
return columnsConfig as ColumnType<K8sNamespacesRowData>[];
};
const dotToUnder: Record<string, keyof K8sNamespacesData['meta']> = {
'k8s.namespace.name': 'k8s_namespace_name',
'k8s.cluster.name': 'k8s_cluster_name',
};
const getGroupByEle = (
namespace: K8sNamespacesData,
groupBy: IBuilderQuery['groupBy'],
@@ -134,13 +129,7 @@ const getGroupByEle = (
const groupByValues: string[] = [];
groupBy.forEach((group) => {
const rawKey = group.key as string;
// Choose mapped key if present, otherwise use rawKey
const metaKey = (dotToUnder[rawKey] ?? rawKey) as keyof typeof namespace.meta;
const value = namespace.meta[metaKey];
groupByValues.push(value);
groupByValues.push(namespace.meta[group.key as keyof typeof namespace.meta]);
});
return (

View File

@@ -28,13 +28,11 @@ import { AppState } from 'store/reducers';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import { getOrderByFromParams } from '../commonUtils';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
K8sCategory,
K8sEntityToAggregateAttributeMapping,
} from '../constants';
import K8sHeader from '../K8sHeader';
import LoadingContainer from '../LoadingContainer';
@@ -61,26 +59,12 @@ function K8sNodesList({
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const [searchParams, setSearchParams] = useSearchParams();
const [currentPage, setCurrentPage] = useState(() => {
const page = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.CURRENT_PAGE);
if (page) {
return parseInt(page, 10);
}
return 1;
});
const [filtersInitialised, setFiltersInitialised] = useState(false);
useEffect(() => {
setSearchParams({
...Object.fromEntries(searchParams.entries()),
[INFRA_MONITORING_K8S_PARAMS_KEYS.CURRENT_PAGE]: currentPage.toString(),
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentPage]);
const [currentPage, setCurrentPage] = useState(1);
const [expandedRowKeys, setExpandedRowKeys] = useState<string[]>([]);
const [searchParams, setSearchParams] = useSearchParams();
const [orderBy, setOrderBy] = useState<{
columnName: string;
order: 'asc' | 'desc';
@@ -127,16 +111,9 @@ function K8sNodesList({
// Reset pagination every time quick filters are changed
useEffect(() => {
if (quickFiltersLastUpdated !== -1) {
setCurrentPage(1);
}
setCurrentPage(1);
}, [quickFiltersLastUpdated]);
const { featureFlags } = useAppContext();
const dotMetricsEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
?.active || false;
const createFiltersForSelectedRowData = (
selectedRowData: K8sNodesRowData,
groupBy: IBuilderQuery['groupBy'],
@@ -190,15 +167,10 @@ function K8sNodesList({
isLoading: isLoadingGroupedByRowData,
isError: isErrorGroupedByRowData,
refetch: fetchGroupedByRowData,
} = useGetK8sNodesList(
fetchGroupedByRowDataQuery as K8sNodesListPayload,
{
queryKey: ['nodeList', fetchGroupedByRowDataQuery],
enabled: !!fetchGroupedByRowDataQuery && !!selectedRowData,
},
undefined,
dotMetricsEnabled,
);
} = useGetK8sNodesList(fetchGroupedByRowDataQuery as K8sNodesListPayload, {
queryKey: ['nodeList', fetchGroupedByRowDataQuery],
enabled: !!fetchGroupedByRowDataQuery && !!selectedRowData,
});
const {
data: groupByFiltersData,
@@ -206,10 +178,7 @@ function K8sNodesList({
} = useGetAggregateKeys(
{
dataSource: currentQuery.builder.queryData[0].dataSource,
aggregateAttribute: GetK8sEntityToAggregateAttribute(
K8sCategory.NODES,
dotMetricsEnabled,
),
aggregateAttribute: K8sEntityToAggregateAttributeMapping[K8sCategory.NODES],
aggregateOperator: 'noop',
searchText: '',
tagType: '',
@@ -255,8 +224,6 @@ function K8sNodesList({
queryKey: ['nodeList', query],
enabled: !!query,
},
undefined,
dotMetricsEnabled,
);
const nodesData = useMemo(() => data?.payload?.data?.records || [], [data]);
@@ -332,11 +299,7 @@ function K8sNodesList({
const handleFiltersChange = useCallback(
(value: IBuilderQuery['filters']): void => {
handleChangeQueryData('filters', value);
if (filtersInitialised) {
setCurrentPage(1);
} else {
setFiltersInitialised(true);
}
setCurrentPage(1);
if (value.items.length > 0) {
logEvent(InfraMonitoringEvents.FilterApplied, {
@@ -346,8 +309,7 @@ function K8sNodesList({
});
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[],
[handleChangeQueryData],
);
useEffect(() => {

View File

@@ -152,12 +152,6 @@ export const getK8sNodesListColumns = (
return columnsConfig as ColumnType<K8sNodesRowData>[];
};
const dotToUnder: Record<string, keyof K8sNodesData['meta']> = {
'k8s.node.name': 'k8s_node_name',
'k8s.cluster.name': 'k8s_cluster_name',
'k8s.node.uid': 'k8s_node_uid',
};
const getGroupByEle = (
node: K8sNodesData,
groupBy: IBuilderQuery['groupBy'],
@@ -165,14 +159,7 @@ const getGroupByEle = (
const groupByValues: string[] = [];
groupBy.forEach((group) => {
const rawKey = group.key as string;
// Choose mapped key if present, otherwise use rawKey
const metaKey = (dotToUnder[rawKey] ?? rawKey) as keyof typeof node.meta;
const value = node.meta[metaKey];
groupByValues.push(value);
groupByValues.push(node.meta[group.key as keyof typeof node.meta]);
});
return (

View File

@@ -29,13 +29,11 @@ import { AppState } from 'store/reducers';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import { getOrderByFromParams } from '../commonUtils';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
K8sCategory,
K8sEntityToAggregateAttributeMapping,
} from '../constants';
import K8sHeader from '../K8sHeader';
import LoadingContainer from '../LoadingContainer';
@@ -66,22 +64,7 @@ function K8sPodsList({
);
const [searchParams, setSearchParams] = useSearchParams();
const [currentPage, setCurrentPage] = useState(() => {
const page = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.CURRENT_PAGE);
if (page) {
return parseInt(page, 10);
}
return 1;
});
const [filtersInitialised, setFiltersInitialised] = useState(false);
useEffect(() => {
setSearchParams({
...Object.fromEntries(searchParams.entries()),
[INFRA_MONITORING_K8S_PARAMS_KEYS.CURRENT_PAGE]: currentPage.toString(),
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentPage]);
const [currentPage, setCurrentPage] = useState(1);
const [addedColumns, setAddedColumns] = useState<IEntityColumn[]>([]);
@@ -120,21 +103,13 @@ function K8sPodsList({
[currentQuery?.builder?.queryData],
);
const { featureFlags } = useAppContext();
const dotMetricsEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
?.active || false;
const {
data: groupByFiltersData,
isLoading: isLoadingGroupByFilters,
} = useGetAggregateKeys(
{
dataSource: currentQuery.builder.queryData[0].dataSource,
aggregateAttribute: GetK8sEntityToAggregateAttribute(
K8sCategory.PODS,
dotMetricsEnabled,
),
aggregateAttribute: K8sEntityToAggregateAttributeMapping[K8sCategory.PODS],
aggregateOperator: 'noop',
searchText: '',
tagType: '',
@@ -148,9 +123,7 @@ function K8sPodsList({
// Reset pagination every time quick filters are changed
useEffect(() => {
if (quickFiltersLastUpdated !== -1) {
setCurrentPage(1);
}
setCurrentPage(1);
}, [quickFiltersLastUpdated]);
useEffect(() => {
@@ -211,8 +184,6 @@ function K8sPodsList({
queryKey: ['hostList', query],
enabled: !!query,
},
undefined,
dotMetricsEnabled,
);
const createFiltersForSelectedRowData = (
@@ -267,15 +238,10 @@ function K8sPodsList({
isLoading: isLoadingGroupedByRowData,
isError: isErrorGroupedByRowData,
refetch: fetchGroupedByRowData,
} = useGetK8sPodsList(
fetchGroupedByRowDataQuery as K8sPodsListPayload,
{
queryKey: ['hostList', fetchGroupedByRowDataQuery],
enabled: !!fetchGroupedByRowDataQuery && !!selectedRowData,
},
undefined,
dotMetricsEnabled,
);
} = useGetK8sPodsList(fetchGroupedByRowDataQuery as K8sPodsListPayload, {
queryKey: ['hostList', fetchGroupedByRowDataQuery],
enabled: !!fetchGroupedByRowDataQuery && !!selectedRowData,
});
const podsData = useMemo(() => data?.payload?.data?.records || [], [data]);
const totalCount = data?.payload?.data?.total || 0;
@@ -348,11 +314,7 @@ function K8sPodsList({
const handleFiltersChange = useCallback(
(value: IBuilderQuery['filters']): void => {
handleChangeQueryData('filters', value);
if (filtersInitialised) {
setCurrentPage(1);
} else {
setFiltersInitialised(true);
}
setCurrentPage(1);
if (value.items.length > 0) {
logEvent(InfraMonitoringEvents.FilterApplied, {
@@ -362,8 +324,7 @@ function K8sPodsList({
});
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[],
[handleChangeQueryData],
);
const handleGroupByChange = useCallback(

File diff suppressed because it is too large Load Diff

View File

@@ -29,13 +29,11 @@ import { AppState } from 'store/reducers';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import { getOrderByFromParams } from '../commonUtils';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
K8sCategory,
K8sEntityToAggregateAttributeMapping,
} from '../constants';
import K8sHeader from '../K8sHeader';
import LoadingContainer from '../LoadingContainer';
@@ -62,26 +60,10 @@ function K8sStatefulSetsList({
(state) => state.globalTime,
);
const [searchParams, setSearchParams] = useSearchParams();
const [currentPage, setCurrentPage] = useState(() => {
const page = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.CURRENT_PAGE);
if (page) {
return parseInt(page, 10);
}
return 1;
});
const [filtersInitialised, setFiltersInitialised] = useState(false);
useEffect(() => {
setSearchParams({
...Object.fromEntries(searchParams.entries()),
[INFRA_MONITORING_K8S_PARAMS_KEYS.CURRENT_PAGE]: currentPage.toString(),
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentPage]);
const [currentPage, setCurrentPage] = useState(1);
const [expandedRowKeys, setExpandedRowKeys] = useState<string[]>([]);
const [searchParams, setSearchParams] = useSearchParams();
const [orderBy, setOrderBy] = useState<{
columnName: string;
@@ -134,16 +116,9 @@ function K8sStatefulSetsList({
// Reset pagination every time quick filters are changed
useEffect(() => {
if (quickFiltersLastUpdated !== -1) {
setCurrentPage(1);
}
setCurrentPage(1);
}, [quickFiltersLastUpdated]);
const { featureFlags } = useAppContext();
const dotMetricsEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
?.active || false;
const createFiltersForSelectedRowData = (
selectedRowData: K8sStatefulSetsRowData,
groupBy: IBuilderQuery['groupBy'],
@@ -203,8 +178,6 @@ function K8sStatefulSetsList({
queryKey: ['statefulSetList', fetchGroupedByRowDataQuery],
enabled: !!fetchGroupedByRowDataQuery && !!selectedRowData,
},
undefined,
dotMetricsEnabled,
);
const {
@@ -213,10 +186,8 @@ function K8sStatefulSetsList({
} = useGetAggregateKeys(
{
dataSource: currentQuery.builder.queryData[0].dataSource,
aggregateAttribute: GetK8sEntityToAggregateAttribute(
K8sCategory.STATEFULSETS,
dotMetricsEnabled,
),
aggregateAttribute:
K8sEntityToAggregateAttributeMapping[K8sCategory.STATEFULSETS],
aggregateOperator: 'noop',
searchText: '',
tagType: '',
@@ -262,8 +233,6 @@ function K8sStatefulSetsList({
queryKey: ['statefulSetList', query],
enabled: !!query,
},
undefined,
dotMetricsEnabled,
);
const statefulSetsData = useMemo(() => data?.payload?.data?.records || [], [
@@ -345,11 +314,7 @@ function K8sStatefulSetsList({
const handleFiltersChange = useCallback(
(value: IBuilderQuery['filters']): void => {
handleChangeQueryData('filters', value);
if (filtersInitialised) {
setCurrentPage(1);
} else {
setFiltersInitialised(true);
}
setCurrentPage(1);
if (value.items.length > 0) {
logEvent(InfraMonitoringEvents.FilterApplied, {
@@ -359,8 +324,7 @@ function K8sStatefulSetsList({
});
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[],
[handleChangeQueryData],
);
useEffect(() => {

Some files were not shown because too many files have changed in this diff Show More