Compare commits
32 Commits
v0.75.0-pa
...
v0.72.0-cl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d22ecb9f7c | ||
|
|
02c2b55d5e | ||
|
|
9a75e27ec3 | ||
|
|
1fb3953614 | ||
|
|
37558facbe | ||
|
|
12f65f4a72 | ||
|
|
398760006b | ||
|
|
37323a64cf | ||
|
|
6d8d2e6b11 | ||
|
|
a8e8f31b00 | ||
|
|
c3164912e6 | ||
|
|
b215c6a0ce | ||
|
|
94c2398a08 | ||
|
|
acd9b97ee3 | ||
|
|
c5219ac157 | ||
|
|
2b32ce190f | ||
|
|
c7c7b25651 | ||
|
|
f548afe284 | ||
|
|
586f5255f0 | ||
|
|
62064f136d | ||
|
|
66adc7fbf9 | ||
|
|
f6b2d5a519 | ||
|
|
035999250e | ||
|
|
aecbb71ce4 | ||
|
|
01b6e22bbd | ||
|
|
dc15ee8176 | ||
|
|
e414215786 | ||
|
|
5fe04078e5 | ||
|
|
cf95b15ba1 | ||
|
|
3b550c485d | ||
|
|
784dccf298 | ||
|
|
aa26dc77af |
2
.github/workflows/push.yaml
vendored
2
.github/workflows/push.yaml
vendored
@@ -68,6 +68,8 @@ jobs:
|
|||||||
echo 'TUNNEL_URL="${{ secrets.TUNNEL_URL }}"' >> frontend/.env
|
echo 'TUNNEL_URL="${{ secrets.TUNNEL_URL }}"' >> frontend/.env
|
||||||
echo 'TUNNEL_DOMAIN="${{ secrets.TUNNEL_DOMAIN }}"' >> frontend/.env
|
echo 'TUNNEL_DOMAIN="${{ secrets.TUNNEL_DOMAIN }}"' >> frontend/.env
|
||||||
echo 'POSTHOG_KEY="${{ secrets.POSTHOG_KEY }}"' >> frontend/.env
|
echo 'POSTHOG_KEY="${{ secrets.POSTHOG_KEY }}"' >> frontend/.env
|
||||||
|
echo 'CUSTOMERIO_ID="${{ secrets.CUSTOMERIO_ID }}"' >> frontend/.env
|
||||||
|
echo 'CUSTOMERIO_SITE_ID="${{ secrets.CUSTOMERIO_SITE_ID }}"' >> frontend/.env
|
||||||
- name: Setup golang
|
- name: Setup golang
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ services:
|
|||||||
- query-service
|
- query-service
|
||||||
query-service:
|
query-service:
|
||||||
!!merge <<: *db-depend
|
!!merge <<: *db-depend
|
||||||
image: signoz/query-service:0.70.1
|
image: signoz/query-service:0.71.0
|
||||||
command:
|
command:
|
||||||
- --config=/root/config/prometheus.yml
|
- --config=/root/config/prometheus.yml
|
||||||
- --use-logs-new-schema=true
|
- --use-logs-new-schema=true
|
||||||
@@ -214,7 +214,7 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
frontend:
|
frontend:
|
||||||
!!merge <<: *common
|
!!merge <<: *common
|
||||||
image: signoz/frontend:0.70.1
|
image: signoz/frontend:0.71.0
|
||||||
depends_on:
|
depends_on:
|
||||||
- alertmanager
|
- alertmanager
|
||||||
- query-service
|
- query-service
|
||||||
@@ -224,7 +224,7 @@ services:
|
|||||||
- ../common/signoz/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
- ../common/signoz/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
||||||
otel-collector:
|
otel-collector:
|
||||||
!!merge <<: *db-depend
|
!!merge <<: *db-depend
|
||||||
image: signoz/signoz-otel-collector:0.111.25
|
image: signoz/signoz-otel-collector:0.111.26
|
||||||
command:
|
command:
|
||||||
- --config=/etc/otel-collector-config.yaml
|
- --config=/etc/otel-collector-config.yaml
|
||||||
- --manager-config=/etc/manager-config.yaml
|
- --manager-config=/etc/manager-config.yaml
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ services:
|
|||||||
- query-service
|
- query-service
|
||||||
query-service:
|
query-service:
|
||||||
!!merge <<: *db-depend
|
!!merge <<: *db-depend
|
||||||
image: signoz/query-service:0.70.1
|
image: signoz/query-service:0.71.0
|
||||||
command:
|
command:
|
||||||
- --config=/root/config/prometheus.yml
|
- --config=/root/config/prometheus.yml
|
||||||
- --use-logs-new-schema=true
|
- --use-logs-new-schema=true
|
||||||
@@ -150,7 +150,7 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
frontend:
|
frontend:
|
||||||
!!merge <<: *common
|
!!merge <<: *common
|
||||||
image: signoz/frontend:0.70.1
|
image: signoz/frontend:0.71.0
|
||||||
depends_on:
|
depends_on:
|
||||||
- alertmanager
|
- alertmanager
|
||||||
- query-service
|
- query-service
|
||||||
@@ -160,7 +160,7 @@ services:
|
|||||||
- ../common/signoz/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
- ../common/signoz/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
||||||
otel-collector:
|
otel-collector:
|
||||||
!!merge <<: *db-depend
|
!!merge <<: *db-depend
|
||||||
image: signoz/signoz-otel-collector:0.111.25
|
image: signoz/signoz-otel-collector:0.111.26
|
||||||
command:
|
command:
|
||||||
- --config=/etc/otel-collector-config.yaml
|
- --config=/etc/otel-collector-config.yaml
|
||||||
- --manager-config=/etc/manager-config.yaml
|
- --manager-config=/etc/manager-config.yaml
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
query-service:
|
query-service:
|
||||||
!!merge <<: *db-depend
|
!!merge <<: *db-depend
|
||||||
image: signoz/query-service:${DOCKER_TAG:-0.70.1}
|
image: signoz/query-service:${DOCKER_TAG:-0.71.0}
|
||||||
container_name: signoz-query-service
|
container_name: signoz-query-service
|
||||||
command:
|
command:
|
||||||
- --config=/root/config/prometheus.yml
|
- --config=/root/config/prometheus.yml
|
||||||
@@ -222,7 +222,7 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
frontend:
|
frontend:
|
||||||
!!merge <<: *common
|
!!merge <<: *common
|
||||||
image: signoz/frontend:${DOCKER_TAG:-0.70.1}
|
image: signoz/frontend:${DOCKER_TAG:-0.71.0}
|
||||||
container_name: signoz-frontend
|
container_name: signoz-frontend
|
||||||
depends_on:
|
depends_on:
|
||||||
- alertmanager
|
- alertmanager
|
||||||
@@ -234,7 +234,7 @@ services:
|
|||||||
# TODO: support otel-collector multiple replicas. Nginx/Traefik for loadbalancing?
|
# TODO: support otel-collector multiple replicas. Nginx/Traefik for loadbalancing?
|
||||||
otel-collector:
|
otel-collector:
|
||||||
!!merge <<: *db-depend
|
!!merge <<: *db-depend
|
||||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.111.25}
|
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.111.26}
|
||||||
container_name: signoz-otel-collector
|
container_name: signoz-otel-collector
|
||||||
command:
|
command:
|
||||||
- --config=/etc/otel-collector-config.yaml
|
- --config=/etc/otel-collector-config.yaml
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
query-service:
|
query-service:
|
||||||
!!merge <<: *db-depend
|
!!merge <<: *db-depend
|
||||||
image: signoz/query-service:${DOCKER_TAG:-0.70.1}
|
image: signoz/query-service:${DOCKER_TAG:-0.71.0}
|
||||||
container_name: signoz-query-service
|
container_name: signoz-query-service
|
||||||
command:
|
command:
|
||||||
- --config=/root/config/prometheus.yml
|
- --config=/root/config/prometheus.yml
|
||||||
@@ -157,7 +157,7 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
frontend:
|
frontend:
|
||||||
!!merge <<: *common
|
!!merge <<: *common
|
||||||
image: signoz/frontend:${DOCKER_TAG:-0.70.1}
|
image: signoz/frontend:${DOCKER_TAG:-0.71.0}
|
||||||
container_name: signoz-frontend
|
container_name: signoz-frontend
|
||||||
depends_on:
|
depends_on:
|
||||||
- alertmanager
|
- alertmanager
|
||||||
@@ -168,7 +168,7 @@ services:
|
|||||||
- ../common/signoz/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
- ../common/signoz/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
||||||
otel-collector:
|
otel-collector:
|
||||||
!!merge <<: *db-depend
|
!!merge <<: *db-depend
|
||||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.111.25}
|
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.111.26}
|
||||||
container_name: signoz-otel-collector
|
container_name: signoz-otel-collector
|
||||||
command:
|
command:
|
||||||
- --config=/etc/otel-collector-config.yaml
|
- --config=/etc/otel-collector-config.yaml
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
query-service:
|
query-service:
|
||||||
!!merge <<: *db-depend
|
!!merge <<: *db-depend
|
||||||
image: signoz/query-service:${DOCKER_TAG:-0.70.1}
|
image: signoz/query-service:${DOCKER_TAG:-0.71.0}
|
||||||
container_name: signoz-query-service
|
container_name: signoz-query-service
|
||||||
command:
|
command:
|
||||||
- --config=/root/config/prometheus.yml
|
- --config=/root/config/prometheus.yml
|
||||||
@@ -155,7 +155,7 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
frontend:
|
frontend:
|
||||||
!!merge <<: *common
|
!!merge <<: *common
|
||||||
image: signoz/frontend:${DOCKER_TAG:-0.70.1}
|
image: signoz/frontend:${DOCKER_TAG:-0.71.0}
|
||||||
container_name: signoz-frontend
|
container_name: signoz-frontend
|
||||||
depends_on:
|
depends_on:
|
||||||
- alertmanager
|
- alertmanager
|
||||||
@@ -166,7 +166,7 @@ services:
|
|||||||
- ../common/signoz/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
- ../common/signoz/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
||||||
otel-collector:
|
otel-collector:
|
||||||
!!merge <<: *db-depend
|
!!merge <<: *db-depend
|
||||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.111.25}
|
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.111.26}
|
||||||
container_name: signoz-otel-collector
|
container_name: signoz-otel-collector
|
||||||
command:
|
command:
|
||||||
- --config=/etc/otel-collector-config.yaml
|
- --config=/etc/otel-collector-config.yaml
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
_ "net/http/pprof" // http profiler
|
_ "net/http/pprof" // http profiler
|
||||||
"os"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -149,25 +148,20 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var reader interfaces.DataConnector
|
var reader interfaces.DataConnector
|
||||||
storage := os.Getenv("STORAGE")
|
qb := db.NewDataConnector(
|
||||||
if storage == "clickhouse" {
|
serverOptions.SigNoz.SQLStore.SQLxDB(),
|
||||||
zap.L().Info("Using ClickHouse as datastore ...")
|
serverOptions.SigNoz.TelemetryStore.ClickHouseDB(),
|
||||||
qb := db.NewDataConnector(
|
serverOptions.PromConfigPath,
|
||||||
serverOptions.SigNoz.SQLStore.SQLxDB(),
|
lm,
|
||||||
serverOptions.SigNoz.TelemetryStore.ClickHouseDB(),
|
serverOptions.Cluster,
|
||||||
serverOptions.PromConfigPath,
|
serverOptions.UseLogsNewSchema,
|
||||||
lm,
|
serverOptions.UseTraceNewSchema,
|
||||||
serverOptions.Cluster,
|
fluxIntervalForTraceDetail,
|
||||||
serverOptions.UseLogsNewSchema,
|
serverOptions.SigNoz.Cache,
|
||||||
serverOptions.UseTraceNewSchema,
|
)
|
||||||
fluxIntervalForTraceDetail,
|
go qb.Start(readerReady)
|
||||||
serverOptions.SigNoz.Cache,
|
reader = qb
|
||||||
)
|
|
||||||
go qb.Start(readerReady)
|
|
||||||
reader = qb
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("storage type: %s is not supported in query service", storage)
|
|
||||||
}
|
|
||||||
skipConfig := &basemodel.SkipConfig{}
|
skipConfig := &basemodel.SkipConfig{}
|
||||||
if serverOptions.SkipTopLvlOpsPath != "" {
|
if serverOptions.SkipTopLvlOpsPath != "" {
|
||||||
// read skip config
|
// read skip config
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
basedao "go.signoz.io/signoz/pkg/query-service/dao"
|
basedao "go.signoz.io/signoz/pkg/query-service/dao"
|
||||||
basedsql "go.signoz.io/signoz/pkg/query-service/dao/sqlite"
|
basedsql "go.signoz.io/signoz/pkg/query-service/dao/sqlite"
|
||||||
baseint "go.signoz.io/signoz/pkg/query-service/interfaces"
|
baseint "go.signoz.io/signoz/pkg/query-service/interfaces"
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type modelDao struct {
|
type modelDao struct {
|
||||||
@@ -29,41 +28,6 @@ func (m *modelDao) checkFeature(key string) error {
|
|||||||
return m.flags.CheckFeature(key)
|
return m.flags.CheckFeature(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func columnExists(db *sqlx.DB, tableName, columnName string) bool {
|
|
||||||
query := fmt.Sprintf("PRAGMA table_info(%s);", tableName)
|
|
||||||
rows, err := db.Query(query)
|
|
||||||
if err != nil {
|
|
||||||
zap.L().Error("Failed to query table info", zap.Error(err))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var (
|
|
||||||
cid int
|
|
||||||
name string
|
|
||||||
ctype string
|
|
||||||
notnull int
|
|
||||||
dflt_value *string
|
|
||||||
pk int
|
|
||||||
)
|
|
||||||
for rows.Next() {
|
|
||||||
err := rows.Scan(&cid, &name, &ctype, ¬null, &dflt_value, &pk)
|
|
||||||
if err != nil {
|
|
||||||
zap.L().Error("Failed to scan table info", zap.Error(err))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if name == columnName {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = rows.Err()
|
|
||||||
if err != nil {
|
|
||||||
zap.L().Error("Failed to scan table info", zap.Error(err))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitDB creates and extends base model DB repository
|
// InitDB creates and extends base model DB repository
|
||||||
func InitDB(inputDB *sqlx.DB) (*modelDao, error) {
|
func InitDB(inputDB *sqlx.DB) (*modelDao, error) {
|
||||||
dao, err := basedsql.InitDB(inputDB)
|
dao, err := basedsql.InitDB(inputDB)
|
||||||
@@ -73,69 +37,6 @@ func InitDB(inputDB *sqlx.DB) (*modelDao, error) {
|
|||||||
// set package variable so dependent base methods (e.g. AuthCache) will work
|
// set package variable so dependent base methods (e.g. AuthCache) will work
|
||||||
basedao.SetDB(dao)
|
basedao.SetDB(dao)
|
||||||
m := &modelDao{ModelDaoSqlite: dao}
|
m := &modelDao{ModelDaoSqlite: dao}
|
||||||
|
|
||||||
table_schema := `
|
|
||||||
PRAGMA foreign_keys = ON;
|
|
||||||
CREATE TABLE IF NOT EXISTS org_domains(
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
org_id TEXT NOT NULL,
|
|
||||||
name VARCHAR(50) NOT NULL UNIQUE,
|
|
||||||
created_at INTEGER NOT NULL,
|
|
||||||
updated_at INTEGER,
|
|
||||||
data TEXT NOT NULL,
|
|
||||||
FOREIGN KEY(org_id) REFERENCES organizations(id)
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS personal_access_tokens (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
role TEXT NOT NULL,
|
|
||||||
user_id TEXT NOT NULL,
|
|
||||||
token TEXT NOT NULL UNIQUE,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
created_at INTEGER NOT NULL,
|
|
||||||
expires_at INTEGER NOT NULL,
|
|
||||||
updated_at INTEGER NOT NULL,
|
|
||||||
last_used INTEGER NOT NULL,
|
|
||||||
revoked BOOLEAN NOT NULL,
|
|
||||||
updated_by_user_id TEXT NOT NULL,
|
|
||||||
FOREIGN KEY(user_id) REFERENCES users(id)
|
|
||||||
);
|
|
||||||
`
|
|
||||||
|
|
||||||
_, err = m.DB().Exec(table_schema)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error in creating tables: %v", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !columnExists(m.DB(), "personal_access_tokens", "role") {
|
|
||||||
_, err = m.DB().Exec("ALTER TABLE personal_access_tokens ADD COLUMN role TEXT NOT NULL DEFAULT 'ADMIN';")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error in adding column: %v", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !columnExists(m.DB(), "personal_access_tokens", "updated_at") {
|
|
||||||
_, err = m.DB().Exec("ALTER TABLE personal_access_tokens ADD COLUMN updated_at INTEGER NOT NULL DEFAULT 0;")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error in adding column: %v", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !columnExists(m.DB(), "personal_access_tokens", "last_used") {
|
|
||||||
_, err = m.DB().Exec("ALTER TABLE personal_access_tokens ADD COLUMN last_used INTEGER NOT NULL DEFAULT 0;")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error in adding column: %v", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !columnExists(m.DB(), "personal_access_tokens", "revoked") {
|
|
||||||
_, err = m.DB().Exec("ALTER TABLE personal_access_tokens ADD COLUMN revoked BOOLEAN NOT NULL DEFAULT FALSE;")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error in adding column: %v", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !columnExists(m.DB(), "personal_access_tokens", "updated_by_user_id") {
|
|
||||||
_, err = m.DB().Exec("ALTER TABLE personal_access_tokens ADD COLUMN updated_by_user_id TEXT NOT NULL DEFAULT '';")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error in adding column: %v", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"github.com/mattn/go-sqlite3"
|
"github.com/mattn/go-sqlite3"
|
||||||
|
|
||||||
"go.signoz.io/signoz/ee/query-service/license/sqlite"
|
|
||||||
"go.signoz.io/signoz/ee/query-service/model"
|
"go.signoz.io/signoz/ee/query-service/model"
|
||||||
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
@@ -28,10 +27,6 @@ func NewLicenseRepo(db *sqlx.DB) Repo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Repo) InitDB(inputDB *sqlx.DB) error {
|
|
||||||
return sqlite.InitDB(inputDB)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Repo) GetLicensesV3(ctx context.Context) ([]*model.LicenseV3, error) {
|
func (r *Repo) GetLicensesV3(ctx context.Context) ([]*model.LicenseV3, error) {
|
||||||
licensesData := []model.LicenseDB{}
|
licensesData := []model.LicenseDB{}
|
||||||
licenseV3Data := []*model.LicenseV3{}
|
licenseV3Data := []*model.LicenseV3{}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package license
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -50,11 +49,6 @@ func StartManager(db *sqlx.DB, features ...basemodel.Feature) (*Manager, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
repo := NewLicenseRepo(db)
|
repo := NewLicenseRepo(db)
|
||||||
err := repo.InitDB(db)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to initiate license repo: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m := &Manager{
|
m := &Manager{
|
||||||
repo: &repo,
|
repo: &repo,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
package sqlite
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
func InitDB(db *sqlx.DB) error {
|
|
||||||
var err error
|
|
||||||
if db == nil {
|
|
||||||
return fmt.Errorf("invalid db connection")
|
|
||||||
}
|
|
||||||
|
|
||||||
table_schema := `CREATE TABLE IF NOT EXISTS licenses(
|
|
||||||
key TEXT PRIMARY KEY,
|
|
||||||
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
planDetails TEXT,
|
|
||||||
activationId TEXT,
|
|
||||||
validationMessage TEXT,
|
|
||||||
lastValidated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS sites(
|
|
||||||
uuid TEXT PRIMARY KEY,
|
|
||||||
alias VARCHAR(180) DEFAULT 'PROD',
|
|
||||||
url VARCHAR(300),
|
|
||||||
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
`
|
|
||||||
|
|
||||||
_, err = db.Exec(table_schema)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error in creating licenses table: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
table_schema = `CREATE TABLE IF NOT EXISTS feature_status (
|
|
||||||
name TEXT PRIMARY KEY,
|
|
||||||
active bool,
|
|
||||||
usage INTEGER DEFAULT 0,
|
|
||||||
usage_limit INTEGER DEFAULT 0,
|
|
||||||
route TEXT
|
|
||||||
);`
|
|
||||||
|
|
||||||
_, err = db.Exec(table_schema)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error in creating feature_status table: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
table_schema = `CREATE TABLE IF NOT EXISTS licenses_v3 (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
key TEXT NOT NULL UNIQUE,
|
|
||||||
data TEXT
|
|
||||||
);`
|
|
||||||
|
|
||||||
_, err = db.Exec(table_schema)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error in creating licenses_v3 table: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -18,7 +18,6 @@ import (
|
|||||||
"go.signoz.io/signoz/pkg/config/fileprovider"
|
"go.signoz.io/signoz/pkg/config/fileprovider"
|
||||||
"go.signoz.io/signoz/pkg/query-service/auth"
|
"go.signoz.io/signoz/pkg/query-service/auth"
|
||||||
baseconst "go.signoz.io/signoz/pkg/query-service/constants"
|
baseconst "go.signoz.io/signoz/pkg/query-service/constants"
|
||||||
"go.signoz.io/signoz/pkg/query-service/migrate"
|
|
||||||
"go.signoz.io/signoz/pkg/query-service/version"
|
"go.signoz.io/signoz/pkg/query-service/version"
|
||||||
"go.signoz.io/signoz/pkg/signoz"
|
"go.signoz.io/signoz/pkg/signoz"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
@@ -183,12 +182,6 @@ func main() {
|
|||||||
zap.L().Info("JWT secret key set successfully.")
|
zap.L().Info("JWT secret key set successfully.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := migrate.Migrate(signoz.SQLStore.SQLxDB()); err != nil {
|
|
||||||
zap.L().Error("Failed to migrate", zap.Error(err))
|
|
||||||
} else {
|
|
||||||
zap.L().Info("Migration successful")
|
|
||||||
}
|
|
||||||
|
|
||||||
server, err := app.NewServer(serverOptions)
|
server, err := app.NewServer(serverOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
zap.L().Fatal("Failed to create server", zap.Error(err))
|
zap.L().Fatal("Failed to create server", zap.Error(err))
|
||||||
|
|||||||
@@ -139,8 +139,8 @@ func NewLicenseV3(data map[string]interface{}) (*LicenseV3, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// if license status is inactive then default it to basic
|
// if license status is invalid then default it to basic
|
||||||
if status == LicenseStatusInactive {
|
if status == LicenseStatusInvalid {
|
||||||
planName = PlanNameBasic
|
planName = PlanNameBasic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
LicenseStatusInactive = "INACTIVE"
|
LicenseStatusInvalid = "INVALID"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DisableUpsell = "DISABLE_UPSELL"
|
const DisableUpsell = "DISABLE_UPSELL"
|
||||||
@@ -157,6 +157,13 @@ var BasicPlan = basemodel.FeatureSet{
|
|||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.AWSIntegration,
|
||||||
|
Active: false,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProPlan = basemodel.FeatureSet{
|
var ProPlan = basemodel.FeatureSet{
|
||||||
@@ -279,6 +286,13 @@ var ProPlan = basemodel.FeatureSet{
|
|||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.AWSIntegration,
|
||||||
|
Active: false,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var EnterprisePlan = basemodel.FeatureSet{
|
var EnterprisePlan = basemodel.FeatureSet{
|
||||||
@@ -415,4 +429,11 @@ var EnterprisePlan = basemodel.FeatureSet{
|
|||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.AWSIntegration,
|
||||||
|
Active: false,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@
|
|||||||
"overlayscrollbars": "^2.8.1",
|
"overlayscrollbars": "^2.8.1",
|
||||||
"overlayscrollbars-react": "^0.5.6",
|
"overlayscrollbars-react": "^0.5.6",
|
||||||
"papaparse": "5.4.1",
|
"papaparse": "5.4.1",
|
||||||
"posthog-js": "1.160.3",
|
"posthog-js": "1.215.5",
|
||||||
"rc-tween-one": "3.0.6",
|
"rc-tween-one": "3.0.6",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-addons-update": "15.6.3",
|
"react-addons-update": "15.6.3",
|
||||||
|
|||||||
@@ -57,5 +57,8 @@
|
|||||||
"ALERT_OVERVIEW": "SigNoz | Alert Rule Overview",
|
"ALERT_OVERVIEW": "SigNoz | Alert Rule Overview",
|
||||||
"MESSAGING_QUEUES": "SigNoz | Messaging Queues",
|
"MESSAGING_QUEUES": "SigNoz | Messaging Queues",
|
||||||
"INFRASTRUCTURE_MONITORING_HOSTS": "SigNoz | Infra Monitoring",
|
"INFRASTRUCTURE_MONITORING_HOSTS": "SigNoz | Infra Monitoring",
|
||||||
"INFRASTRUCTURE_MONITORING_KUBERNETES": "SigNoz | Infra Monitoring"
|
"INFRASTRUCTURE_MONITORING_KUBERNETES": "SigNoz | Infra Monitoring",
|
||||||
|
"METRICS_EXPLORER": "SigNoz | Metrics Explorer",
|
||||||
|
"METRICS_EXPLORER_EXPLORER": "SigNoz | Metrics Explorer",
|
||||||
|
"METRICS_EXPLORER_VIEWS": "SigNoz | Metrics Explorer"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
|||||||
const currentRoute = mapRoutes.get('current');
|
const currentRoute = mapRoutes.get('current');
|
||||||
const shouldSuspendWorkspace =
|
const shouldSuspendWorkspace =
|
||||||
activeLicenseV3.status === LicenseStatus.SUSPENDED &&
|
activeLicenseV3.status === LicenseStatus.SUSPENDED &&
|
||||||
activeLicenseV3.state === LicenseState.PAYMENT_FAILED;
|
activeLicenseV3.state === LicenseState.DEFAULTED;
|
||||||
|
|
||||||
if (shouldSuspendWorkspace && currentRoute) {
|
if (shouldSuspendWorkspace && currentRoute) {
|
||||||
navigateToWorkSpaceSuspended(currentRoute);
|
navigateToWorkSpaceSuspended(currentRoute);
|
||||||
|
|||||||
@@ -110,6 +110,18 @@ function App(): JSX.Element {
|
|||||||
source: 'signoz-ui',
|
source: 'signoz-ui',
|
||||||
isPaidUser: !!licenses?.trialConvertedToSubscription,
|
isPaidUser: !!licenses?.trialConvertedToSubscription,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
window.cioanalytics &&
|
||||||
|
typeof window.cioanalytics.identify === 'function'
|
||||||
|
) {
|
||||||
|
window.cioanalytics.reset();
|
||||||
|
window.cioanalytics.identify(email, {
|
||||||
|
name: user.name,
|
||||||
|
email,
|
||||||
|
role: user.role,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[hostname, isFetchingLicenses, licenses, org],
|
[hostname, isFetchingLicenses, licenses, org],
|
||||||
|
|||||||
@@ -264,3 +264,8 @@ export const CeleryOverview = Loadable(
|
|||||||
/* webpackChunkName: "CeleryOverview" */ 'pages/Celery/CeleryOverview/CeleryOverview'
|
/* webpackChunkName: "CeleryOverview" */ 'pages/Celery/CeleryOverview/CeleryOverview'
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const MetricsExplorer = Loadable(
|
||||||
|
() =>
|
||||||
|
import(/* webpackChunkName: "MetricsExplorer" */ 'pages/MetricsExplorer'),
|
||||||
|
);
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import {
|
|||||||
LogsExplorer,
|
LogsExplorer,
|
||||||
LogsIndexToFields,
|
LogsIndexToFields,
|
||||||
LogsSaveViews,
|
LogsSaveViews,
|
||||||
|
MetricsExplorer,
|
||||||
MySettings,
|
MySettings,
|
||||||
NewDashboardPage,
|
NewDashboardPage,
|
||||||
OldLogsExplorer,
|
OldLogsExplorer,
|
||||||
@@ -435,6 +436,27 @@ const routes: AppRoutes[] = [
|
|||||||
key: 'INFRASTRUCTURE_MONITORING_KUBERNETES',
|
key: 'INFRASTRUCTURE_MONITORING_KUBERNETES',
|
||||||
isPrivate: true,
|
isPrivate: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: ROUTES.METRICS_EXPLORER,
|
||||||
|
exact: true,
|
||||||
|
component: MetricsExplorer,
|
||||||
|
key: 'METRICS_EXPLORER',
|
||||||
|
isPrivate: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ROUTES.METRICS_EXPLORER_EXPLORER,
|
||||||
|
exact: true,
|
||||||
|
component: MetricsExplorer,
|
||||||
|
key: 'METRICS_EXPLORER_EXPLORER',
|
||||||
|
isPrivate: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ROUTES.METRICS_EXPLORER_VIEWS,
|
||||||
|
exact: true,
|
||||||
|
component: MetricsExplorer,
|
||||||
|
key: 'METRICS_EXPLORER_VIEWS',
|
||||||
|
isPrivate: true,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const SUPPORT_ROUTE: AppRoutes = {
|
export const SUPPORT_ROUTE: AppRoutes = {
|
||||||
|
|||||||
19
frontend/src/api/Integrations/removeAwsIntegrationAccount.ts
Normal file
19
frontend/src/api/Integrations/removeAwsIntegrationAccount.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import axios from 'api';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
|
||||||
|
const removeAwsIntegrationAccount = async (
|
||||||
|
accountId: string,
|
||||||
|
): Promise<SuccessResponse<Record<string, never>> | ErrorResponse> => {
|
||||||
|
const response = await axios.post(
|
||||||
|
`/cloud-integrations/aws/accounts/${accountId}/disconnect`,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: response.data.status,
|
||||||
|
payload: response.data.data,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default removeAwsIntegrationAccount;
|
||||||
@@ -9,19 +9,22 @@ import {
|
|||||||
import {
|
import {
|
||||||
AccountConfigPayload,
|
AccountConfigPayload,
|
||||||
AccountConfigResponse,
|
AccountConfigResponse,
|
||||||
|
ConnectionParams,
|
||||||
ConnectionUrlResponse,
|
ConnectionUrlResponse,
|
||||||
} from 'types/api/integrations/aws';
|
} from 'types/api/integrations/aws';
|
||||||
|
|
||||||
export const getAwsAccounts = async (): Promise<CloudAccount[]> => {
|
export const getAwsAccounts = async (): Promise<CloudAccount[]> => {
|
||||||
const response = await axios.get('/cloud-integrations/aws/accounts');
|
const response = await axios.get('/cloud-integrations/aws/accounts');
|
||||||
|
|
||||||
return response.data.data;
|
return response.data.data.accounts;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAwsServices = async (
|
export const getAwsServices = async (
|
||||||
accountId?: string,
|
cloudAccountId?: string,
|
||||||
): Promise<Service[]> => {
|
): Promise<Service[]> => {
|
||||||
const params = accountId ? { account_id: accountId } : undefined;
|
const params = cloudAccountId
|
||||||
|
? { cloud_account_id: cloudAccountId }
|
||||||
|
: undefined;
|
||||||
const response = await axios.get('/cloud-integrations/aws/services', {
|
const response = await axios.get('/cloud-integrations/aws/services', {
|
||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
@@ -31,9 +34,11 @@ export const getAwsServices = async (
|
|||||||
|
|
||||||
export const getServiceDetails = async (
|
export const getServiceDetails = async (
|
||||||
serviceId: string,
|
serviceId: string,
|
||||||
accountId?: string,
|
cloudAccountId?: string,
|
||||||
): Promise<ServiceData> => {
|
): Promise<ServiceData> => {
|
||||||
const params = accountId ? { account_id: accountId } : undefined;
|
const params = cloudAccountId
|
||||||
|
? { cloud_account_id: cloudAccountId }
|
||||||
|
: undefined;
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
`/cloud-integrations/aws/services/${serviceId}`,
|
`/cloud-integrations/aws/services/${serviceId}`,
|
||||||
{ params },
|
{ params },
|
||||||
@@ -74,3 +79,10 @@ export const updateServiceConfig = async (
|
|||||||
);
|
);
|
||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getConnectionParams = async (): Promise<ConnectionParams> => {
|
||||||
|
const response = await axios.get(
|
||||||
|
'/cloud-integrations/aws/accounts/generate-connection-params',
|
||||||
|
);
|
||||||
|
return response.data.data;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import './CeleryOverviewConfigOptions.styles.scss';
|
import './CeleryOverviewConfigOptions.styles.scss';
|
||||||
|
|
||||||
import { Color } from '@signozhq/design-tokens';
|
import { Row, Select, Spin } from 'antd';
|
||||||
import { Button, Row, Select, Spin, Tooltip } from 'antd';
|
|
||||||
import {
|
import {
|
||||||
getValuesFromQueryParams,
|
getValuesFromQueryParams,
|
||||||
setQueryParamsFromOptions,
|
setQueryParamsFromOptions,
|
||||||
@@ -10,10 +9,7 @@ import { useCeleryFilterOptions } from 'components/CeleryTask/useCeleryFilterOpt
|
|||||||
import { SelectMaxTagPlaceholder } from 'components/MessagingQueues/MQCommon/MQCommon';
|
import { SelectMaxTagPlaceholder } from 'components/MessagingQueues/MQCommon/MQCommon';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import useUrlQuery from 'hooks/useUrlQuery';
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
import { Check, Share2 } from 'lucide-react';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { useHistory, useLocation } from 'react-router-dom';
|
import { useHistory, useLocation } from 'react-router-dom';
|
||||||
import { useCopyToClipboard } from 'react-use';
|
|
||||||
|
|
||||||
interface SelectOptionConfig {
|
interface SelectOptionConfig {
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
@@ -66,10 +62,6 @@ function FilterSelect({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function CeleryOverviewConfigOptions(): JSX.Element {
|
function CeleryOverviewConfigOptions(): JSX.Element {
|
||||||
const [isURLCopied, setIsURLCopied] = useState(false);
|
|
||||||
|
|
||||||
const [, handleCopyToClipboard] = useCopyToClipboard();
|
|
||||||
|
|
||||||
const selectConfigs: SelectOptionConfig[] = [
|
const selectConfigs: SelectOptionConfig[] = [
|
||||||
{
|
{
|
||||||
placeholder: 'Service Name',
|
placeholder: 'Service Name',
|
||||||
@@ -98,14 +90,6 @@ function CeleryOverviewConfigOptions(): JSX.Element {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleShareURL = (): void => {
|
|
||||||
handleCopyToClipboard(window.location.href);
|
|
||||||
setIsURLCopied(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsURLCopied(false);
|
|
||||||
}, 1000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="celery-overview-filters">
|
<div className="celery-overview-filters">
|
||||||
<Row className="celery-filters">
|
<Row className="celery-filters">
|
||||||
@@ -118,19 +102,6 @@ function CeleryOverviewConfigOptions(): JSX.Element {
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Row>
|
</Row>
|
||||||
<Tooltip title="Share this" arrow={false}>
|
|
||||||
<Button
|
|
||||||
className="periscope-btn copy-url-btn"
|
|
||||||
onClick={handleShareURL}
|
|
||||||
icon={
|
|
||||||
isURLCopied ? (
|
|
||||||
<Check size={14} color={Color.BG_FOREST_500} />
|
|
||||||
) : (
|
|
||||||
<Share2 size={14} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,6 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 18px; /* 163.636% */
|
line-height: 18px; /* 163.636% */
|
||||||
letter-spacing: 0.44px;
|
letter-spacing: 0.44px;
|
||||||
text-transform: uppercase;
|
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|||||||
@@ -218,35 +218,44 @@ function getColumns(data: RowData[]): TableColumnsType<RowData> {
|
|||||||
showTitle: false,
|
showTitle: false,
|
||||||
},
|
},
|
||||||
width: 200,
|
width: 200,
|
||||||
sorter: (a: RowData, b: RowData): number =>
|
sorter: (a: RowData, b: RowData): number => {
|
||||||
String(a.error_percentage).localeCompare(String(b.error_percentage)),
|
const aValue = Number(a.error_percentage);
|
||||||
|
const bValue = Number(b.error_percentage);
|
||||||
|
return aValue - bValue;
|
||||||
|
},
|
||||||
render: ProgressRender,
|
render: ProgressRender,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'LATENCY (P95)',
|
title: 'LATENCY (P95) in ms',
|
||||||
dataIndex: 'p95_latency',
|
dataIndex: 'p95_latency',
|
||||||
key: 'p95_latency',
|
key: 'p95_latency',
|
||||||
ellipsis: {
|
ellipsis: {
|
||||||
showTitle: false,
|
showTitle: false,
|
||||||
},
|
},
|
||||||
width: 100,
|
width: 100,
|
||||||
sorter: (a: RowData, b: RowData): number =>
|
sorter: (a: RowData, b: RowData): number => {
|
||||||
String(a.p95_latency).localeCompare(String(b.p95_latency)),
|
const aValue = Number(a.p95_latency);
|
||||||
|
const bValue = Number(b.p95_latency);
|
||||||
|
return aValue - bValue;
|
||||||
|
},
|
||||||
render: (value: number | string): string => {
|
render: (value: number | string): string => {
|
||||||
if (!isNumber(value)) return value.toString();
|
if (!isNumber(value)) return value.toString();
|
||||||
return (typeof value === 'string' ? parseFloat(value) : value).toFixed(3);
|
return (typeof value === 'string' ? parseFloat(value) : value).toFixed(3);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'THROUGHPUT',
|
title: 'THROUGHPUT (ops/s)',
|
||||||
dataIndex: 'throughput',
|
dataIndex: 'throughput',
|
||||||
key: 'throughput',
|
key: 'throughput',
|
||||||
ellipsis: {
|
ellipsis: {
|
||||||
showTitle: false,
|
showTitle: false,
|
||||||
},
|
},
|
||||||
width: 100,
|
width: 100,
|
||||||
sorter: (a: RowData, b: RowData): number =>
|
sorter: (a: RowData, b: RowData): number => {
|
||||||
String(a.throughput).localeCompare(String(b.throughput)),
|
const aValue = Number(a.throughput);
|
||||||
|
const bValue = Number(b.throughput);
|
||||||
|
return aValue - bValue;
|
||||||
|
},
|
||||||
render: (value: number | string): string => {
|
render: (value: number | string): string => {
|
||||||
if (!isNumber(value)) return value.toString();
|
if (!isNumber(value)) return value.toString();
|
||||||
return (typeof value === 'string' ? parseFloat(value) : value).toFixed(3);
|
return (typeof value === 'string' ? parseFloat(value) : value).toFixed(3);
|
||||||
|
|||||||
@@ -2,24 +2,16 @@ import './CeleryTaskDetail.style.scss';
|
|||||||
|
|
||||||
import { Color, Spacing } from '@signozhq/design-tokens';
|
import { Color, Spacing } from '@signozhq/design-tokens';
|
||||||
import { Divider, Drawer, Typography } from 'antd';
|
import { Divider, Drawer, Typography } from 'antd';
|
||||||
import { QueryParams } from 'constants/query';
|
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import useUrlQuery from 'hooks/useUrlQuery';
|
|
||||||
import { X } from 'lucide-react';
|
import { X } from 'lucide-react';
|
||||||
import { useEffect, useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
|
||||||
import { useHistory, useLocation } from 'react-router-dom';
|
|
||||||
import { UpdateTimeInterval } from 'store/actions';
|
|
||||||
import { AppState } from 'store/reducers';
|
|
||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
|
|
||||||
import CeleryTaskGraph from '../CeleryTaskGraph/CeleryTaskGraph';
|
import CeleryTaskGraph from '../CeleryTaskGraph/CeleryTaskGraph';
|
||||||
|
import { createFiltersFromData } from '../CeleryUtils';
|
||||||
import { useNavigateToTraces } from '../useNavigateToTraces';
|
import { useNavigateToTraces } from '../useNavigateToTraces';
|
||||||
|
|
||||||
export type CeleryTaskData = {
|
export type CeleryTaskData = {
|
||||||
@@ -39,40 +31,6 @@ export type CeleryTaskDetailProps = {
|
|||||||
drawerOpen: boolean;
|
drawerOpen: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const createFiltersFromData = (
|
|
||||||
data: Record<string, any>,
|
|
||||||
): Array<{
|
|
||||||
id: string;
|
|
||||||
key: {
|
|
||||||
key: string;
|
|
||||||
dataType: DataTypes;
|
|
||||||
type: string;
|
|
||||||
isColumn: boolean;
|
|
||||||
isJSON: boolean;
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
op: string;
|
|
||||||
value: string;
|
|
||||||
}> => {
|
|
||||||
const excludeKeys = ['A', 'A_without_unit'];
|
|
||||||
|
|
||||||
return Object.entries(data)
|
|
||||||
.filter(([key]) => !excludeKeys.includes(key))
|
|
||||||
.map(([key, value]) => ({
|
|
||||||
id: uuidv4(),
|
|
||||||
key: {
|
|
||||||
key,
|
|
||||||
dataType: DataTypes.String,
|
|
||||||
type: 'tag',
|
|
||||||
isColumn: false,
|
|
||||||
isJSON: false,
|
|
||||||
id: `${key}--string--tag--false`,
|
|
||||||
},
|
|
||||||
op: '=',
|
|
||||||
value: value.toString(),
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function CeleryTaskDetail({
|
export default function CeleryTaskDetail({
|
||||||
widgetData,
|
widgetData,
|
||||||
taskData,
|
taskData,
|
||||||
@@ -85,7 +43,7 @@ export default function CeleryTaskDetail({
|
|||||||
!!taskData.entity && !!taskData.timeRange[0] && drawerOpen;
|
!!taskData.entity && !!taskData.timeRange[0] && drawerOpen;
|
||||||
|
|
||||||
const formatTimestamp = (timestamp: number): string =>
|
const formatTimestamp = (timestamp: number): string =>
|
||||||
dayjs(timestamp * 1000).format('MM-DD-YYYY hh:mm A');
|
dayjs(timestamp).format('DD-MM-YYYY hh:mm A');
|
||||||
|
|
||||||
const [totalTask, setTotalTask] = useState(0);
|
const [totalTask, setTotalTask] = useState(0);
|
||||||
|
|
||||||
@@ -93,52 +51,9 @@ export default function CeleryTaskDetail({
|
|||||||
setTotalTask((graphData?.result?.[0] as any)?.table?.rows.length);
|
setTotalTask((graphData?.result?.[0] as any)?.table?.rows.length);
|
||||||
};
|
};
|
||||||
|
|
||||||
// set time range
|
|
||||||
const { minTime, maxTime, selectedTime } = useSelector<
|
|
||||||
AppState,
|
|
||||||
GlobalReducer
|
|
||||||
>((state) => state.globalTime);
|
|
||||||
|
|
||||||
const startTime = taskData.timeRange[0];
|
const startTime = taskData.timeRange[0];
|
||||||
const endTime = taskData.timeRange[1];
|
const endTime = taskData.timeRange[1];
|
||||||
|
|
||||||
const urlQuery = useUrlQuery();
|
|
||||||
const location = useLocation();
|
|
||||||
const history = useHistory();
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
urlQuery.delete(QueryParams.relativeTime);
|
|
||||||
urlQuery.set(QueryParams.startTime, startTime.toString());
|
|
||||||
urlQuery.set(QueryParams.endTime, endTime.toString());
|
|
||||||
|
|
||||||
const generatedUrl = `${location.pathname}?${urlQuery.toString()}`;
|
|
||||||
history.replace(generatedUrl);
|
|
||||||
|
|
||||||
if (startTime !== endTime) {
|
|
||||||
dispatch(UpdateTimeInterval('custom', [startTime, endTime]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (): void => {
|
|
||||||
urlQuery.delete(QueryParams.relativeTime);
|
|
||||||
urlQuery.delete(QueryParams.startTime);
|
|
||||||
urlQuery.delete(QueryParams.endTime);
|
|
||||||
|
|
||||||
if (selectedTime !== 'custom') {
|
|
||||||
dispatch(UpdateTimeInterval(selectedTime));
|
|
||||||
urlQuery.set(QueryParams.relativeTime, selectedTime);
|
|
||||||
} else {
|
|
||||||
dispatch(UpdateTimeInterval('custom', [minTime / 1e6, maxTime / 1e6]));
|
|
||||||
urlQuery.set(QueryParams.startTime, Math.floor(minTime / 1e6).toString());
|
|
||||||
urlQuery.set(QueryParams.endTime, Math.floor(maxTime / 1e6).toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
const generatedUrl = `${location.pathname}?${urlQuery.toString()}`;
|
|
||||||
history.replace(generatedUrl);
|
|
||||||
};
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const navigateToTrace = useNavigateToTraces();
|
const navigateToTrace = useNavigateToTraces();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -149,10 +64,8 @@ export default function CeleryTaskDetail({
|
|||||||
<Typography.Text className="title">{`Details - ${taskData.entity}`}</Typography.Text>
|
<Typography.Text className="title">{`Details - ${taskData.entity}`}</Typography.Text>
|
||||||
<div>
|
<div>
|
||||||
<Typography.Text className="subtitle">
|
<Typography.Text className="subtitle">
|
||||||
{`${formatTimestamp(taskData.timeRange[0])} ${
|
{`${formatTimestamp(startTime)} ${
|
||||||
taskData.timeRange[1]
|
endTime ? `- ${formatTimestamp(endTime)}` : ''
|
||||||
? `- ${formatTimestamp(taskData.timeRange[1])}`
|
|
||||||
: ''
|
|
||||||
}`}
|
}`}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
<Divider type="vertical" />
|
<Divider type="vertical" />
|
||||||
@@ -185,8 +98,10 @@ export default function CeleryTaskDetail({
|
|||||||
...rowData,
|
...rowData,
|
||||||
[taskData.entity]: taskData.value,
|
[taskData.entity]: taskData.value,
|
||||||
});
|
});
|
||||||
navigateToTrace(filters);
|
navigateToTrace(filters, startTime, endTime);
|
||||||
}}
|
}}
|
||||||
|
start={startTime}
|
||||||
|
end={endTime}
|
||||||
/>
|
/>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ import { Widgets } from 'types/api/dashboard/getAll';
|
|||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
import { CaptureDataProps } from '../CeleryTaskDetail/CeleryTaskDetail';
|
import { CaptureDataProps } from '../CeleryTaskDetail/CeleryTaskDetail';
|
||||||
|
import {
|
||||||
|
applyCeleryFilterOnWidgetData,
|
||||||
|
getFiltersFromQueryParams,
|
||||||
|
} from '../CeleryUtils';
|
||||||
import { useGetGraphCustomSeries } from '../useGetGraphCustomSeries';
|
import { useGetGraphCustomSeries } from '../useGetGraphCustomSeries';
|
||||||
import {
|
import {
|
||||||
celeryAllStateWidgetData,
|
celeryAllStateWidgetData,
|
||||||
@@ -72,26 +76,60 @@ function CeleryTaskBar({
|
|||||||
|
|
||||||
const [barState, setBarState] = useState<CeleryTaskState>(CeleryTaskState.All);
|
const [barState, setBarState] = useState<CeleryTaskState>(CeleryTaskState.All);
|
||||||
|
|
||||||
|
const selectedFilters = useMemo(
|
||||||
|
() =>
|
||||||
|
getFiltersFromQueryParams(
|
||||||
|
QueryParams.taskName,
|
||||||
|
urlQuery,
|
||||||
|
'celery.task_name',
|
||||||
|
),
|
||||||
|
[urlQuery],
|
||||||
|
);
|
||||||
|
|
||||||
const celeryAllStateData = useMemo(
|
const celeryAllStateData = useMemo(
|
||||||
() => celeryAllStateWidgetData(minTime, maxTime),
|
() => celeryAllStateWidgetData(minTime, maxTime),
|
||||||
[minTime, maxTime],
|
[minTime, maxTime],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const celeryAllStateFilteredData = useMemo(
|
||||||
|
() =>
|
||||||
|
applyCeleryFilterOnWidgetData(selectedFilters || [], celeryAllStateData),
|
||||||
|
[selectedFilters, celeryAllStateData],
|
||||||
|
);
|
||||||
|
|
||||||
const celeryFailedStateData = useMemo(
|
const celeryFailedStateData = useMemo(
|
||||||
() => celeryFailedStateWidgetData(minTime, maxTime),
|
() => celeryFailedStateWidgetData(minTime, maxTime),
|
||||||
[minTime, maxTime],
|
[minTime, maxTime],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const celeryFailedStateFilteredData = useMemo(
|
||||||
|
() =>
|
||||||
|
applyCeleryFilterOnWidgetData(selectedFilters || [], celeryFailedStateData),
|
||||||
|
[selectedFilters, celeryFailedStateData],
|
||||||
|
);
|
||||||
|
|
||||||
const celeryRetryStateData = useMemo(
|
const celeryRetryStateData = useMemo(
|
||||||
() => celeryRetryStateWidgetData(minTime, maxTime),
|
() => celeryRetryStateWidgetData(minTime, maxTime),
|
||||||
[minTime, maxTime],
|
[minTime, maxTime],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const celeryRetryStateFilteredData = useMemo(
|
||||||
|
() =>
|
||||||
|
applyCeleryFilterOnWidgetData(selectedFilters || [], celeryRetryStateData),
|
||||||
|
[selectedFilters, celeryRetryStateData],
|
||||||
|
);
|
||||||
|
|
||||||
const celerySuccessStateData = useMemo(
|
const celerySuccessStateData = useMemo(
|
||||||
() => celerySuccessStateWidgetData(minTime, maxTime),
|
() => celerySuccessStateWidgetData(minTime, maxTime),
|
||||||
[minTime, maxTime],
|
[minTime, maxTime],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const celerySuccessStateFilteredData = useMemo(
|
||||||
|
() =>
|
||||||
|
applyCeleryFilterOnWidgetData(selectedFilters || [], celerySuccessStateData),
|
||||||
|
[selectedFilters, celerySuccessStateData],
|
||||||
|
);
|
||||||
|
|
||||||
const onGraphClick = (
|
const onGraphClick = (
|
||||||
widgetData: Widgets,
|
widgetData: Widgets,
|
||||||
xValue: number,
|
xValue: number,
|
||||||
@@ -141,7 +179,7 @@ function CeleryTaskBar({
|
|||||||
<div className="celery-task-graph-grid-content">
|
<div className="celery-task-graph-grid-content">
|
||||||
{barState === CeleryTaskState.All && (
|
{barState === CeleryTaskState.All && (
|
||||||
<GridCard
|
<GridCard
|
||||||
widget={celeryAllStateData}
|
widget={celeryAllStateFilteredData}
|
||||||
headerMenuList={[...ViewMenuAction]}
|
headerMenuList={[...ViewMenuAction]}
|
||||||
onDragSelect={onDragSelect}
|
onDragSelect={onDragSelect}
|
||||||
isQueryEnabled={queryEnabled}
|
isQueryEnabled={queryEnabled}
|
||||||
@@ -153,7 +191,7 @@ function CeleryTaskBar({
|
|||||||
)}
|
)}
|
||||||
{barState === CeleryTaskState.Failed && (
|
{barState === CeleryTaskState.Failed && (
|
||||||
<GridCard
|
<GridCard
|
||||||
widget={celeryFailedStateData}
|
widget={celeryFailedStateFilteredData}
|
||||||
headerMenuList={[...ViewMenuAction]}
|
headerMenuList={[...ViewMenuAction]}
|
||||||
onDragSelect={onDragSelect}
|
onDragSelect={onDragSelect}
|
||||||
isQueryEnabled={queryEnabled}
|
isQueryEnabled={queryEnabled}
|
||||||
@@ -165,7 +203,7 @@ function CeleryTaskBar({
|
|||||||
)}
|
)}
|
||||||
{barState === CeleryTaskState.Retry && (
|
{barState === CeleryTaskState.Retry && (
|
||||||
<GridCard
|
<GridCard
|
||||||
widget={celeryRetryStateData}
|
widget={celeryRetryStateFilteredData}
|
||||||
headerMenuList={[...ViewMenuAction]}
|
headerMenuList={[...ViewMenuAction]}
|
||||||
onDragSelect={onDragSelect}
|
onDragSelect={onDragSelect}
|
||||||
isQueryEnabled={queryEnabled}
|
isQueryEnabled={queryEnabled}
|
||||||
@@ -177,7 +215,7 @@ function CeleryTaskBar({
|
|||||||
)}
|
)}
|
||||||
{barState === CeleryTaskState.Successful && (
|
{barState === CeleryTaskState.Successful && (
|
||||||
<GridCard
|
<GridCard
|
||||||
widget={celerySuccessStateData}
|
widget={celerySuccessStateFilteredData}
|
||||||
headerMenuList={[...ViewMenuAction]}
|
headerMenuList={[...ViewMenuAction]}
|
||||||
onDragSelect={onDragSelect}
|
onDragSelect={onDragSelect}
|
||||||
isQueryEnabled={queryEnabled}
|
isQueryEnabled={queryEnabled}
|
||||||
|
|||||||
@@ -116,9 +116,8 @@
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|
||||||
.metric-page-grid {
|
.metric-page-grid {
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: 50% 50%;
|
flex-direction: row;
|
||||||
align-items: flex-start;
|
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
@@ -144,6 +143,11 @@
|
|||||||
gap: 16px;
|
gap: 16px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
border: 1px dashed var(--bg-slate-50);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px 24px 6px 12px;
|
||||||
|
width: max-content;
|
||||||
|
|
||||||
.configure-option-Info-text {
|
.configure-option-Info-text {
|
||||||
color: var(--bg-vanilla-400);
|
color: var(--bg-vanilla-400);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@@ -241,11 +245,13 @@
|
|||||||
|
|
||||||
.lightMode {
|
.lightMode {
|
||||||
.celery-task-graph-grid-container {
|
.celery-task-graph-grid-container {
|
||||||
.celery-task-graph-grid {
|
.celery-task-graph-worker-count {
|
||||||
.celery-task-graph-worker-count {
|
border: 1px solid var(--bg-vanilla-300);
|
||||||
border: 1px solid var(--bg-vanilla-300);
|
background: unset;
|
||||||
background: unset;
|
}
|
||||||
}
|
|
||||||
|
.row-panel .row-panel-section .section-title {
|
||||||
|
color: var(--bg-ink-400);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,4 +280,8 @@
|
|||||||
background-color: var(--bg-ink-400);
|
background-color: var(--bg-ink-400);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.configure-option-Info {
|
||||||
|
border: 1px dashed var(--bg-robin-400);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ function CeleryTaskGraph({
|
|||||||
openTracesButton,
|
openTracesButton,
|
||||||
onOpenTraceBtnClick,
|
onOpenTraceBtnClick,
|
||||||
applyCeleryTaskFilter,
|
applyCeleryTaskFilter,
|
||||||
|
customErrorMessage,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
}: {
|
}: {
|
||||||
widgetData: Widgets;
|
widgetData: Widgets;
|
||||||
onClick?: (task: CaptureDataProps) => void;
|
onClick?: (task: CaptureDataProps) => void;
|
||||||
@@ -42,6 +45,9 @@ function CeleryTaskGraph({
|
|||||||
openTracesButton?: boolean;
|
openTracesButton?: boolean;
|
||||||
onOpenTraceBtnClick?: (record: RowData) => void;
|
onOpenTraceBtnClick?: (record: RowData) => void;
|
||||||
applyCeleryTaskFilter?: boolean;
|
applyCeleryTaskFilter?: boolean;
|
||||||
|
customErrorMessage?: string;
|
||||||
|
start?: number;
|
||||||
|
end?: number;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
@@ -116,6 +122,9 @@ function CeleryTaskGraph({
|
|||||||
openTracesButton={openTracesButton}
|
openTracesButton={openTracesButton}
|
||||||
onOpenTraceBtnClick={onOpenTraceBtnClick}
|
onOpenTraceBtnClick={onOpenTraceBtnClick}
|
||||||
version={ENTITY_VERSION_V4}
|
version={ENTITY_VERSION_V4}
|
||||||
|
customErrorMessage={customErrorMessage}
|
||||||
|
start={start}
|
||||||
|
end={end}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
@@ -129,6 +138,9 @@ CeleryTaskGraph.defaultProps = {
|
|||||||
openTracesButton: false,
|
openTracesButton: false,
|
||||||
onOpenTraceBtnClick: undefined,
|
onOpenTraceBtnClick: undefined,
|
||||||
applyCeleryTaskFilter: false,
|
applyCeleryTaskFilter: false,
|
||||||
|
customErrorMessage: undefined,
|
||||||
|
start: undefined,
|
||||||
|
end: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CeleryTaskGraph;
|
export default CeleryTaskGraph;
|
||||||
|
|||||||
@@ -123,11 +123,12 @@ export default function CeleryTaskGraphGrid({
|
|||||||
key={celeryActiveTasksData.id}
|
key={celeryActiveTasksData.id}
|
||||||
widgetData={celeryActiveTasksData}
|
widgetData={celeryActiveTasksData}
|
||||||
queryEnabled={queryEnabled}
|
queryEnabled={queryEnabled}
|
||||||
|
customErrorMessage="Enable Flower metrics to view this graph"
|
||||||
/>
|
/>
|
||||||
<Card className="celery-task-graph-worker-count">
|
<Card className="celery-task-graph-worker-count">
|
||||||
<div className="worker-count-header">
|
<div className="worker-count-header">
|
||||||
<Typography.Text className="worker-count-header-text">
|
<Typography.Text className="worker-count-header-text">
|
||||||
Worker Count
|
Worker Online
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</div>
|
</div>
|
||||||
<div className="worker-count-text-container">
|
<div className="worker-count-text-container">
|
||||||
@@ -173,7 +174,7 @@ export default function CeleryTaskGraphGrid({
|
|||||||
{!collapsedSections.traceBasedGraphs && (
|
{!collapsedSections.traceBasedGraphs && (
|
||||||
<>
|
<>
|
||||||
<CeleryTaskBar queryEnabled={queryEnabled} onClick={onClick} />
|
<CeleryTaskBar queryEnabled={queryEnabled} onClick={onClick} />
|
||||||
<CeleryTaskLatencyGraph onClick={onClick} queryEnabled={queryEnabled} />
|
<CeleryTaskLatencyGraph queryEnabled={queryEnabled} />
|
||||||
<div className="celery-task-graph-grid-bottom">
|
<div className="celery-task-graph-grid-bottom">
|
||||||
{bottomWidgetData.map((widgetData, index) => (
|
{bottomWidgetData.map((widgetData, index) => (
|
||||||
<CeleryTaskGraph
|
<CeleryTaskGraph
|
||||||
|
|||||||
@@ -42,21 +42,7 @@ export const celeryAllStateWidgetData = (
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
expression: 'A',
|
expression: 'A',
|
||||||
filters: {
|
filters: {
|
||||||
items: [
|
items: [],
|
||||||
{
|
|
||||||
id: uuidv4(),
|
|
||||||
key: {
|
|
||||||
dataType: DataTypes.String,
|
|
||||||
id: 'celery.task_name--string--tag--false',
|
|
||||||
isColumn: false,
|
|
||||||
isJSON: false,
|
|
||||||
key: 'celery.task_name',
|
|
||||||
type: 'tag',
|
|
||||||
},
|
|
||||||
op: '=',
|
|
||||||
value: 'tasks.tasks.divide',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
op: 'AND',
|
op: 'AND',
|
||||||
},
|
},
|
||||||
functions: [],
|
functions: [],
|
||||||
@@ -113,7 +99,7 @@ export const celeryRetryStateWidgetData = (
|
|||||||
filters: {
|
filters: {
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: '6d97eed3',
|
id: uuidv4(),
|
||||||
key: {
|
key: {
|
||||||
dataType: DataTypes.String,
|
dataType: DataTypes.String,
|
||||||
id: 'celery.state--string--tag--false',
|
id: 'celery.state--string--tag--false',
|
||||||
@@ -179,7 +165,7 @@ export const celeryFailedStateWidgetData = (
|
|||||||
filters: {
|
filters: {
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: '5983eae2',
|
id: uuidv4(),
|
||||||
key: {
|
key: {
|
||||||
dataType: DataTypes.String,
|
dataType: DataTypes.String,
|
||||||
id: 'celery.state--string--tag--false',
|
id: 'celery.state--string--tag--false',
|
||||||
@@ -245,7 +231,7 @@ export const celerySuccessStateWidgetData = (
|
|||||||
filters: {
|
filters: {
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: '000c5a93',
|
id: uuidv4(),
|
||||||
key: {
|
key: {
|
||||||
dataType: DataTypes.String,
|
dataType: DataTypes.String,
|
||||||
id: 'celery.state--string--tag--false',
|
id: 'celery.state--string--tag--false',
|
||||||
@@ -602,7 +588,7 @@ export const celeryTaskLatencyWidgetData = (
|
|||||||
reduceTo: 'avg',
|
reduceTo: 'avg',
|
||||||
spaceAggregation: 'sum',
|
spaceAggregation: 'sum',
|
||||||
stepInterval: getStepInterval(startTime, endTime),
|
stepInterval: getStepInterval(startTime, endTime),
|
||||||
timeAggregation: 'p99',
|
timeAggregation: type || 'p99',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
yAxisUnit: 'ns',
|
yAxisUnit: 'ns',
|
||||||
@@ -686,7 +672,7 @@ export const celeryRetryTasksTableWidgetData = getWidgetQueryBuilder(
|
|||||||
filters: {
|
filters: {
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: '9e09c9ed',
|
id: uuidv4(),
|
||||||
key: {
|
key: {
|
||||||
dataType: DataTypes.String,
|
dataType: DataTypes.String,
|
||||||
id: 'celery.state--string--tag--false',
|
id: 'celery.state--string--tag--false',
|
||||||
@@ -755,7 +741,7 @@ export const celeryFailedTasksTableWidgetData = getWidgetQueryBuilder(
|
|||||||
filters: {
|
filters: {
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: '2330f906',
|
id: uuidv4(),
|
||||||
key: {
|
key: {
|
||||||
dataType: DataTypes.String,
|
dataType: DataTypes.String,
|
||||||
id: 'celery.state--string--tag--false',
|
id: 'celery.state--string--tag--false',
|
||||||
@@ -822,7 +808,7 @@ export const celerySuccessTasksTableWidgetData = getWidgetQueryBuilder(
|
|||||||
filters: {
|
filters: {
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: 'ec3df7b7',
|
id: uuidv4(),
|
||||||
key: {
|
key: {
|
||||||
dataType: DataTypes.String,
|
dataType: DataTypes.String,
|
||||||
id: 'celery.state--string--tag--false',
|
id: 'celery.state--string--tag--false',
|
||||||
@@ -945,33 +931,19 @@ export const celeryAllStateCountWidgetData = getWidgetQueryBuilder(
|
|||||||
queryData: [
|
queryData: [
|
||||||
{
|
{
|
||||||
aggregateAttribute: {
|
aggregateAttribute: {
|
||||||
dataType: DataTypes.EMPTY,
|
dataType: DataTypes.String,
|
||||||
id: '------false',
|
id: 'span_id--string----true',
|
||||||
isColumn: false,
|
isColumn: true,
|
||||||
isJSON: false,
|
isJSON: false,
|
||||||
key: '',
|
key: 'span_id',
|
||||||
type: '',
|
type: '',
|
||||||
},
|
},
|
||||||
aggregateOperator: 'count',
|
aggregateOperator: 'count_distinct',
|
||||||
dataSource: DataSource.TRACES,
|
dataSource: DataSource.TRACES,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
expression: 'A',
|
expression: 'A',
|
||||||
filters: {
|
filters: {
|
||||||
items: [
|
items: [],
|
||||||
{
|
|
||||||
id: uuidv4(),
|
|
||||||
key: {
|
|
||||||
dataType: DataTypes.String,
|
|
||||||
id: 'celery.task_name--string--tag--false',
|
|
||||||
isColumn: false,
|
|
||||||
isJSON: false,
|
|
||||||
key: 'celery.task_name',
|
|
||||||
type: 'tag',
|
|
||||||
},
|
|
||||||
op: '=',
|
|
||||||
value: 'tasks.tasks.divide',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
op: 'AND',
|
op: 'AND',
|
||||||
},
|
},
|
||||||
functions: [],
|
functions: [],
|
||||||
@@ -981,10 +953,10 @@ export const celeryAllStateCountWidgetData = getWidgetQueryBuilder(
|
|||||||
limit: null,
|
limit: null,
|
||||||
orderBy: [],
|
orderBy: [],
|
||||||
queryName: 'A',
|
queryName: 'A',
|
||||||
reduceTo: 'avg',
|
reduceTo: 'last',
|
||||||
spaceAggregation: 'sum',
|
spaceAggregation: 'sum',
|
||||||
stepInterval: 60,
|
stepInterval: 60,
|
||||||
timeAggregation: 'rate',
|
timeAggregation: 'count_distinct',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
@@ -998,14 +970,14 @@ export const celerySuccessStateCountWidgetData = getWidgetQueryBuilder(
|
|||||||
queryData: [
|
queryData: [
|
||||||
{
|
{
|
||||||
aggregateAttribute: {
|
aggregateAttribute: {
|
||||||
dataType: DataTypes.EMPTY,
|
dataType: DataTypes.String,
|
||||||
id: '------false',
|
id: 'span_id--string----true',
|
||||||
isColumn: false,
|
isColumn: true,
|
||||||
isJSON: false,
|
isJSON: false,
|
||||||
key: '',
|
key: 'span_id',
|
||||||
type: '',
|
type: '',
|
||||||
},
|
},
|
||||||
aggregateOperator: 'count',
|
aggregateOperator: 'count_distinct',
|
||||||
dataSource: DataSource.TRACES,
|
dataSource: DataSource.TRACES,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
expression: 'A',
|
expression: 'A',
|
||||||
@@ -1034,10 +1006,10 @@ export const celerySuccessStateCountWidgetData = getWidgetQueryBuilder(
|
|||||||
limit: null,
|
limit: null,
|
||||||
orderBy: [],
|
orderBy: [],
|
||||||
queryName: 'A',
|
queryName: 'A',
|
||||||
reduceTo: 'avg',
|
reduceTo: 'last',
|
||||||
spaceAggregation: 'sum',
|
spaceAggregation: 'sum',
|
||||||
stepInterval: 60,
|
stepInterval: 60,
|
||||||
timeAggregation: 'rate',
|
timeAggregation: 'count_distinct',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
@@ -1051,14 +1023,14 @@ export const celeryFailedStateCountWidgetData = getWidgetQueryBuilder(
|
|||||||
queryData: [
|
queryData: [
|
||||||
{
|
{
|
||||||
aggregateAttribute: {
|
aggregateAttribute: {
|
||||||
dataType: DataTypes.EMPTY,
|
dataType: DataTypes.String,
|
||||||
id: '------false',
|
id: 'span_id--string----true',
|
||||||
isColumn: false,
|
isColumn: true,
|
||||||
isJSON: false,
|
isJSON: false,
|
||||||
key: '',
|
key: 'span_id',
|
||||||
type: '',
|
type: '',
|
||||||
},
|
},
|
||||||
aggregateOperator: 'count',
|
aggregateOperator: 'count_distinct',
|
||||||
dataSource: DataSource.TRACES,
|
dataSource: DataSource.TRACES,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
expression: 'A',
|
expression: 'A',
|
||||||
@@ -1087,10 +1059,10 @@ export const celeryFailedStateCountWidgetData = getWidgetQueryBuilder(
|
|||||||
limit: null,
|
limit: null,
|
||||||
orderBy: [],
|
orderBy: [],
|
||||||
queryName: 'A',
|
queryName: 'A',
|
||||||
reduceTo: 'avg',
|
reduceTo: 'last',
|
||||||
spaceAggregation: 'sum',
|
spaceAggregation: 'sum',
|
||||||
stepInterval: 60,
|
stepInterval: 60,
|
||||||
timeAggregation: 'rate',
|
timeAggregation: 'count_distinct',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
@@ -1104,13 +1076,13 @@ export const celeryRetryStateCountWidgetData = getWidgetQueryBuilder(
|
|||||||
queryData: [
|
queryData: [
|
||||||
{
|
{
|
||||||
aggregateAttribute: {
|
aggregateAttribute: {
|
||||||
dataType: DataTypes.EMPTY,
|
dataType: DataTypes.String,
|
||||||
id: '------false',
|
id: 'span_id--string----true',
|
||||||
isColumn: false,
|
isColumn: true,
|
||||||
key: '',
|
key: 'span_id',
|
||||||
type: '',
|
type: '',
|
||||||
},
|
},
|
||||||
aggregateOperator: 'count',
|
aggregateOperator: 'count_distinct',
|
||||||
dataSource: DataSource.TRACES,
|
dataSource: DataSource.TRACES,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
expression: 'A',
|
expression: 'A',
|
||||||
@@ -1139,10 +1111,10 @@ export const celeryRetryStateCountWidgetData = getWidgetQueryBuilder(
|
|||||||
limit: null,
|
limit: null,
|
||||||
orderBy: [],
|
orderBy: [],
|
||||||
queryName: 'A',
|
queryName: 'A',
|
||||||
reduceTo: 'avg',
|
reduceTo: 'last',
|
||||||
spaceAggregation: 'sum',
|
spaceAggregation: 'sum',
|
||||||
stepInterval: 60,
|
stepInterval: 60,
|
||||||
timeAggregation: 'rate',
|
timeAggregation: 'count_distinct',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -6,8 +6,11 @@ import { PANEL_TYPES } from 'constants/queryBuilder';
|
|||||||
import { ViewMenuAction } from 'container/GridCardLayout/config';
|
import { ViewMenuAction } from 'container/GridCardLayout/config';
|
||||||
import GridCard from 'container/GridCardLayout/GridCard';
|
import GridCard from 'container/GridCardLayout/GridCard';
|
||||||
import { Card } from 'container/GridCardLayout/styles';
|
import { Card } from 'container/GridCardLayout/styles';
|
||||||
|
import { Button } from 'container/MetricsApplication/Tabs/styles';
|
||||||
|
import { onGraphClickHandler } from 'container/MetricsApplication/Tabs/util';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import useUrlQuery from 'hooks/useUrlQuery';
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
|
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
|
||||||
import { getStartAndEndTimesInMilliseconds } from 'pages/MessagingQueues/MessagingQueuesUtils';
|
import { getStartAndEndTimesInMilliseconds } from 'pages/MessagingQueues/MessagingQueuesUtils';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
@@ -16,15 +19,13 @@ import { UpdateTimeInterval } from 'store/actions';
|
|||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
import { CaptureDataProps } from '../CeleryTaskDetail/CeleryTaskDetail';
|
|
||||||
import {
|
import {
|
||||||
applyCeleryFilterOnWidgetData,
|
applyCeleryFilterOnWidgetData,
|
||||||
|
createFiltersFromData,
|
||||||
getFiltersFromQueryParams,
|
getFiltersFromQueryParams,
|
||||||
} from '../CeleryUtils';
|
} from '../CeleryUtils';
|
||||||
import {
|
import { useNavigateToTraces } from '../useNavigateToTraces';
|
||||||
celeryTaskLatencyWidgetData,
|
import { celeryTaskLatencyWidgetData } from './CeleryTaskGraphUtils';
|
||||||
celeryTimeSeriesTablesWidgetData,
|
|
||||||
} from './CeleryTaskGraphUtils';
|
|
||||||
|
|
||||||
interface TabData {
|
interface TabData {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -38,10 +39,8 @@ export enum CeleryTaskGraphState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function CeleryTaskLatencyGraph({
|
function CeleryTaskLatencyGraph({
|
||||||
onClick,
|
|
||||||
queryEnabled,
|
queryEnabled,
|
||||||
}: {
|
}: {
|
||||||
onClick: (task: CaptureDataProps) => void;
|
|
||||||
queryEnabled: boolean;
|
queryEnabled: boolean;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
@@ -106,31 +105,51 @@ function CeleryTaskLatencyGraph({
|
|||||||
[celeryTaskLatencyData, selectedFilters],
|
[celeryTaskLatencyData, selectedFilters],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onGraphClick = (
|
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
|
||||||
xValue: number,
|
const [entityData, setEntityData] = useState<{
|
||||||
_yValue: number,
|
entity: string;
|
||||||
_mouseX: number,
|
value: string;
|
||||||
_mouseY: number,
|
}>();
|
||||||
data?: {
|
|
||||||
[key: string]: string;
|
const handleSetTimeStamp = useCallback((selectTime: number) => {
|
||||||
|
setSelectedTimeStamp(selectTime);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onGraphClick = useCallback(
|
||||||
|
(type: string): OnClickPluginOpts['onClick'] => (
|
||||||
|
xValue,
|
||||||
|
yValue,
|
||||||
|
mouseX,
|
||||||
|
mouseY,
|
||||||
|
data,
|
||||||
|
): Promise<void> => {
|
||||||
|
const [firstDataPoint] = Object.entries(data || {});
|
||||||
|
const [entity, value] = firstDataPoint;
|
||||||
|
setEntityData({
|
||||||
|
entity,
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
|
||||||
|
return onGraphClickHandler(handleSetTimeStamp)(
|
||||||
|
xValue,
|
||||||
|
yValue,
|
||||||
|
mouseX,
|
||||||
|
mouseY,
|
||||||
|
type,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
): void => {
|
[handleSetTimeStamp],
|
||||||
const { start, end } = getStartAndEndTimesInMilliseconds(xValue);
|
);
|
||||||
|
|
||||||
// Extract entity and value from data
|
const navigateToTraces = useNavigateToTraces();
|
||||||
const [firstDataPoint] = Object.entries(data || {});
|
|
||||||
const [entity, value] = (firstDataPoint || ([] as unknown)) as [
|
|
||||||
string,
|
|
||||||
string,
|
|
||||||
];
|
|
||||||
|
|
||||||
onClick?.({
|
const goToTraces = useCallback(() => {
|
||||||
entity,
|
const { start, end } = getStartAndEndTimesInMilliseconds(selectedTimeStamp);
|
||||||
value,
|
const filters = createFiltersFromData({
|
||||||
timeRange: [start, end],
|
[entityData?.entity as string]: entityData?.value,
|
||||||
widgetData: celeryTimeSeriesTablesWidgetData(entity, value, 'Task Latency'),
|
|
||||||
});
|
});
|
||||||
};
|
navigateToTraces(filters, start, end, true);
|
||||||
|
}, [entityData, navigateToTraces, selectedTimeStamp]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
@@ -161,32 +180,62 @@ function CeleryTaskLatencyGraph({
|
|||||||
</Row>
|
</Row>
|
||||||
<div className="celery-task-graph-grid-content">
|
<div className="celery-task-graph-grid-content">
|
||||||
{graphState === CeleryTaskGraphState.P99 && (
|
{graphState === CeleryTaskGraphState.P99 && (
|
||||||
<GridCard
|
<>
|
||||||
widget={updatedWidgetData}
|
<Button
|
||||||
headerMenuList={[...ViewMenuAction]}
|
type="default"
|
||||||
onDragSelect={onDragSelect}
|
size="small"
|
||||||
onClickHandler={onGraphClick}
|
id="Celery_p99_latency_button"
|
||||||
isQueryEnabled={queryEnabled}
|
onClick={goToTraces}
|
||||||
/>
|
>
|
||||||
|
View Traces
|
||||||
|
</Button>
|
||||||
|
<GridCard
|
||||||
|
widget={updatedWidgetData}
|
||||||
|
headerMenuList={[...ViewMenuAction]}
|
||||||
|
onDragSelect={onDragSelect}
|
||||||
|
onClickHandler={onGraphClick('Celery_p99_latency')}
|
||||||
|
isQueryEnabled={queryEnabled}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{graphState === CeleryTaskGraphState.P95 && (
|
{graphState === CeleryTaskGraphState.P95 && (
|
||||||
<GridCard
|
<>
|
||||||
widget={updatedWidgetData}
|
<Button
|
||||||
headerMenuList={[...ViewMenuAction]}
|
type="default"
|
||||||
onDragSelect={onDragSelect}
|
size="small"
|
||||||
onClickHandler={onGraphClick}
|
id="Celery_p95_latency_button"
|
||||||
isQueryEnabled={queryEnabled}
|
onClick={goToTraces}
|
||||||
/>
|
>
|
||||||
|
View Traces
|
||||||
|
</Button>
|
||||||
|
<GridCard
|
||||||
|
widget={updatedWidgetData}
|
||||||
|
headerMenuList={[...ViewMenuAction]}
|
||||||
|
onDragSelect={onDragSelect}
|
||||||
|
onClickHandler={onGraphClick('Celery_p95_latency')}
|
||||||
|
isQueryEnabled={queryEnabled}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{graphState === CeleryTaskGraphState.P90 && (
|
{graphState === CeleryTaskGraphState.P90 && (
|
||||||
<GridCard
|
<>
|
||||||
widget={updatedWidgetData}
|
<Button
|
||||||
headerMenuList={[...ViewMenuAction]}
|
type="default"
|
||||||
onDragSelect={onDragSelect}
|
size="small"
|
||||||
onClickHandler={onGraphClick}
|
id="Celery_p90_latency_button"
|
||||||
isQueryEnabled={queryEnabled}
|
onClick={goToTraces}
|
||||||
/>
|
>
|
||||||
|
View Traces
|
||||||
|
</Button>
|
||||||
|
<GridCard
|
||||||
|
widget={updatedWidgetData}
|
||||||
|
headerMenuList={[...ViewMenuAction]}
|
||||||
|
onDragSelect={onDragSelect}
|
||||||
|
onClickHandler={onGraphClick('Celery_p90_latency')}
|
||||||
|
isQueryEnabled={queryEnabled}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -2,8 +2,14 @@
|
|||||||
import './CeleryTaskGraph.style.scss';
|
import './CeleryTaskGraph.style.scss';
|
||||||
|
|
||||||
import { Col, Row } from 'antd';
|
import { Col, Row } from 'antd';
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
import { QueryParams } from 'constants/query';
|
||||||
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
|
import { Dispatch, SetStateAction, useMemo } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
applyCeleryFilterOnWidgetData,
|
||||||
|
getFiltersFromQueryParams,
|
||||||
|
} from '../CeleryUtils';
|
||||||
import {
|
import {
|
||||||
celeryAllStateCountWidgetData,
|
celeryAllStateCountWidgetData,
|
||||||
celeryFailedStateCountWidgetData,
|
celeryFailedStateCountWidgetData,
|
||||||
@@ -42,16 +48,29 @@ function CeleryTaskStateGraphConfig({
|
|||||||
setBarState(key as CeleryTaskState);
|
setBarState(key as CeleryTaskState);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { values, isLoading, isError } = useGetValueFromWidget(
|
const urlQuery = useUrlQuery();
|
||||||
[
|
|
||||||
celeryAllStateCountWidgetData,
|
const selectedFilters = useMemo(
|
||||||
celeryFailedStateCountWidgetData,
|
() =>
|
||||||
celeryRetryStateCountWidgetData,
|
getFiltersFromQueryParams(
|
||||||
celerySuccessStateCountWidgetData,
|
QueryParams.taskName,
|
||||||
],
|
urlQuery,
|
||||||
['celery-task-states'],
|
'celery.task_name',
|
||||||
|
),
|
||||||
|
[urlQuery],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const widgetData = [
|
||||||
|
celeryAllStateCountWidgetData,
|
||||||
|
celeryFailedStateCountWidgetData,
|
||||||
|
celeryRetryStateCountWidgetData,
|
||||||
|
celerySuccessStateCountWidgetData,
|
||||||
|
].map((data) => applyCeleryFilterOnWidgetData(selectedFilters || [], data));
|
||||||
|
|
||||||
|
const { values, isLoading, isError } = useGetValueFromWidget(widgetData, [
|
||||||
|
'celery-task-states',
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row className="celery-task-states">
|
<Row className="celery-task-states">
|
||||||
{tabs.map((tab, index) => (
|
{tabs.map((tab, index) => (
|
||||||
@@ -66,7 +85,13 @@ function CeleryTaskStateGraphConfig({
|
|||||||
<div className="celery-task-states__label-wrapper">
|
<div className="celery-task-states__label-wrapper">
|
||||||
<div className="celery-task-states__label">{tab.label}</div>
|
<div className="celery-task-states__label">{tab.label}</div>
|
||||||
<div className="celery-task-states__value">
|
<div className="celery-task-states__value">
|
||||||
{isLoading ? '-' : isError ? '-' : values[index]}
|
{isLoading
|
||||||
|
? '-'
|
||||||
|
: isError
|
||||||
|
? '-'
|
||||||
|
: Number.isNaN(values[index])
|
||||||
|
? '-'
|
||||||
|
: Math.round(Number(values[index]))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{tab.key === barState && <div className="celery-task-states__indicator" />}
|
{tab.key === barState && <div className="celery-task-states__indicator" />}
|
||||||
|
|||||||
@@ -90,3 +90,40 @@ export const paths = (
|
|||||||
|
|
||||||
return renderer(u, seriesIdx, idx0, idx1, extendGap, buildClip);
|
return renderer(u, seriesIdx, idx0, idx1, extendGap, buildClip);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const createFiltersFromData = (
|
||||||
|
data: Record<string, any>,
|
||||||
|
): Array<{
|
||||||
|
id: string;
|
||||||
|
key: {
|
||||||
|
key: string;
|
||||||
|
dataType: DataTypes;
|
||||||
|
type: string;
|
||||||
|
isColumn: boolean;
|
||||||
|
isJSON: boolean;
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
op: string;
|
||||||
|
value: string;
|
||||||
|
}> => {
|
||||||
|
const excludeKeys = ['A', 'A_without_unit'];
|
||||||
|
|
||||||
|
return (
|
||||||
|
Object.entries(data)
|
||||||
|
.filter(([key]) => !excludeKeys.includes(key))
|
||||||
|
// eslint-disable-next-line sonarjs/no-identical-functions
|
||||||
|
.map(([key, value]) => ({
|
||||||
|
id: uuidv4(),
|
||||||
|
key: {
|
||||||
|
key,
|
||||||
|
dataType: DataTypes.String,
|
||||||
|
type: 'tag',
|
||||||
|
isColumn: false,
|
||||||
|
isJSON: false,
|
||||||
|
id: `${key}--string--tag--false`,
|
||||||
|
},
|
||||||
|
op: '=',
|
||||||
|
value: value.toString(),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export function useNavigateToTraces(): (
|
|||||||
filters: TagFilterItem[],
|
filters: TagFilterItem[],
|
||||||
startTime?: number,
|
startTime?: number,
|
||||||
endTime?: number,
|
endTime?: number,
|
||||||
|
sameTab?: boolean,
|
||||||
) => void {
|
) => void {
|
||||||
const { currentQuery } = useQueryBuilder();
|
const { currentQuery } = useQueryBuilder();
|
||||||
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
||||||
@@ -38,7 +39,12 @@ export function useNavigateToTraces(): (
|
|||||||
);
|
);
|
||||||
|
|
||||||
return useCallback(
|
return useCallback(
|
||||||
(filters: TagFilterItem[], startTime?: number, endTime?: number): void => {
|
(
|
||||||
|
filters: TagFilterItem[],
|
||||||
|
startTime?: number,
|
||||||
|
endTime?: number,
|
||||||
|
sameTab?: boolean,
|
||||||
|
): void => {
|
||||||
const urlParams = new URLSearchParams();
|
const urlParams = new URLSearchParams();
|
||||||
if (startTime && endTime) {
|
if (startTime && endTime) {
|
||||||
urlParams.set(QueryParams.startTime, startTime.toString());
|
urlParams.set(QueryParams.startTime, startTime.toString());
|
||||||
@@ -58,7 +64,7 @@ export function useNavigateToTraces(): (
|
|||||||
QueryParams.compositeQuery
|
QueryParams.compositeQuery
|
||||||
}=${JSONCompositeQuery}`;
|
}=${JSONCompositeQuery}`;
|
||||||
|
|
||||||
window.open(newTraceExplorerPath, '_blank');
|
window.open(newTraceExplorerPath, sameTab ? '_self' : '_blank');
|
||||||
},
|
},
|
||||||
[minTime, maxTime, prepareQuery],
|
[minTime, maxTime, prepareQuery],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -133,7 +133,7 @@
|
|||||||
.go-to-docs {
|
.go-to-docs {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 64px;
|
gap: 44px;
|
||||||
&__container {
|
&__container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -23,4 +23,5 @@ export enum FeatureKeys {
|
|||||||
PREMIUM_SUPPORT = 'PREMIUM_SUPPORT',
|
PREMIUM_SUPPORT = 'PREMIUM_SUPPORT',
|
||||||
QUERY_BUILDER_SEARCH_V2 = 'QUERY_BUILDER_SEARCH_V2',
|
QUERY_BUILDER_SEARCH_V2 = 'QUERY_BUILDER_SEARCH_V2',
|
||||||
ANOMALY_DETECTION = 'ANOMALY_DETECTION',
|
ANOMALY_DETECTION = 'ANOMALY_DETECTION',
|
||||||
|
AWS_INTEGRATION = 'AWS_INTEGRATION',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,5 +41,6 @@ export const REACT_QUERY_KEY = {
|
|||||||
AWS_UPDATE_ACCOUNT_CONFIG: 'AWS_UPDATE_ACCOUNT_CONFIG',
|
AWS_UPDATE_ACCOUNT_CONFIG: 'AWS_UPDATE_ACCOUNT_CONFIG',
|
||||||
AWS_UPDATE_SERVICE_CONFIG: 'AWS_UPDATE_SERVICE_CONFIG',
|
AWS_UPDATE_SERVICE_CONFIG: 'AWS_UPDATE_SERVICE_CONFIG',
|
||||||
AWS_GENERATE_CONNECTION_URL: 'AWS_GENERATE_CONNECTION_URL',
|
AWS_GENERATE_CONNECTION_URL: 'AWS_GENERATE_CONNECTION_URL',
|
||||||
|
AWS_GET_CONNECTION_PARAMS: 'AWS_GET_CONNECTION_PARAMS',
|
||||||
GET_ATTRIBUTE_VALUES: 'GET_ATTRIBUTE_VALUES',
|
GET_ATTRIBUTE_VALUES: 'GET_ATTRIBUTE_VALUES',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -65,6 +65,9 @@ const ROUTES = {
|
|||||||
INFRASTRUCTURE_MONITORING_KUBERNETES: '/infrastructure-monitoring/kubernetes',
|
INFRASTRUCTURE_MONITORING_KUBERNETES: '/infrastructure-monitoring/kubernetes',
|
||||||
MESSAGING_QUEUES_CELERY_TASK: '/messaging-queues/celery-task',
|
MESSAGING_QUEUES_CELERY_TASK: '/messaging-queues/celery-task',
|
||||||
MESSAGING_QUEUES_OVERVIEW: '/messaging-queues/overview',
|
MESSAGING_QUEUES_OVERVIEW: '/messaging-queues/overview',
|
||||||
|
METRICS_EXPLORER: '/metrics-explorer/summary',
|
||||||
|
METRICS_EXPLORER_EXPLORER: '/metrics-explorer/explorer',
|
||||||
|
METRICS_EXPLORER_VIEWS: '/metrics-explorer/views',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export default ROUTES;
|
export default ROUTES;
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
if (
|
if (
|
||||||
!isFetchingActiveLicenseV3 &&
|
!isFetchingActiveLicenseV3 &&
|
||||||
!isNull(activeLicenseV3) &&
|
!isNull(activeLicenseV3) &&
|
||||||
activeLicenseV3?.event_queue?.event === LicenseEvent.FAILED_PAYMENT
|
activeLicenseV3?.event_queue?.event === LicenseEvent.DEFAULT
|
||||||
) {
|
) {
|
||||||
setShowPaymentFailedWarning(true);
|
setShowPaymentFailedWarning(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ function Header(): JSX.Element {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: (
|
title: (
|
||||||
<div className="cloud-header__breadcrumb-title">AWS web services</div>
|
<div className="cloud-header__breadcrumb-title">
|
||||||
|
Amazon Web Services
|
||||||
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ function HeroSection(): JSX.Element {
|
|||||||
<img src="/Logos/aws-dark.svg" alt="aws-logo" />
|
<img src="/Logos/aws-dark.svg" alt="aws-logo" />
|
||||||
</div>
|
</div>
|
||||||
<div className="hero-section__details">
|
<div className="hero-section__details">
|
||||||
<div className="title">AWS Web Services</div>
|
<div className="title">Amazon Web Services</div>
|
||||||
<div className="description">
|
<div className="description">
|
||||||
One-click setup for AWS monitoring with SigNoz
|
One-click setup for AWS monitoring with SigNoz
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,41 +1,56 @@
|
|||||||
.hero-section__actions {
|
.hero-section {
|
||||||
margin-top: 12px;
|
&__actions {
|
||||||
|
margin-top: 12px;
|
||||||
|
|
||||||
&-with-account {
|
&-with-account {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
.hero-section__action-buttons {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-section__action-button {
|
|
||||||
font-family: 'Inter';
|
|
||||||
border-radius: 2px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 16px;
|
|
||||||
padding: 8px 17px;
|
|
||||||
|
|
||||||
&.primary {
|
|
||||||
background: var(--bg-robin-500);
|
|
||||||
border: none;
|
|
||||||
color: var(--bg-vanilla-100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.secondary {
|
&__input-skeleton {
|
||||||
|
width: 300px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__new-account-button-skeleton {
|
||||||
|
width: 180px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__account-settings-button-skeleton {
|
||||||
|
width: 140px;
|
||||||
|
}
|
||||||
|
&__action-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border: 1px solid var(--bg-ink-300);
|
gap: 8px;
|
||||||
color: var(--bg-vanilla-100);
|
}
|
||||||
|
&__action-button {
|
||||||
|
font-family: 'Inter';
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
background: var(--bg-slate-400);
|
cursor: pointer;
|
||||||
box-shadow: none;
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 16px;
|
||||||
|
padding: 8px 17px;
|
||||||
|
|
||||||
|
&.primary {
|
||||||
|
background: var(--bg-robin-500);
|
||||||
|
border: none;
|
||||||
|
color: var(--bg-vanilla-100);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.secondary {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid var(--bg-ink-300);
|
||||||
|
color: var(--bg-vanilla-100);
|
||||||
|
border-radius: 2px;
|
||||||
|
background: var(--bg-slate-400);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import './AccountActions.style.scss';
|
import './AccountActions.style.scss';
|
||||||
|
|
||||||
import { Color } from '@signozhq/design-tokens';
|
import { Color } from '@signozhq/design-tokens';
|
||||||
import { Button, Select } from 'antd';
|
import { Button, Select, Skeleton } from 'antd';
|
||||||
import { SelectProps } from 'antd/lib';
|
import { SelectProps } from 'antd/lib';
|
||||||
import { useAwsAccounts } from 'hooks/integrations/aws/useAwsAccounts';
|
import { useAwsAccounts } from 'hooks/integrations/aws/useAwsAccounts';
|
||||||
import useUrlQuery from 'hooks/useUrlQuery';
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
@@ -53,15 +53,100 @@ const getAccountById = (
|
|||||||
): CloudAccount | null =>
|
): CloudAccount | null =>
|
||||||
accounts.find((account) => account.cloud_account_id === accountId) || null;
|
accounts.find((account) => account.cloud_account_id === accountId) || null;
|
||||||
|
|
||||||
|
function AccountActionsRenderer({
|
||||||
|
accounts,
|
||||||
|
isLoading,
|
||||||
|
activeAccount,
|
||||||
|
selectOptions,
|
||||||
|
onAccountChange,
|
||||||
|
onIntegrationModalOpen,
|
||||||
|
onAccountSettingsModalOpen,
|
||||||
|
}: {
|
||||||
|
accounts: CloudAccount[] | undefined;
|
||||||
|
isLoading: boolean;
|
||||||
|
activeAccount: CloudAccount | null;
|
||||||
|
selectOptions: SelectProps['options'];
|
||||||
|
onAccountChange: (value: string) => void;
|
||||||
|
onIntegrationModalOpen: () => void;
|
||||||
|
onAccountSettingsModalOpen: () => void;
|
||||||
|
}): JSX.Element {
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="hero-section__actions-with-account">
|
||||||
|
<Skeleton.Input
|
||||||
|
active
|
||||||
|
size="large"
|
||||||
|
block
|
||||||
|
className="hero-section__input-skeleton"
|
||||||
|
/>
|
||||||
|
<div className="hero-section__action-buttons">
|
||||||
|
<Skeleton.Button
|
||||||
|
active
|
||||||
|
size="large"
|
||||||
|
className="hero-section__new-account-button-skeleton"
|
||||||
|
/>
|
||||||
|
<Skeleton.Button
|
||||||
|
active
|
||||||
|
size="large"
|
||||||
|
className="hero-section__account-settings-button-skeleton"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (accounts?.length) {
|
||||||
|
return (
|
||||||
|
<div className="hero-section__actions-with-account">
|
||||||
|
<Select
|
||||||
|
value={`Account: ${activeAccount?.cloud_account_id}`}
|
||||||
|
options={selectOptions}
|
||||||
|
rootClassName="cloud-account-selector"
|
||||||
|
placeholder="Select AWS Account"
|
||||||
|
suffixIcon={<ChevronDown size={16} color={Color.BG_VANILLA_400} />}
|
||||||
|
optionRender={(option): JSX.Element =>
|
||||||
|
renderOption(option, activeAccount?.cloud_account_id)
|
||||||
|
}
|
||||||
|
onChange={onAccountChange}
|
||||||
|
/>
|
||||||
|
<div className="hero-section__action-buttons">
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
className="hero-section__action-button primary"
|
||||||
|
onClick={onIntegrationModalOpen}
|
||||||
|
>
|
||||||
|
Add New AWS Account
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="default"
|
||||||
|
className="hero-section__action-button secondary"
|
||||||
|
onClick={onAccountSettingsModalOpen}
|
||||||
|
>
|
||||||
|
Account Settings
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
className="hero-section__action-button primary"
|
||||||
|
onClick={onIntegrationModalOpen}
|
||||||
|
>
|
||||||
|
Integrate Now
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function AccountActions(): JSX.Element {
|
function AccountActions(): JSX.Element {
|
||||||
const urlQuery = useUrlQuery();
|
const urlQuery = useUrlQuery();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { data: accounts } = useAwsAccounts();
|
const { data: accounts, isLoading } = useAwsAccounts();
|
||||||
|
|
||||||
const initialAccount = useMemo(
|
const initialAccount = useMemo(
|
||||||
() =>
|
() =>
|
||||||
accounts?.length
|
accounts?.length
|
||||||
? getAccountById(accounts, urlQuery.get('accountId') || '') || accounts[0]
|
? getAccountById(accounts, urlQuery.get('cloudAccountId') || '') ||
|
||||||
|
accounts[0]
|
||||||
: null,
|
: null,
|
||||||
[accounts, urlQuery],
|
[accounts, urlQuery],
|
||||||
);
|
);
|
||||||
@@ -74,7 +159,7 @@ function AccountActions(): JSX.Element {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialAccount !== null) {
|
if (initialAccount !== null) {
|
||||||
setActiveAccount(initialAccount);
|
setActiveAccount(initialAccount);
|
||||||
urlQuery.set('accountId', initialAccount.cloud_account_id);
|
urlQuery.set('cloudAccountId', initialAccount.cloud_account_id);
|
||||||
navigate({ search: urlQuery.toString() });
|
navigate({ search: urlQuery.toString() });
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -98,60 +183,35 @@ function AccountActions(): JSX.Element {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="hero-section__actions">
|
<div className="hero-section__actions">
|
||||||
{accounts?.length ? (
|
<AccountActionsRenderer
|
||||||
<div className="hero-section__actions-with-account">
|
accounts={accounts}
|
||||||
<Select
|
isLoading={isLoading}
|
||||||
value={`Account: ${activeAccount?.cloud_account_id}`}
|
activeAccount={activeAccount}
|
||||||
options={selectOptions}
|
selectOptions={selectOptions}
|
||||||
rootClassName="cloud-account-selector"
|
onAccountChange={(value): void => {
|
||||||
placeholder="Select AWS Account"
|
if (accounts) {
|
||||||
suffixIcon={<ChevronDown size={16} color={Color.BG_VANILLA_400} />}
|
setActiveAccount(getAccountById(accounts, value));
|
||||||
optionRender={(option): JSX.Element =>
|
urlQuery.set('cloudAccountId', value);
|
||||||
renderOption(option, activeAccount?.cloud_account_id)
|
navigate({ search: urlQuery.toString() });
|
||||||
}
|
}
|
||||||
onChange={(value): void => {
|
}}
|
||||||
setActiveAccount(getAccountById(accounts, value));
|
onIntegrationModalOpen={(): void => setIsIntegrationModalOpen(true)}
|
||||||
urlQuery.set('accountId', value);
|
onAccountSettingsModalOpen={(): void => setIsAccountSettingsModalOpen(true)}
|
||||||
navigate({ search: urlQuery.toString() });
|
/>
|
||||||
}}
|
|
||||||
/>
|
{isIntegrationModalOpen && (
|
||||||
<div className="hero-section__action-buttons">
|
<CloudAccountSetupModal
|
||||||
<Button
|
onClose={(): void => setIsIntegrationModalOpen(false)}
|
||||||
type="primary"
|
/>
|
||||||
className="hero-section__action-button primary"
|
|
||||||
onClick={(): void => setIsIntegrationModalOpen(true)}
|
|
||||||
>
|
|
||||||
Add New AWS Account
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type="default"
|
|
||||||
className="hero-section__action-button secondary"
|
|
||||||
onClick={(): void => setIsAccountSettingsModalOpen(true)}
|
|
||||||
>
|
|
||||||
Account Settings
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Button
|
|
||||||
className="hero-section__action-button primary"
|
|
||||||
onClick={(): void => setIsIntegrationModalOpen(true)}
|
|
||||||
>
|
|
||||||
Integrate Now
|
|
||||||
</Button>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<CloudAccountSetupModal
|
{isAccountSettingsModalOpen && (
|
||||||
isOpen={isIntegrationModalOpen}
|
<AccountSettingsModal
|
||||||
onClose={(): void => setIsIntegrationModalOpen(false)}
|
onClose={(): void => setIsAccountSettingsModalOpen(false)}
|
||||||
/>
|
account={activeAccount as CloudAccount}
|
||||||
|
setActiveAccount={setActiveAccount}
|
||||||
<AccountSettingsModal
|
/>
|
||||||
isOpen={isAccountSettingsModalOpen}
|
)}
|
||||||
onClose={(): void => setIsAccountSettingsModalOpen(false)}
|
|
||||||
account={activeAccount as CloudAccount}
|
|
||||||
setActiveAccount={setActiveAccount}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,27 +2,27 @@ import './AccountSettingsModal.style.scss';
|
|||||||
|
|
||||||
import { Form, Select, Switch } from 'antd';
|
import { Form, Select, Switch } from 'antd';
|
||||||
import SignozModal from 'components/SignozModal/SignozModal';
|
import SignozModal from 'components/SignozModal/SignozModal';
|
||||||
|
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||||
import {
|
import {
|
||||||
getRegionPreviewText,
|
getRegionPreviewText,
|
||||||
useAccountSettingsModal,
|
useAccountSettingsModal,
|
||||||
} from 'hooks/integrations/aws/useAccountSettingsModal';
|
} from 'hooks/integrations/aws/useAccountSettingsModal';
|
||||||
import IntergrationsUninstallBar from 'pages/Integrations/IntegrationDetailPage/IntegrationsUninstallBar';
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
import { ConnectionStates } from 'pages/Integrations/IntegrationDetailPage/TestConnection';
|
import history from 'lib/history';
|
||||||
import { AWS_INTEGRATION } from 'pages/Integrations/IntegrationsList';
|
|
||||||
import { Dispatch, SetStateAction, useCallback } from 'react';
|
import { Dispatch, SetStateAction, useCallback } from 'react';
|
||||||
|
import { useQueryClient } from 'react-query';
|
||||||
|
|
||||||
import { CloudAccount } from '../../ServicesSection/types';
|
import { CloudAccount } from '../../ServicesSection/types';
|
||||||
import { RegionSelector } from './RegionSelector';
|
import { RegionSelector } from './RegionSelector';
|
||||||
|
import RemoveIntegrationAccount from './RemoveIntegrationAccount';
|
||||||
|
|
||||||
interface AccountSettingsModalProps {
|
interface AccountSettingsModalProps {
|
||||||
isOpen: boolean;
|
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
account: CloudAccount;
|
account: CloudAccount;
|
||||||
setActiveAccount: Dispatch<SetStateAction<CloudAccount | null>>;
|
setActiveAccount: Dispatch<SetStateAction<CloudAccount | null>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function AccountSettingsModal({
|
function AccountSettingsModal({
|
||||||
isOpen,
|
|
||||||
onClose,
|
onClose,
|
||||||
account,
|
account,
|
||||||
setActiveAccount,
|
setActiveAccount,
|
||||||
@@ -42,6 +42,16 @@ function AccountSettingsModal({
|
|||||||
handleClose,
|
handleClose,
|
||||||
} = useAccountSettingsModal({ onClose, account, setActiveAccount });
|
} = useAccountSettingsModal({ onClose, account, setActiveAccount });
|
||||||
|
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const urlQuery = useUrlQuery();
|
||||||
|
|
||||||
|
const handleRemoveIntegrationAccountSuccess = (): void => {
|
||||||
|
queryClient.invalidateQueries([REACT_QUERY_KEY.AWS_ACCOUNTS]);
|
||||||
|
urlQuery.delete('cloudAccountId');
|
||||||
|
handleClose();
|
||||||
|
history.replace({ search: urlQuery.toString() });
|
||||||
|
};
|
||||||
|
|
||||||
const renderRegionSelector = useCallback(() => {
|
const renderRegionSelector = useCallback(() => {
|
||||||
if (isRegionSelectOpen) {
|
if (isRegionSelectOpen) {
|
||||||
return (
|
return (
|
||||||
@@ -120,7 +130,7 @@ function AccountSettingsModal({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SignozModal
|
<SignozModal
|
||||||
open={isOpen}
|
open
|
||||||
title={modalTitle}
|
title={modalTitle}
|
||||||
onCancel={handleClose}
|
onCancel={handleClose}
|
||||||
onOk={handleSubmit}
|
onOk={handleSubmit}
|
||||||
@@ -164,12 +174,9 @@ function AccountSettingsModal({
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<div className="integration-detail-content">
|
<div className="integration-detail-content">
|
||||||
<IntergrationsUninstallBar
|
<RemoveIntegrationAccount
|
||||||
integrationTitle={AWS_INTEGRATION.title}
|
accountId={account?.id}
|
||||||
integrationId={AWS_INTEGRATION.id}
|
onRemoveIntegrationAccountSuccess={handleRemoveIntegrationAccountSuccess}
|
||||||
onUnInstallSuccess={handleClose}
|
|
||||||
removeIntegrationTitle="Remove"
|
|
||||||
connectionStatus={ConnectionStates.Connected}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,11 +2,9 @@ import './CloudAccountSetupModal.style.scss';
|
|||||||
|
|
||||||
import { Color } from '@signozhq/design-tokens';
|
import { Color } from '@signozhq/design-tokens';
|
||||||
import SignozModal from 'components/SignozModal/SignozModal';
|
import SignozModal from 'components/SignozModal/SignozModal';
|
||||||
import ROUTES from 'constants/routes';
|
|
||||||
import { useIntegrationModal } from 'hooks/integrations/aws/useIntegrationModal';
|
import { useIntegrationModal } from 'hooks/integrations/aws/useIntegrationModal';
|
||||||
import { SquareArrowOutUpRight } from 'lucide-react';
|
import { SquareArrowOutUpRight } from 'lucide-react';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom-v5-compat';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ActiveViewEnum,
|
ActiveViewEnum,
|
||||||
@@ -18,7 +16,6 @@ import { RegionSelector } from './RegionSelector';
|
|||||||
import { SuccessView } from './SuccessView';
|
import { SuccessView } from './SuccessView';
|
||||||
|
|
||||||
function CloudAccountSetupModal({
|
function CloudAccountSetupModal({
|
||||||
isOpen,
|
|
||||||
onClose,
|
onClose,
|
||||||
}: IntegrationModalProps): JSX.Element {
|
}: IntegrationModalProps): JSX.Element {
|
||||||
const {
|
const {
|
||||||
@@ -41,6 +38,8 @@ function CloudAccountSetupModal({
|
|||||||
accountId,
|
accountId,
|
||||||
selectedDeploymentRegion,
|
selectedDeploymentRegion,
|
||||||
handleRegionChange,
|
handleRegionChange,
|
||||||
|
connectionParams,
|
||||||
|
isConnectionParamsLoading,
|
||||||
} = useIntegrationModal({ onClose });
|
} = useIntegrationModal({ onClose });
|
||||||
|
|
||||||
const renderContent = useCallback(() => {
|
const renderContent = useCallback(() => {
|
||||||
@@ -71,6 +70,8 @@ function CloudAccountSetupModal({
|
|||||||
accountId={accountId}
|
accountId={accountId}
|
||||||
selectedDeploymentRegion={selectedDeploymentRegion}
|
selectedDeploymentRegion={selectedDeploymentRegion}
|
||||||
handleRegionChange={handleRegionChange}
|
handleRegionChange={handleRegionChange}
|
||||||
|
connectionParams={connectionParams}
|
||||||
|
isConnectionParamsLoading={isConnectionParamsLoading}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}, [
|
}, [
|
||||||
@@ -86,6 +87,8 @@ function CloudAccountSetupModal({
|
|||||||
accountId,
|
accountId,
|
||||||
selectedDeploymentRegion,
|
selectedDeploymentRegion,
|
||||||
handleRegionChange,
|
handleRegionChange,
|
||||||
|
connectionParams,
|
||||||
|
isConnectionParamsLoading,
|
||||||
setSelectedRegions,
|
setSelectedRegions,
|
||||||
setIncludeAllRegions,
|
setIncludeAllRegions,
|
||||||
]);
|
]);
|
||||||
@@ -96,11 +99,6 @@ function CloudAccountSetupModal({
|
|||||||
[selectedRegions, allRegions],
|
[selectedRegions, allRegions],
|
||||||
);
|
);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const handleGoToDashboards = useCallback((): void => {
|
|
||||||
navigate(ROUTES.ALL_DASHBOARD);
|
|
||||||
}, [navigate]);
|
|
||||||
|
|
||||||
const getModalConfig = useCallback(() => {
|
const getModalConfig = useCallback(() => {
|
||||||
// Handle success state first
|
// Handle success state first
|
||||||
if (modalState === ModalStateEnum.SUCCESS) {
|
if (modalState === ModalStateEnum.SUCCESS) {
|
||||||
@@ -108,11 +106,11 @@ function CloudAccountSetupModal({
|
|||||||
title: 'AWS Webservice Integration',
|
title: 'AWS Webservice Integration',
|
||||||
okText: (
|
okText: (
|
||||||
<div className="cloud-account-setup-success-view__footer-button">
|
<div className="cloud-account-setup-success-view__footer-button">
|
||||||
Go to Dashboards
|
Continue
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
block: true,
|
block: true,
|
||||||
onOk: handleGoToDashboards,
|
onOk: handleClose,
|
||||||
cancelButtonProps: { style: { display: 'none' } },
|
cancelButtonProps: { style: { display: 'none' } },
|
||||||
disabled: false,
|
disabled: false,
|
||||||
};
|
};
|
||||||
@@ -151,7 +149,7 @@ function CloudAccountSetupModal({
|
|||||||
isLoading,
|
isLoading,
|
||||||
isGeneratingUrl,
|
isGeneratingUrl,
|
||||||
activeView,
|
activeView,
|
||||||
handleGoToDashboards,
|
handleClose,
|
||||||
setActiveView,
|
setActiveView,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -159,7 +157,7 @@ function CloudAccountSetupModal({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SignozModal
|
<SignozModal
|
||||||
open={isOpen}
|
open
|
||||||
className="cloud-account-setup-modal"
|
className="cloud-account-setup-modal"
|
||||||
title={modalConfig.title}
|
title={modalConfig.title}
|
||||||
onCancel={handleClose}
|
onCancel={handleClose}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
MonitoringRegionsSection,
|
MonitoringRegionsSection,
|
||||||
RegionDeploymentSection,
|
RegionDeploymentSection,
|
||||||
} from './IntegrateNowFormSections';
|
} from './IntegrateNowFormSections';
|
||||||
|
import RenderConnectionFields from './RenderConnectionParams';
|
||||||
|
|
||||||
const allRegions = (): string[] =>
|
const allRegions = (): string[] =>
|
||||||
regions.flatMap((r) => r.subRegions.map((sr) => sr.name));
|
regions.flatMap((r) => r.subRegions.map((sr) => sr.name));
|
||||||
@@ -35,6 +36,8 @@ export function RegionForm({
|
|||||||
accountId,
|
accountId,
|
||||||
selectedDeploymentRegion,
|
selectedDeploymentRegion,
|
||||||
handleRegionChange,
|
handleRegionChange,
|
||||||
|
connectionParams,
|
||||||
|
isConnectionParamsLoading,
|
||||||
}: RegionFormProps): JSX.Element {
|
}: RegionFormProps): JSX.Element {
|
||||||
const startTimeRef = useRef(Date.now());
|
const startTimeRef = useRef(Date.now());
|
||||||
const refetchInterval = 10 * 1000;
|
const refetchInterval = 10 * 1000;
|
||||||
@@ -88,6 +91,11 @@ export function RegionForm({
|
|||||||
isFormDisabled={isFormDisabled}
|
isFormDisabled={isFormDisabled}
|
||||||
/>
|
/>
|
||||||
<ComplianceNote />
|
<ComplianceNote />
|
||||||
|
<RenderConnectionFields
|
||||||
|
isConnectionParamsLoading={isConnectionParamsLoading}
|
||||||
|
connectionParams={connectionParams}
|
||||||
|
isFormDisabled={isFormDisabled}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
.remove-integration-account {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid rgba(218, 85, 101, 0.2);
|
||||||
|
background: rgba(218, 85, 101, 0.06);
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
color: var(--bg-cherry-500);
|
||||||
|
font-size: 14px;
|
||||||
|
letter-spacing: -0.07px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subtitle {
|
||||||
|
color: var(--bg-cherry-300);
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
letter-spacing: -0.07px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--bg-cherry-500);
|
||||||
|
border: none;
|
||||||
|
color: var(--bg-vanilla-100);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 13.3px; /* 110.833% */
|
||||||
|
padding: 9px 13px;
|
||||||
|
.ant-btn-icon {
|
||||||
|
margin-inline-end: 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
&.ant-btn-default {
|
||||||
|
color: var(--bg-vanilla-300) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import './RemoveIntegrationAccount.scss';
|
||||||
|
|
||||||
|
import { Button, Modal } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
|
import removeAwsIntegrationAccount from 'api/Integrations/removeAwsIntegrationAccount';
|
||||||
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
|
import { X } from 'lucide-react';
|
||||||
|
import { INTEGRATION_TELEMETRY_EVENTS } from 'pages/Integrations/utils';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useMutation } from 'react-query';
|
||||||
|
|
||||||
|
function RemoveIntegrationAccount({
|
||||||
|
accountId,
|
||||||
|
onRemoveIntegrationAccountSuccess,
|
||||||
|
}: {
|
||||||
|
accountId: string;
|
||||||
|
onRemoveIntegrationAccountSuccess: () => void;
|
||||||
|
}): JSX.Element {
|
||||||
|
const { notifications } = useNotifications();
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
|
||||||
|
const showModal = (): void => {
|
||||||
|
setIsModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
mutate: removeIntegration,
|
||||||
|
isLoading: isRemoveIntegrationLoading,
|
||||||
|
} = useMutation(removeAwsIntegrationAccount, {
|
||||||
|
onSuccess: () => {
|
||||||
|
onRemoveIntegrationAccountSuccess?.();
|
||||||
|
setIsModalOpen(false);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
notifications.error({
|
||||||
|
message: SOMETHING_WENT_WRONG,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const handleOk = (): void => {
|
||||||
|
logEvent(INTEGRATION_TELEMETRY_EVENTS.AWS_INTEGRATION_ACCOUNT_REMOVED, {
|
||||||
|
accountId,
|
||||||
|
});
|
||||||
|
removeIntegration(accountId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = (): void => {
|
||||||
|
setIsModalOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="remove-integration-account">
|
||||||
|
<div className="remove-integration-account__header">
|
||||||
|
<div className="remove-integration-account__title">Remove Integration</div>
|
||||||
|
<div className="remove-integration-account__subtitle">
|
||||||
|
Removing this integration won't delete any existing data but will stop
|
||||||
|
collecting new data from AWS.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
className="remove-integration-account__button"
|
||||||
|
icon={<X size={14} />}
|
||||||
|
onClick={(): void => showModal()}
|
||||||
|
>
|
||||||
|
Remove
|
||||||
|
</Button>
|
||||||
|
<Modal
|
||||||
|
className="remove-integration-modal"
|
||||||
|
open={isModalOpen}
|
||||||
|
title="Remove integration"
|
||||||
|
onOk={handleOk}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
okText="Remove Integration"
|
||||||
|
okButtonProps={{
|
||||||
|
danger: true,
|
||||||
|
disabled: isRemoveIntegrationLoading,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="remove-integration-modal__text">
|
||||||
|
Removing this account will remove all components created for sending
|
||||||
|
telemetry to SigNoz in your AWS account within the next ~15 minutes
|
||||||
|
(cloudformation stacks named signoz-integration-telemetry-collection in
|
||||||
|
enabled regions). <br />
|
||||||
|
<br />
|
||||||
|
After that, you can delete the cloudformation stack that was created
|
||||||
|
manually when connecting this account.
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RemoveIntegrationAccount;
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import { Form, Input } from 'antd';
|
||||||
|
import { ConnectionParams } from 'types/api/integrations/aws';
|
||||||
|
|
||||||
|
function RenderConnectionFields({
|
||||||
|
isConnectionParamsLoading,
|
||||||
|
connectionParams,
|
||||||
|
isFormDisabled,
|
||||||
|
}: {
|
||||||
|
isConnectionParamsLoading?: boolean;
|
||||||
|
connectionParams?: ConnectionParams | null;
|
||||||
|
isFormDisabled?: boolean;
|
||||||
|
}): JSX.Element | null {
|
||||||
|
if (
|
||||||
|
isConnectionParamsLoading ||
|
||||||
|
(!!connectionParams?.ingestion_url &&
|
||||||
|
!!connectionParams?.ingestion_key &&
|
||||||
|
!!connectionParams?.signoz_api_url &&
|
||||||
|
!!connectionParams?.signoz_api_key)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form.Item name="connection_params">
|
||||||
|
{!connectionParams?.ingestion_url && (
|
||||||
|
<Form.Item
|
||||||
|
name="ingestion_url"
|
||||||
|
label="Ingestion URL"
|
||||||
|
rules={[{ required: true, message: 'Please enter ingestion URL' }]}
|
||||||
|
>
|
||||||
|
<Input placeholder="Enter ingestion URL" disabled={isFormDisabled} />
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
{!connectionParams?.ingestion_key && (
|
||||||
|
<Form.Item
|
||||||
|
name="ingestion_key"
|
||||||
|
label="Ingestion Key"
|
||||||
|
rules={[{ required: true, message: 'Please enter ingestion key' }]}
|
||||||
|
>
|
||||||
|
<Input placeholder="Enter ingestion key" disabled={isFormDisabled} />
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
{!connectionParams?.signoz_api_url && (
|
||||||
|
<Form.Item
|
||||||
|
name="signoz_api_url"
|
||||||
|
label="SigNoz API URL"
|
||||||
|
rules={[{ required: true, message: 'Please enter SigNoz API URL' }]}
|
||||||
|
>
|
||||||
|
<Input placeholder="Enter SigNoz API URL" disabled={isFormDisabled} />
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
{!connectionParams?.signoz_api_key && (
|
||||||
|
<Form.Item
|
||||||
|
name="signoz_api_key"
|
||||||
|
label="SigNoz API KEY"
|
||||||
|
rules={[{ required: true, message: 'Please enter SigNoz API Key' }]}
|
||||||
|
>
|
||||||
|
<Input placeholder="Enter SigNoz API Key" disabled={isFormDisabled} />
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderConnectionFields.defaultProps = {
|
||||||
|
connectionParams: null,
|
||||||
|
isFormDisabled: false,
|
||||||
|
isConnectionParamsLoading: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RenderConnectionFields;
|
||||||
@@ -53,23 +53,18 @@ export function SuccessView(): JSX.Element {
|
|||||||
WHAT NEXT
|
WHAT NEXT
|
||||||
</h4>
|
</h4>
|
||||||
<div className="what-next-items-wrapper">
|
<div className="what-next-items-wrapper">
|
||||||
{[
|
<Alert
|
||||||
'Understand your AWS services with SigNoz’s out-of-the-box dashboards',
|
message={
|
||||||
'Set up alerts for real-time monitoring.',
|
<div className="what-next-items-wrapper__item">
|
||||||
'Track logs and traces.',
|
<div className="what-next-item-bullet-icon">•</div>
|
||||||
].map((item) => (
|
<div className="what-next-item-text">
|
||||||
<Alert
|
Set up your AWS services effortlessly under your enabled account.
|
||||||
key={item}
|
|
||||||
message={
|
|
||||||
<div className="what-next-items-wrapper__item">
|
|
||||||
<div className="what-next-item-bullet-icon">•</div>
|
|
||||||
<div className="what-next-item-text">{item}</div>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
</div>
|
||||||
type="info"
|
}
|
||||||
className="what-next-items-wrapper__item"
|
type="info"
|
||||||
/>
|
className="what-next-items-wrapper__item"
|
||||||
))}
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { FormInstance } from 'antd';
|
import { FormInstance } from 'antd';
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
|
import { ConnectionParams } from 'types/api/integrations/aws';
|
||||||
|
|
||||||
export enum ActiveViewEnum {
|
export enum ActiveViewEnum {
|
||||||
SELECT_REGIONS = 'select-regions',
|
SELECT_REGIONS = 'select-regions',
|
||||||
@@ -25,9 +26,10 @@ export interface RegionFormProps {
|
|||||||
accountId?: string;
|
accountId?: string;
|
||||||
selectedDeploymentRegion: string | undefined;
|
selectedDeploymentRegion: string | undefined;
|
||||||
handleRegionChange: (value: string) => void;
|
handleRegionChange: (value: string) => void;
|
||||||
|
connectionParams?: ConnectionParams;
|
||||||
|
isConnectionParamsLoading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IntegrationModalProps {
|
export interface IntegrationModalProps {
|
||||||
isOpen: boolean;
|
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { ServiceData } from './types';
|
import { ServiceData } from './types';
|
||||||
|
|
||||||
function DashboardItem({
|
function DashboardItem({
|
||||||
@@ -5,8 +7,8 @@ function DashboardItem({
|
|||||||
}: {
|
}: {
|
||||||
dashboard: ServiceData['assets']['dashboards'][number];
|
dashboard: ServiceData['assets']['dashboards'][number];
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
return (
|
const content = (
|
||||||
<div className="cloud-service-dashboard-item">
|
<>
|
||||||
<div className="cloud-service-dashboard-item__title">{dashboard.title}</div>
|
<div className="cloud-service-dashboard-item__title">{dashboard.title}</div>
|
||||||
<div className="cloud-service-dashboard-item__preview">
|
<div className="cloud-service-dashboard-item__preview">
|
||||||
<img
|
<img
|
||||||
@@ -15,6 +17,18 @@ function DashboardItem({
|
|||||||
className="cloud-service-dashboard-item__preview-image"
|
className="cloud-service-dashboard-item__preview-image"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="cloud-service-dashboard-item">
|
||||||
|
{dashboard.url ? (
|
||||||
|
<Link to={dashboard.url} className="cloud-service-dashboard-item__link">
|
||||||
|
{content}
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
content
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Color } from '@signozhq/design-tokens';
|
|
||||||
import { Button, Tabs, TabsProps } from 'antd';
|
import { Button, Tabs, TabsProps } from 'antd';
|
||||||
import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer';
|
import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
@@ -8,8 +7,7 @@ import { IServiceStatus } from 'container/CloudIntegrationPage/ServicesSection/t
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useServiceDetails } from 'hooks/integrations/aws/useServiceDetails';
|
import { useServiceDetails } from 'hooks/integrations/aws/useServiceDetails';
|
||||||
import useUrlQuery from 'hooks/useUrlQuery';
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
import { Wrench } from 'lucide-react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useState } from 'react';
|
|
||||||
|
|
||||||
import ConfigureServiceModal from './ConfigureServiceModal';
|
import ConfigureServiceModal from './ConfigureServiceModal';
|
||||||
|
|
||||||
@@ -38,7 +36,7 @@ const getStatus = (
|
|||||||
function ServiceStatus({
|
function ServiceStatus({
|
||||||
serviceStatus,
|
serviceStatus,
|
||||||
}: {
|
}: {
|
||||||
serviceStatus: IServiceStatus | null;
|
serviceStatus: IServiceStatus | undefined;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const logsLastReceivedTimestamp = serviceStatus?.logs?.last_received_ts_ms;
|
const logsLastReceivedTimestamp = serviceStatus?.logs?.last_received_ts_ms;
|
||||||
const metricsLastReceivedTimestamp =
|
const metricsLastReceivedTimestamp =
|
||||||
@@ -54,7 +52,7 @@ function ServiceStatus({
|
|||||||
|
|
||||||
function ServiceDetails(): JSX.Element | null {
|
function ServiceDetails(): JSX.Element | null {
|
||||||
const urlQuery = useUrlQuery();
|
const urlQuery = useUrlQuery();
|
||||||
const accountId = urlQuery.get('accountId');
|
const cloudAccountId = urlQuery.get('cloudAccountId');
|
||||||
const serviceId = urlQuery.get('service');
|
const serviceId = urlQuery.get('service');
|
||||||
const [isConfigureServiceModalOpen, setIsConfigureServiceModalOpen] = useState(
|
const [isConfigureServiceModalOpen, setIsConfigureServiceModalOpen] = useState(
|
||||||
false,
|
false,
|
||||||
@@ -62,7 +60,24 @@ function ServiceDetails(): JSX.Element | null {
|
|||||||
|
|
||||||
const { data: serviceDetailsData, isLoading } = useServiceDetails(
|
const { data: serviceDetailsData, isLoading } = useServiceDetails(
|
||||||
serviceId || '',
|
serviceId || '',
|
||||||
accountId || undefined,
|
cloudAccountId || undefined,
|
||||||
|
);
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
const { config, supported_signals } = serviceDetailsData ?? {};
|
||||||
|
|
||||||
|
const totalSupportedSignals = Object.entries(supported_signals || {}).filter(
|
||||||
|
([, value]) => !!value,
|
||||||
|
).length;
|
||||||
|
const enabledSignals = useMemo(
|
||||||
|
() =>
|
||||||
|
Object.values(config || {}).filter((item) => item && item.enabled).length,
|
||||||
|
[config],
|
||||||
|
);
|
||||||
|
|
||||||
|
const isAnySignalConfigured = useMemo(
|
||||||
|
() => !!config?.logs?.enabled || !!config?.metrics?.enabled,
|
||||||
|
[config],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
@@ -96,16 +111,22 @@ function ServiceDetails(): JSX.Element | null {
|
|||||||
<div className="service-details__title-bar">
|
<div className="service-details__title-bar">
|
||||||
<div className="service-details__details-title">Details</div>
|
<div className="service-details__details-title">Details</div>
|
||||||
<div className="service-details__right-actions">
|
<div className="service-details__right-actions">
|
||||||
{serviceDetailsData?.status && (
|
<ServiceStatus serviceStatus={serviceDetailsData.status} />
|
||||||
<ServiceStatus serviceStatus={serviceDetailsData.status} />
|
|
||||||
)}
|
{!!cloudAccountId && isAnySignalConfigured ? (
|
||||||
{!!accountId && (
|
|
||||||
<Button
|
<Button
|
||||||
className="configure-button"
|
className="configure-button configure-button--default"
|
||||||
onClick={(): void => setIsConfigureServiceModalOpen(true)}
|
onClick={(): void => setIsConfigureServiceModalOpen(true)}
|
||||||
>
|
>
|
||||||
<Wrench size={12} color={Color.BG_VANILLA_400} />
|
Configure ({enabledSignals}/{totalSupportedSignals})
|
||||||
Configure
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
className="configure-button configure-button--primary"
|
||||||
|
onClick={(): void => setIsConfigureServiceModalOpen(true)}
|
||||||
|
>
|
||||||
|
Enable Service
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -119,15 +140,17 @@ function ServiceDetails(): JSX.Element | null {
|
|||||||
<div className="service-details__tabs">
|
<div className="service-details__tabs">
|
||||||
<Tabs items={tabItems} />
|
<Tabs items={tabItems} />
|
||||||
</div>
|
</div>
|
||||||
<ConfigureServiceModal
|
{isConfigureServiceModalOpen && (
|
||||||
isOpen={isConfigureServiceModalOpen}
|
<ConfigureServiceModal
|
||||||
onClose={(): void => setIsConfigureServiceModalOpen(false)}
|
isOpen
|
||||||
serviceName={serviceDetailsData.title}
|
onClose={(): void => setIsConfigureServiceModalOpen(false)}
|
||||||
serviceId={serviceId || ''}
|
serviceName={serviceDetailsData.title}
|
||||||
cloudAccountId={accountId || ''}
|
serviceId={serviceId || ''}
|
||||||
initialConfig={serviceDetailsData.config}
|
cloudAccountId={cloudAccountId || ''}
|
||||||
supportedSignals={serviceDetailsData.supported_signals || {}}
|
initialConfig={serviceDetailsData.config}
|
||||||
/>
|
supportedSignals={serviceDetailsData.supported_signals || {}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,34 @@
|
|||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import { useGetAccountServices } from 'hooks/integrations/aws/useGetAccountServices';
|
import { useGetAccountServices } from 'hooks/integrations/aws/useGetAccountServices';
|
||||||
import useUrlQuery from 'hooks/useUrlQuery';
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
import { useMemo } from 'react';
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom-v5-compat';
|
import { useNavigate } from 'react-router-dom-v5-compat';
|
||||||
|
|
||||||
import ServiceItem from './ServiceItem';
|
import ServiceItem from './ServiceItem';
|
||||||
|
|
||||||
interface ServicesListProps {
|
interface ServicesListProps {
|
||||||
accountId: string;
|
cloudAccountId: string;
|
||||||
filter: 'all_services' | 'enabled' | 'available';
|
filter: 'all_services' | 'enabled' | 'available';
|
||||||
}
|
}
|
||||||
|
|
||||||
function ServicesList({ accountId, filter }: ServicesListProps): JSX.Element {
|
function ServicesList({
|
||||||
|
cloudAccountId,
|
||||||
|
filter,
|
||||||
|
}: ServicesListProps): JSX.Element {
|
||||||
const urlQuery = useUrlQuery();
|
const urlQuery = useUrlQuery();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { data: services = [], isLoading } = useGetAccountServices(accountId);
|
const { data: services = [], isLoading } = useGetAccountServices(
|
||||||
|
cloudAccountId,
|
||||||
|
);
|
||||||
const activeService = urlQuery.get('service');
|
const activeService = urlQuery.get('service');
|
||||||
|
|
||||||
const handleServiceClick = (serviceId: string): void => {
|
const handleActiveService = useCallback(
|
||||||
urlQuery.set('service', serviceId);
|
(serviceId: string): void => {
|
||||||
navigate({ search: urlQuery.toString() });
|
urlQuery.set('service', serviceId);
|
||||||
};
|
navigate({ search: urlQuery.toString() });
|
||||||
|
},
|
||||||
|
[navigate, urlQuery],
|
||||||
|
);
|
||||||
|
|
||||||
const filteredServices = useMemo(() => {
|
const filteredServices = useMemo(() => {
|
||||||
if (filter === 'all_services') return services;
|
if (filter === 'all_services') return services;
|
||||||
@@ -32,6 +40,12 @@ function ServicesList({ accountId, filter }: ServicesListProps): JSX.Element {
|
|||||||
});
|
});
|
||||||
}, [services, filter]);
|
}, [services, filter]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeService || !services?.length) return;
|
||||||
|
|
||||||
|
handleActiveService(services[0].id);
|
||||||
|
}, [services, activeService, handleActiveService]);
|
||||||
|
|
||||||
if (isLoading) return <Spinner size="large" height="25vh" />;
|
if (isLoading) return <Spinner size="large" height="25vh" />;
|
||||||
if (!services) return <div>No services found</div>;
|
if (!services) return <div>No services found</div>;
|
||||||
|
|
||||||
@@ -41,7 +55,7 @@ function ServicesList({ accountId, filter }: ServicesListProps): JSX.Element {
|
|||||||
<ServiceItem
|
<ServiceItem
|
||||||
key={service.id}
|
key={service.id}
|
||||||
service={service}
|
service={service}
|
||||||
onClick={handleServiceClick}
|
onClick={handleActiveService}
|
||||||
isActive={service.id === activeService}
|
isActive={service.id === activeService}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -135,18 +135,25 @@
|
|||||||
color: var(--bg-cherry-400);
|
color: var(--bg-cherry-400);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.configure-button {
|
.configure-button {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
color: var(--bg-vanilla-400);
|
|
||||||
background: var(--bg-ink-300);
|
|
||||||
border: 1px solid var(--bg-slate-400);
|
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 400;
|
|
||||||
line-height: 10px; /* 83.333% */
|
|
||||||
letter-spacing: 0.12px;
|
letter-spacing: 0.12px;
|
||||||
|
font-weight: 500;
|
||||||
|
width: 116px;
|
||||||
|
box-shadow: none;
|
||||||
|
&--default {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
background: var(--bg-slate-400);
|
||||||
|
border: 1px solid var(--bg-slate-400);
|
||||||
|
}
|
||||||
|
&--primary {
|
||||||
|
background-color: var(--bg-robin-500);
|
||||||
|
color: var(--bg-vanilla-100);
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--Vanilla-100, #fff);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,17 +20,17 @@ export enum ServiceFilterType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ServicesFilterProps {
|
interface ServicesFilterProps {
|
||||||
accountId: string;
|
cloudAccountId: string;
|
||||||
onFilterChange: (value: ServiceFilterType) => void;
|
onFilterChange: (value: ServiceFilterType) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ServicesFilter({
|
function ServicesFilter({
|
||||||
accountId,
|
cloudAccountId,
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
}: ServicesFilterProps): JSX.Element | null {
|
}: ServicesFilterProps): JSX.Element | null {
|
||||||
const { data: services, isLoading } = useQuery(
|
const { data: services, isLoading } = useQuery(
|
||||||
[REACT_QUERY_KEY.AWS_SERVICES, accountId],
|
[REACT_QUERY_KEY.AWS_SERVICES, cloudAccountId],
|
||||||
() => getAwsServices(accountId),
|
() => getAwsServices(cloudAccountId),
|
||||||
);
|
);
|
||||||
|
|
||||||
const { enabledCount, availableCount } = useMemo(() => {
|
const { enabledCount, availableCount } = useMemo(() => {
|
||||||
@@ -77,7 +77,7 @@ function ServicesFilter({
|
|||||||
|
|
||||||
function ServicesSection(): JSX.Element {
|
function ServicesSection(): JSX.Element {
|
||||||
const urlQuery = useUrlQuery();
|
const urlQuery = useUrlQuery();
|
||||||
const accountId = urlQuery.get('accountId') || '';
|
const cloudAccountId = urlQuery.get('cloudAccountId') || '';
|
||||||
|
|
||||||
const [activeFilter, setActiveFilter] = useState<
|
const [activeFilter, setActiveFilter] = useState<
|
||||||
'all_services' | 'enabled' | 'available'
|
'all_services' | 'enabled' | 'available'
|
||||||
@@ -86,8 +86,11 @@ function ServicesSection(): JSX.Element {
|
|||||||
return (
|
return (
|
||||||
<div className="services-section">
|
<div className="services-section">
|
||||||
<div className="services-section__sidebar">
|
<div className="services-section__sidebar">
|
||||||
<ServicesFilter accountId={accountId} onFilterChange={setActiveFilter} />
|
<ServicesFilter
|
||||||
<ServicesList accountId={accountId} filter={activeFilter} />
|
cloudAccountId={cloudAccountId}
|
||||||
|
onFilterChange={setActiveFilter}
|
||||||
|
/>
|
||||||
|
<ServicesList cloudAccountId={cloudAccountId} filter={activeFilter} />
|
||||||
</div>
|
</div>
|
||||||
<div className="services-section__content">
|
<div className="services-section__content">
|
||||||
<ServiceDetails />
|
<ServiceDetails />
|
||||||
|
|||||||
@@ -0,0 +1,233 @@
|
|||||||
|
import { fireEvent, render, screen } from '@testing-library/react';
|
||||||
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
|
import { AppProvider } from 'providers/App/App';
|
||||||
|
import MockQueryClientProvider from 'providers/test/MockQueryClientProvider';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import store from 'store';
|
||||||
|
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
|
import {
|
||||||
|
MENUITEM_KEYS_VS_LABELS,
|
||||||
|
MenuItemKeys,
|
||||||
|
} from '../WidgetHeader/contants';
|
||||||
|
import { WidgetGraphComponentProps } from './types';
|
||||||
|
import WidgetGraphComponent from './WidgetGraphComponent';
|
||||||
|
|
||||||
|
jest.mock('react-router-dom', () => ({
|
||||||
|
...jest.requireActual('react-router-dom'),
|
||||||
|
useLocation: (): { pathname: string } => ({
|
||||||
|
pathname: `${process.env.FRONTEND_API_ENDPOINT}/${ROUTES.DASHBOARD}/624652db-6097-42f5-bbca-e9012901db00`,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('uplot', () => {
|
||||||
|
const paths = {
|
||||||
|
spline: jest.fn(),
|
||||||
|
bars: jest.fn(),
|
||||||
|
};
|
||||||
|
const uplotMock = jest.fn(() => ({
|
||||||
|
paths,
|
||||||
|
}));
|
||||||
|
return {
|
||||||
|
paths,
|
||||||
|
default: uplotMock,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock data
|
||||||
|
const mockProps: WidgetGraphComponentProps = {
|
||||||
|
widget: {
|
||||||
|
bucketCount: 30,
|
||||||
|
bucketWidth: 0,
|
||||||
|
columnUnits: {},
|
||||||
|
description: '',
|
||||||
|
fillSpans: false,
|
||||||
|
id: '17f905f6-d355-46bd-a78e-cbc87e6f58cc',
|
||||||
|
isStacked: false,
|
||||||
|
mergeAllActiveQueries: false,
|
||||||
|
nullZeroValues: 'zero',
|
||||||
|
opacity: '1',
|
||||||
|
panelTypes: PANEL_TYPES.VALUE,
|
||||||
|
query: {
|
||||||
|
builder: {
|
||||||
|
queryData: [
|
||||||
|
{
|
||||||
|
aggregateAttribute: {
|
||||||
|
dataType: DataTypes.String,
|
||||||
|
id: 'span_id--string----true',
|
||||||
|
isColumn: true,
|
||||||
|
isJSON: false,
|
||||||
|
key: 'span_id',
|
||||||
|
type: '',
|
||||||
|
},
|
||||||
|
aggregateOperator: 'count_distinct',
|
||||||
|
dataSource: DataSource.TRACES,
|
||||||
|
disabled: false,
|
||||||
|
expression: 'A',
|
||||||
|
filters: {
|
||||||
|
items: [],
|
||||||
|
op: 'AND',
|
||||||
|
},
|
||||||
|
functions: [],
|
||||||
|
groupBy: [],
|
||||||
|
having: [],
|
||||||
|
legend: '',
|
||||||
|
limit: null,
|
||||||
|
orderBy: [],
|
||||||
|
queryName: 'A',
|
||||||
|
reduceTo: 'last',
|
||||||
|
spaceAggregation: 'sum',
|
||||||
|
stepInterval: 60,
|
||||||
|
timeAggregation: 'count_distinct',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
queryFormulas: [],
|
||||||
|
},
|
||||||
|
clickhouse_sql: [
|
||||||
|
{
|
||||||
|
disabled: false,
|
||||||
|
legend: '',
|
||||||
|
name: 'A',
|
||||||
|
query: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
id: '47449208-2c76-4465-9c62-a37fb4f5f11f',
|
||||||
|
promql: [
|
||||||
|
{
|
||||||
|
disabled: false,
|
||||||
|
legend: '',
|
||||||
|
name: 'A',
|
||||||
|
query: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
queryType: EQueryType.QUERY_BUILDER,
|
||||||
|
},
|
||||||
|
selectedLogFields: [
|
||||||
|
{
|
||||||
|
dataType: 'string',
|
||||||
|
name: 'body',
|
||||||
|
type: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataType: 'string',
|
||||||
|
name: 'timestamp',
|
||||||
|
type: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
selectedTracesFields: [],
|
||||||
|
softMax: 0,
|
||||||
|
softMin: 0,
|
||||||
|
stackedBarChart: false,
|
||||||
|
thresholds: [],
|
||||||
|
timePreferance: 'GLOBAL_TIME',
|
||||||
|
title: 'Test Dashboard',
|
||||||
|
yAxisUnit: 'none',
|
||||||
|
},
|
||||||
|
queryResponse: {
|
||||||
|
status: 'loading',
|
||||||
|
isLoading: true,
|
||||||
|
isSuccess: false,
|
||||||
|
isError: false,
|
||||||
|
isIdle: false,
|
||||||
|
dataUpdatedAt: 0,
|
||||||
|
error: null,
|
||||||
|
errorUpdatedAt: 0,
|
||||||
|
failureCount: 0,
|
||||||
|
errorUpdateCount: 0,
|
||||||
|
isFetched: false,
|
||||||
|
isFetchedAfterMount: false,
|
||||||
|
isFetching: true,
|
||||||
|
isRefetching: false,
|
||||||
|
isLoadingError: false,
|
||||||
|
isPlaceholderData: false,
|
||||||
|
isPreviousData: false,
|
||||||
|
isRefetchError: false,
|
||||||
|
isStale: true,
|
||||||
|
data: undefined,
|
||||||
|
refetch: jest.fn(),
|
||||||
|
remove: jest.fn(),
|
||||||
|
},
|
||||||
|
errorMessage: '',
|
||||||
|
version: 'v4',
|
||||||
|
headerMenuList: [
|
||||||
|
MenuItemKeys.View,
|
||||||
|
MenuItemKeys.Clone,
|
||||||
|
MenuItemKeys.Delete,
|
||||||
|
MenuItemKeys.Edit,
|
||||||
|
MenuItemKeys.CreateAlerts,
|
||||||
|
],
|
||||||
|
isWarning: false,
|
||||||
|
isFetchingResponse: false,
|
||||||
|
setRequestData: jest.fn(),
|
||||||
|
onClickHandler: jest.fn(),
|
||||||
|
onDragSelect: jest.fn(),
|
||||||
|
openTracesButton: false,
|
||||||
|
onOpenTraceBtnClick: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mock useDashabord hook
|
||||||
|
jest.mock('providers/Dashboard/Dashboard', () => ({
|
||||||
|
useDashboard: (): any => ({
|
||||||
|
selectedDashboard: {
|
||||||
|
data: {
|
||||||
|
variables: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('WidgetGraphComponent', () => {
|
||||||
|
it('should show correct menu items when hovering over more options while loading', async () => {
|
||||||
|
const { getByTestId, findByRole, getByText, container } = render(
|
||||||
|
<MockQueryClientProvider>
|
||||||
|
<Provider store={store}>
|
||||||
|
<AppProvider>
|
||||||
|
<WidgetGraphComponent
|
||||||
|
widget={mockProps.widget}
|
||||||
|
queryResponse={mockProps.queryResponse}
|
||||||
|
errorMessage={mockProps.errorMessage}
|
||||||
|
version={mockProps.version}
|
||||||
|
headerMenuList={mockProps.headerMenuList}
|
||||||
|
isWarning={mockProps.isWarning}
|
||||||
|
isFetchingResponse={mockProps.isFetchingResponse}
|
||||||
|
setRequestData={mockProps.setRequestData}
|
||||||
|
onClickHandler={mockProps.onClickHandler}
|
||||||
|
onDragSelect={mockProps.onDragSelect}
|
||||||
|
openTracesButton={mockProps.openTracesButton}
|
||||||
|
onOpenTraceBtnClick={mockProps.onOpenTraceBtnClick}
|
||||||
|
/>
|
||||||
|
</AppProvider>
|
||||||
|
</Provider>
|
||||||
|
</MockQueryClientProvider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(getByText('Test Dashboard')).toBeInTheDocument();
|
||||||
|
|
||||||
|
// check if skeleton is rendered
|
||||||
|
const skeleton = container.querySelector('.ant-skeleton');
|
||||||
|
expect(skeleton).toBeInTheDocument();
|
||||||
|
|
||||||
|
const moreOptionsButton = getByTestId('widget-header-options');
|
||||||
|
fireEvent.mouseEnter(moreOptionsButton);
|
||||||
|
|
||||||
|
const menu = await findByRole('menu');
|
||||||
|
expect(menu).toBeInTheDocument();
|
||||||
|
|
||||||
|
// Check if all menu items are present
|
||||||
|
const expectedMenuItems = [
|
||||||
|
MENUITEM_KEYS_VS_LABELS[MenuItemKeys.View],
|
||||||
|
MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Clone],
|
||||||
|
MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Delete],
|
||||||
|
MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Edit],
|
||||||
|
MENUITEM_KEYS_VS_LABELS[MenuItemKeys.CreateAlerts],
|
||||||
|
];
|
||||||
|
|
||||||
|
// check that menu is visible
|
||||||
|
expectedMenuItems.forEach((item) => {
|
||||||
|
expect(screen.getByText(item)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -6,6 +6,7 @@ import { ToggleGraphProps } from 'components/Graph/types';
|
|||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
|
import { placeWidgetAtBottom } from 'container/NewWidget/utils';
|
||||||
import PanelWrapper from 'container/PanelWrapper/PanelWrapper';
|
import PanelWrapper from 'container/PanelWrapper/PanelWrapper';
|
||||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
@@ -48,6 +49,7 @@ function WidgetGraphComponent({
|
|||||||
openTracesButton,
|
openTracesButton,
|
||||||
onOpenTraceBtnClick,
|
onOpenTraceBtnClick,
|
||||||
customSeries,
|
customSeries,
|
||||||
|
customErrorMessage,
|
||||||
}: WidgetGraphComponentProps): JSX.Element {
|
}: WidgetGraphComponentProps): JSX.Element {
|
||||||
const [deleteModal, setDeleteModal] = useState(false);
|
const [deleteModal, setDeleteModal] = useState(false);
|
||||||
const [hovered, setHovered] = useState(false);
|
const [hovered, setHovered] = useState(false);
|
||||||
@@ -132,18 +134,14 @@ function WidgetGraphComponent({
|
|||||||
(l) => l.i === widget.id,
|
(l) => l.i === widget.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
// added the cloned panel on the top as it is given most priority when arranging
|
const newLayoutItem = placeWidgetAtBottom(
|
||||||
// in the layout. React_grid_layout assigns priority from top, hence no random position for cloned panel
|
uuid,
|
||||||
const layout = [
|
selectedDashboard?.data.layout || [],
|
||||||
{
|
originalPanelLayout?.w || 6,
|
||||||
i: uuid,
|
originalPanelLayout?.h || 6,
|
||||||
w: originalPanelLayout?.w || 6,
|
);
|
||||||
x: 0,
|
|
||||||
h: originalPanelLayout?.h || 6,
|
const layout = [...(selectedDashboard.data.layout || []), newLayoutItem];
|
||||||
y: 0,
|
|
||||||
},
|
|
||||||
...(selectedDashboard.data.layout || []),
|
|
||||||
];
|
|
||||||
|
|
||||||
updateDashboardMutation.mutateAsync(
|
updateDashboardMutation.mutateAsync(
|
||||||
{
|
{
|
||||||
@@ -231,21 +229,6 @@ function WidgetGraphComponent({
|
|||||||
|
|
||||||
const [searchTerm, setSearchTerm] = useState<string>('');
|
const [searchTerm, setSearchTerm] = useState<string>('');
|
||||||
|
|
||||||
const loadingState =
|
|
||||||
(queryResponse.isLoading || queryResponse.status === 'idle') &&
|
|
||||||
widget.panelTypes !== PANEL_TYPES.LIST;
|
|
||||||
|
|
||||||
if (loadingState) {
|
|
||||||
return (
|
|
||||||
<Skeleton
|
|
||||||
style={{
|
|
||||||
height: '100%',
|
|
||||||
padding: '16px',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -317,6 +300,13 @@ function WidgetGraphComponent({
|
|||||||
setSearchTerm={setSearchTerm}
|
setSearchTerm={setSearchTerm}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{queryResponse.error && customErrorMessage && (
|
||||||
|
<div className="error-message-container">
|
||||||
|
<Typography.Text type="warning">{customErrorMessage}</Typography.Text>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{queryResponse.isLoading && widget.panelTypes !== PANEL_TYPES.LIST && (
|
{queryResponse.isLoading && widget.panelTypes !== PANEL_TYPES.LIST && (
|
||||||
<Skeleton />
|
<Skeleton />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -41,9 +41,15 @@ function GridCardGraph({
|
|||||||
openTracesButton,
|
openTracesButton,
|
||||||
onOpenTraceBtnClick,
|
onOpenTraceBtnClick,
|
||||||
customSeries,
|
customSeries,
|
||||||
|
customErrorMessage,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
}: GridCardGraphProps): JSX.Element {
|
}: GridCardGraphProps): JSX.Element {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [errorMessage, setErrorMessage] = useState<string>();
|
const [errorMessage, setErrorMessage] = useState<string>();
|
||||||
|
const [isInternalServerError, setIsInternalServerError] = useState<boolean>(
|
||||||
|
false,
|
||||||
|
);
|
||||||
const {
|
const {
|
||||||
toScrollWidgetId,
|
toScrollWidgetId,
|
||||||
setToScrollWidgetId,
|
setToScrollWidgetId,
|
||||||
@@ -178,6 +184,8 @@ function GridCardGraph({
|
|||||||
variables: getDashboardVariables(variables),
|
variables: getDashboardVariables(variables),
|
||||||
selectedTime: widget.timePreferance || 'GLOBAL_TIME',
|
selectedTime: widget.timePreferance || 'GLOBAL_TIME',
|
||||||
globalSelectedInterval,
|
globalSelectedInterval,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
},
|
},
|
||||||
version || DEFAULT_ENTITY_VERSION,
|
version || DEFAULT_ENTITY_VERSION,
|
||||||
{
|
{
|
||||||
@@ -207,6 +215,11 @@ function GridCardGraph({
|
|||||||
refetchOnMount: false,
|
refetchOnMount: false,
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setErrorMessage(error.message);
|
setErrorMessage(error.message);
|
||||||
|
if (customErrorMessage) {
|
||||||
|
setIsInternalServerError(
|
||||||
|
String(error.message).includes('API responded with 500'),
|
||||||
|
);
|
||||||
|
}
|
||||||
setDashboardQueryRangeCalled(true);
|
setDashboardQueryRangeCalled(true);
|
||||||
},
|
},
|
||||||
onSettled: (data) => {
|
onSettled: (data) => {
|
||||||
@@ -256,6 +269,7 @@ function GridCardGraph({
|
|||||||
openTracesButton={openTracesButton}
|
openTracesButton={openTracesButton}
|
||||||
onOpenTraceBtnClick={onOpenTraceBtnClick}
|
onOpenTraceBtnClick={onOpenTraceBtnClick}
|
||||||
customSeries={customSeries}
|
customSeries={customSeries}
|
||||||
|
customErrorMessage={isInternalServerError ? customErrorMessage : undefined}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export interface WidgetGraphComponentProps {
|
|||||||
openTracesButton?: boolean;
|
openTracesButton?: boolean;
|
||||||
onOpenTraceBtnClick?: (record: RowData) => void;
|
onOpenTraceBtnClick?: (record: RowData) => void;
|
||||||
customSeries?: (data: QueryData[]) => uPlot.Series[];
|
customSeries?: (data: QueryData[]) => uPlot.Series[];
|
||||||
|
customErrorMessage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GridCardGraphProps {
|
export interface GridCardGraphProps {
|
||||||
@@ -54,6 +55,9 @@ export interface GridCardGraphProps {
|
|||||||
openTracesButton?: boolean;
|
openTracesButton?: boolean;
|
||||||
onOpenTraceBtnClick?: (record: RowData) => void;
|
onOpenTraceBtnClick?: (record: RowData) => void;
|
||||||
customSeries?: (data: QueryData[]) => uPlot.Series[];
|
customSeries?: (data: QueryData[]) => uPlot.Series[];
|
||||||
|
customErrorMessage?: string;
|
||||||
|
start?: number;
|
||||||
|
end?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GetGraphVisibilityStateOnLegendClickProps {
|
export interface GetGraphVisibilityStateOnLegendClickProps {
|
||||||
|
|||||||
@@ -106,6 +106,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error-message-container {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
.row-settings {
|
.row-settings {
|
||||||
.ant-popover-inner {
|
.ant-popover-inner {
|
||||||
width: 191px;
|
width: 191px;
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import DashboardEmptyState from './DashboardEmptyState/DashboardEmptyState';
|
|||||||
import GridCard from './GridCard';
|
import GridCard from './GridCard';
|
||||||
import { Card, CardContainer, ReactGridLayout } from './styles';
|
import { Card, CardContainer, ReactGridLayout } from './styles';
|
||||||
import { removeUndefinedValuesFromLayout } from './utils';
|
import { removeUndefinedValuesFromLayout } from './utils';
|
||||||
|
import { MenuItemKeys } from './WidgetHeader/contants';
|
||||||
import { WidgetRowHeader } from './WidgetRow';
|
import { WidgetRowHeader } from './WidgetRow';
|
||||||
|
|
||||||
interface GraphLayoutProps {
|
interface GraphLayoutProps {
|
||||||
@@ -190,7 +191,7 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
|
|||||||
|
|
||||||
const widgetActions = !isDashboardLocked
|
const widgetActions = !isDashboardLocked
|
||||||
? [...ViewMenuAction, ...EditMenuAction]
|
? [...ViewMenuAction, ...EditMenuAction]
|
||||||
: [...ViewMenuAction];
|
: [...ViewMenuAction, MenuItemKeys.CreateAlerts];
|
||||||
|
|
||||||
const handleLayoutChange = (layout: Layout[]): void => {
|
const handleLayoutChange = (layout: Layout[]): void => {
|
||||||
const filterLayout = removeUndefinedValuesFromLayout(layout);
|
const filterLayout = removeUndefinedValuesFromLayout(layout);
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
import './InfraMonitoring.styles.scss';
|
import './InfraMonitoring.styles.scss';
|
||||||
|
|
||||||
|
import { initialQueriesMap } from 'constants/queryBuilder';
|
||||||
import { K8sCategory } from 'container/InfraMonitoringK8s/constants';
|
import { K8sCategory } from 'container/InfraMonitoringK8s/constants';
|
||||||
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
|
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
|
||||||
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
|
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
function HostsListControls({
|
function HostsListControls({
|
||||||
handleFiltersChange,
|
handleFiltersChange,
|
||||||
}: {
|
}: {
|
||||||
handleFiltersChange: (value: IBuilderQuery['filters']) => void;
|
handleFiltersChange: (value: IBuilderQuery['filters']) => void;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const { currentQuery } = useQueryBuilder();
|
const currentQuery = initialQueriesMap[DataSource.METRICS];
|
||||||
const updatedCurrentQuery = useMemo(
|
const updatedCurrentQuery = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...currentQuery,
|
...currentQuery,
|
||||||
|
|||||||
@@ -141,13 +141,9 @@ function ClusterDetails({
|
|||||||
[cluster?.meta.k8s_cluster_name],
|
[cluster?.meta.k8s_cluster_name],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [logFilters, setLogFilters] = useState<IBuilderQuery['filters']>(
|
const [logsAndTracesFilters, setLogsAndTracesFilters] = useState<
|
||||||
initialFilters,
|
IBuilderQuery['filters']
|
||||||
);
|
>(initialFilters);
|
||||||
|
|
||||||
const [tracesFilters, setTracesFilters] = useState<IBuilderQuery['filters']>(
|
|
||||||
initialFilters,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [eventsFilters, setEventsFilters] = useState<IBuilderQuery['filters']>(
|
const [eventsFilters, setEventsFilters] = useState<IBuilderQuery['filters']>(
|
||||||
initialEventsFilters,
|
initialEventsFilters,
|
||||||
@@ -161,8 +157,7 @@ function ClusterDetails({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLogFilters(initialFilters);
|
setLogsAndTracesFilters(initialFilters);
|
||||||
setTracesFilters(initialFilters);
|
|
||||||
setEventsFilters(initialEventsFilters);
|
setEventsFilters(initialEventsFilters);
|
||||||
}, [initialFilters, initialEventsFilters]);
|
}, [initialFilters, initialEventsFilters]);
|
||||||
|
|
||||||
@@ -181,6 +176,10 @@ function ClusterDetails({
|
|||||||
|
|
||||||
const handleTabChange = (e: RadioChangeEvent): void => {
|
const handleTabChange = (e: RadioChangeEvent): void => {
|
||||||
setSelectedView(e.target.value);
|
setSelectedView(e.target.value);
|
||||||
|
logEvent('Infra Monitoring: Clusters list details tab changed', {
|
||||||
|
cluster: cluster?.clusterUID,
|
||||||
|
view: e.target.value,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTimeChange = useCallback(
|
const handleTimeChange = useCallback(
|
||||||
@@ -204,6 +203,7 @@ function ClusterDetails({
|
|||||||
logEvent('Infra Monitoring: Clusters list details time updated', {
|
logEvent('Infra Monitoring: Clusters list details time updated', {
|
||||||
cluster: cluster?.clusterUID,
|
cluster: cluster?.clusterUID,
|
||||||
interval,
|
interval,
|
||||||
|
view: selectedView,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -212,7 +212,7 @@ function ClusterDetails({
|
|||||||
|
|
||||||
const handleChangeLogFilters = useCallback(
|
const handleChangeLogFilters = useCallback(
|
||||||
(value: IBuilderQuery['filters']) => {
|
(value: IBuilderQuery['filters']) => {
|
||||||
setLogFilters((prevFilters) => {
|
setLogsAndTracesFilters((prevFilters) => {
|
||||||
const primaryFilters = prevFilters.items.filter((item) =>
|
const primaryFilters = prevFilters.items.filter((item) =>
|
||||||
[QUERY_KEYS.K8S_CLUSTER_NAME].includes(item.key?.key ?? ''),
|
[QUERY_KEYS.K8S_CLUSTER_NAME].includes(item.key?.key ?? ''),
|
||||||
);
|
);
|
||||||
@@ -244,7 +244,7 @@ function ClusterDetails({
|
|||||||
|
|
||||||
const handleChangeTracesFilters = useCallback(
|
const handleChangeTracesFilters = useCallback(
|
||||||
(value: IBuilderQuery['filters']) => {
|
(value: IBuilderQuery['filters']) => {
|
||||||
setTracesFilters((prevFilters) => {
|
setLogsAndTracesFilters((prevFilters) => {
|
||||||
const primaryFilters = prevFilters.items.filter((item) =>
|
const primaryFilters = prevFilters.items.filter((item) =>
|
||||||
[QUERY_KEYS.K8S_CLUSTER_NAME].includes(item.key?.key ?? ''),
|
[QUERY_KEYS.K8S_CLUSTER_NAME].includes(item.key?.key ?? ''),
|
||||||
);
|
);
|
||||||
@@ -320,8 +320,8 @@ function ClusterDetails({
|
|||||||
|
|
||||||
if (selectedView === VIEW_TYPES.LOGS) {
|
if (selectedView === VIEW_TYPES.LOGS) {
|
||||||
const filtersWithoutPagination = {
|
const filtersWithoutPagination = {
|
||||||
...logFilters,
|
...logsAndTracesFilters,
|
||||||
items: logFilters.items.filter((item) => item.key?.key !== 'id'),
|
items: logsAndTracesFilters.items.filter((item) => item.key?.key !== 'id'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const compositeQuery = {
|
const compositeQuery = {
|
||||||
@@ -355,7 +355,7 @@ function ClusterDetails({
|
|||||||
{
|
{
|
||||||
...initialQueryBuilderFormValuesMap.traces,
|
...initialQueryBuilderFormValuesMap.traces,
|
||||||
aggregateOperator: TracesAggregatorOperator.NOOP,
|
aggregateOperator: TracesAggregatorOperator.NOOP,
|
||||||
filters: tracesFilters,
|
filters: logsAndTracesFilters,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -519,7 +519,7 @@ function ClusterDetails({
|
|||||||
isModalTimeSelection={isModalTimeSelection}
|
isModalTimeSelection={isModalTimeSelection}
|
||||||
handleTimeChange={handleTimeChange}
|
handleTimeChange={handleTimeChange}
|
||||||
handleChangeLogFilters={handleChangeLogFilters}
|
handleChangeLogFilters={handleChangeLogFilters}
|
||||||
logFilters={logFilters}
|
logFilters={logsAndTracesFilters}
|
||||||
selectedInterval={selectedInterval}
|
selectedInterval={selectedInterval}
|
||||||
queryKey="clusterLogs"
|
queryKey="clusterLogs"
|
||||||
category={K8sCategory.CLUSTERS}
|
category={K8sCategory.CLUSTERS}
|
||||||
@@ -532,9 +532,10 @@ function ClusterDetails({
|
|||||||
isModalTimeSelection={isModalTimeSelection}
|
isModalTimeSelection={isModalTimeSelection}
|
||||||
handleTimeChange={handleTimeChange}
|
handleTimeChange={handleTimeChange}
|
||||||
handleChangeTracesFilters={handleChangeTracesFilters}
|
handleChangeTracesFilters={handleChangeTracesFilters}
|
||||||
tracesFilters={tracesFilters}
|
tracesFilters={logsAndTracesFilters}
|
||||||
selectedInterval={selectedInterval}
|
selectedInterval={selectedInterval}
|
||||||
queryKey="clusterTraces"
|
queryKey="clusterTraces"
|
||||||
|
queryKeyFilters={[QUERY_KEYS.K8S_CLUSTER_NAME]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{selectedView === VIEW_TYPES.EVENTS && (
|
{selectedView === VIEW_TYPES.EVENTS && (
|
||||||
|
|||||||
@@ -240,6 +240,11 @@ function K8sClustersList({
|
|||||||
}
|
}
|
||||||
}, [selectedRowData, fetchGroupedByRowData]);
|
}, [selectedRowData, fetchGroupedByRowData]);
|
||||||
|
|
||||||
|
const numberOfPages = useMemo(() => Math.ceil(totalCount / pageSize), [
|
||||||
|
totalCount,
|
||||||
|
pageSize,
|
||||||
|
]);
|
||||||
|
|
||||||
const handleTableChange: TableProps<K8sClustersRowData>['onChange'] = useCallback(
|
const handleTableChange: TableProps<K8sClustersRowData>['onChange'] = useCallback(
|
||||||
(
|
(
|
||||||
pagination: TablePaginationConfig,
|
pagination: TablePaginationConfig,
|
||||||
@@ -250,6 +255,11 @@ function K8sClustersList({
|
|||||||
): void => {
|
): void => {
|
||||||
if (pagination.current) {
|
if (pagination.current) {
|
||||||
setCurrentPage(pagination.current);
|
setCurrentPage(pagination.current);
|
||||||
|
logEvent('Infra Monitoring: K8s clusters list page number changed', {
|
||||||
|
page: pagination.current,
|
||||||
|
pageSize,
|
||||||
|
numberOfPages,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('field' in sorter && sorter.order) {
|
if ('field' in sorter && sorter.order) {
|
||||||
@@ -261,7 +271,7 @@ function K8sClustersList({
|
|||||||
setOrderBy(null);
|
setOrderBy(null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[],
|
[numberOfPages, pageSize],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { handleChangeQueryData } = useQueryOperations({
|
const { handleChangeQueryData } = useQueryOperations({
|
||||||
@@ -275,15 +285,13 @@ function K8sClustersList({
|
|||||||
handleChangeQueryData('filters', value);
|
handleChangeQueryData('filters', value);
|
||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
|
|
||||||
logEvent('Infra Monitoring: K8s list filters applied', {
|
logEvent('Infra Monitoring: K8s clusters list filters applied', {});
|
||||||
filters: value,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[handleChangeQueryData],
|
[handleChangeQueryData],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
logEvent('Infra Monitoring: K8s list page visited', {});
|
logEvent('Infra Monitoring: K8s clusters list page visited', {});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const selectedClusterData = useMemo(() => {
|
const selectedClusterData = useMemo(() => {
|
||||||
@@ -442,6 +450,7 @@ function K8sClustersList({
|
|||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
setGroupBy(groupBy);
|
setGroupBy(groupBy);
|
||||||
setExpandedRowKeys([]);
|
setExpandedRowKeys([]);
|
||||||
|
logEvent('Infra Monitoring: K8s clusters list group by changed', {});
|
||||||
},
|
},
|
||||||
[groupByFiltersData],
|
[groupByFiltersData],
|
||||||
);
|
);
|
||||||
@@ -457,6 +466,16 @@ function K8sClustersList({
|
|||||||
}
|
}
|
||||||
}, [groupByFiltersData]);
|
}, [groupByFiltersData]);
|
||||||
|
|
||||||
|
const onPaginationChange = (page: number, pageSize: number): void => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
setPageSize(pageSize);
|
||||||
|
logEvent('Infra Monitoring: K8s clusters list page number changed', {
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
numberOfPages,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="k8s-list">
|
<div className="k8s-list">
|
||||||
<K8sHeader
|
<K8sHeader
|
||||||
@@ -482,10 +501,7 @@ function K8sClustersList({
|
|||||||
total: totalCount,
|
total: totalCount,
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
hideOnSinglePage: false,
|
hideOnSinglePage: false,
|
||||||
onChange: (page, pageSize): void => {
|
onChange: onPaginationChange,
|
||||||
setCurrentPage(page);
|
|
||||||
setPageSize(pageSize);
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
scroll={{ x: true }}
|
scroll={{ x: true }}
|
||||||
loading={{
|
loading={{
|
||||||
|
|||||||
@@ -155,13 +155,9 @@ function DaemonSetDetails({
|
|||||||
[daemonSet?.meta.k8s_daemonset_name],
|
[daemonSet?.meta.k8s_daemonset_name],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [logFilters, setLogFilters] = useState<IBuilderQuery['filters']>(
|
const [logAndTracesFilters, setLogAndTracesFilters] = useState<
|
||||||
initialFilters,
|
IBuilderQuery['filters']
|
||||||
);
|
>(initialFilters);
|
||||||
|
|
||||||
const [tracesFilters, setTracesFilters] = useState<IBuilderQuery['filters']>(
|
|
||||||
initialFilters,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [eventsFilters, setEventsFilters] = useState<IBuilderQuery['filters']>(
|
const [eventsFilters, setEventsFilters] = useState<IBuilderQuery['filters']>(
|
||||||
initialEventsFilters,
|
initialEventsFilters,
|
||||||
@@ -175,8 +171,7 @@ function DaemonSetDetails({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLogFilters(initialFilters);
|
setLogAndTracesFilters(initialFilters);
|
||||||
setTracesFilters(initialFilters);
|
|
||||||
setEventsFilters(initialEventsFilters);
|
setEventsFilters(initialEventsFilters);
|
||||||
}, [initialFilters, initialEventsFilters]);
|
}, [initialFilters, initialEventsFilters]);
|
||||||
|
|
||||||
@@ -195,6 +190,10 @@ function DaemonSetDetails({
|
|||||||
|
|
||||||
const handleTabChange = (e: RadioChangeEvent): void => {
|
const handleTabChange = (e: RadioChangeEvent): void => {
|
||||||
setSelectedView(e.target.value);
|
setSelectedView(e.target.value);
|
||||||
|
logEvent('Infra Monitoring: DaemonSets list details tab changed', {
|
||||||
|
daemonSet: daemonSet?.daemonSetName,
|
||||||
|
view: e.target.value,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTimeChange = useCallback(
|
const handleTimeChange = useCallback(
|
||||||
@@ -218,6 +217,7 @@ function DaemonSetDetails({
|
|||||||
logEvent('Infra Monitoring: DaemonSets list details time updated', {
|
logEvent('Infra Monitoring: DaemonSets list details time updated', {
|
||||||
daemonSet: daemonSet?.daemonSetName,
|
daemonSet: daemonSet?.daemonSetName,
|
||||||
interval,
|
interval,
|
||||||
|
view: selectedView,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -226,7 +226,7 @@ function DaemonSetDetails({
|
|||||||
|
|
||||||
const handleChangeLogFilters = useCallback(
|
const handleChangeLogFilters = useCallback(
|
||||||
(value: IBuilderQuery['filters']) => {
|
(value: IBuilderQuery['filters']) => {
|
||||||
setLogFilters((prevFilters) => {
|
setLogAndTracesFilters((prevFilters) => {
|
||||||
const primaryFilters = prevFilters.items.filter((item) =>
|
const primaryFilters = prevFilters.items.filter((item) =>
|
||||||
[QUERY_KEYS.K8S_DAEMON_SET_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes(
|
[QUERY_KEYS.K8S_DAEMON_SET_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes(
|
||||||
item.key?.key ?? '',
|
item.key?.key ?? '',
|
||||||
@@ -259,7 +259,7 @@ function DaemonSetDetails({
|
|||||||
|
|
||||||
const handleChangeTracesFilters = useCallback(
|
const handleChangeTracesFilters = useCallback(
|
||||||
(value: IBuilderQuery['filters']) => {
|
(value: IBuilderQuery['filters']) => {
|
||||||
setTracesFilters((prevFilters) => {
|
setLogAndTracesFilters((prevFilters) => {
|
||||||
const primaryFilters = prevFilters.items.filter((item) =>
|
const primaryFilters = prevFilters.items.filter((item) =>
|
||||||
[QUERY_KEYS.K8S_DAEMON_SET_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes(
|
[QUERY_KEYS.K8S_DAEMON_SET_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes(
|
||||||
item.key?.key ?? '',
|
item.key?.key ?? '',
|
||||||
@@ -339,8 +339,8 @@ function DaemonSetDetails({
|
|||||||
|
|
||||||
if (selectedView === VIEW_TYPES.LOGS) {
|
if (selectedView === VIEW_TYPES.LOGS) {
|
||||||
const filtersWithoutPagination = {
|
const filtersWithoutPagination = {
|
||||||
...logFilters,
|
...logAndTracesFilters,
|
||||||
items: logFilters.items.filter((item) => item.key?.key !== 'id'),
|
items: logAndTracesFilters.items.filter((item) => item.key?.key !== 'id'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const compositeQuery = {
|
const compositeQuery = {
|
||||||
@@ -374,7 +374,7 @@ function DaemonSetDetails({
|
|||||||
{
|
{
|
||||||
...initialQueryBuilderFormValuesMap.traces,
|
...initialQueryBuilderFormValuesMap.traces,
|
||||||
aggregateOperator: TracesAggregatorOperator.NOOP,
|
aggregateOperator: TracesAggregatorOperator.NOOP,
|
||||||
filters: tracesFilters,
|
filters: logAndTracesFilters,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -551,7 +551,7 @@ function DaemonSetDetails({
|
|||||||
isModalTimeSelection={isModalTimeSelection}
|
isModalTimeSelection={isModalTimeSelection}
|
||||||
handleTimeChange={handleTimeChange}
|
handleTimeChange={handleTimeChange}
|
||||||
handleChangeLogFilters={handleChangeLogFilters}
|
handleChangeLogFilters={handleChangeLogFilters}
|
||||||
logFilters={logFilters}
|
logFilters={logAndTracesFilters}
|
||||||
selectedInterval={selectedInterval}
|
selectedInterval={selectedInterval}
|
||||||
category={K8sCategory.DAEMONSETS}
|
category={K8sCategory.DAEMONSETS}
|
||||||
queryKey="daemonsetLogs"
|
queryKey="daemonsetLogs"
|
||||||
@@ -567,9 +567,13 @@ function DaemonSetDetails({
|
|||||||
isModalTimeSelection={isModalTimeSelection}
|
isModalTimeSelection={isModalTimeSelection}
|
||||||
handleTimeChange={handleTimeChange}
|
handleTimeChange={handleTimeChange}
|
||||||
handleChangeTracesFilters={handleChangeTracesFilters}
|
handleChangeTracesFilters={handleChangeTracesFilters}
|
||||||
tracesFilters={tracesFilters}
|
tracesFilters={logAndTracesFilters}
|
||||||
selectedInterval={selectedInterval}
|
selectedInterval={selectedInterval}
|
||||||
queryKey="daemonsetTraces"
|
queryKey="daemonsetTraces"
|
||||||
|
queryKeyFilters={[
|
||||||
|
QUERY_KEYS.K8S_DAEMON_SET_NAME,
|
||||||
|
QUERY_KEYS.K8S_NAMESPACE_NAME,
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{selectedView === VIEW_TYPES.EVENTS && (
|
{selectedView === VIEW_TYPES.EVENTS && (
|
||||||
|
|||||||
@@ -243,6 +243,11 @@ function K8sDaemonSetsList({
|
|||||||
}
|
}
|
||||||
}, [selectedRowData, fetchGroupedByRowData]);
|
}, [selectedRowData, fetchGroupedByRowData]);
|
||||||
|
|
||||||
|
const numberOfPages = useMemo(() => Math.ceil(totalCount / pageSize), [
|
||||||
|
totalCount,
|
||||||
|
pageSize,
|
||||||
|
]);
|
||||||
|
|
||||||
const handleTableChange: TableProps<K8sDaemonSetsRowData>['onChange'] = useCallback(
|
const handleTableChange: TableProps<K8sDaemonSetsRowData>['onChange'] = useCallback(
|
||||||
(
|
(
|
||||||
pagination: TablePaginationConfig,
|
pagination: TablePaginationConfig,
|
||||||
@@ -253,6 +258,11 @@ function K8sDaemonSetsList({
|
|||||||
): void => {
|
): void => {
|
||||||
if (pagination.current) {
|
if (pagination.current) {
|
||||||
setCurrentPage(pagination.current);
|
setCurrentPage(pagination.current);
|
||||||
|
logEvent('Infra Monitoring: K8s daemonSets list page number changed', {
|
||||||
|
page: pagination.current,
|
||||||
|
pageSize,
|
||||||
|
numberOfPages,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('field' in sorter && sorter.order) {
|
if ('field' in sorter && sorter.order) {
|
||||||
@@ -264,7 +274,7 @@ function K8sDaemonSetsList({
|
|||||||
setOrderBy(null);
|
setOrderBy(null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[],
|
[numberOfPages, pageSize],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { handleChangeQueryData } = useQueryOperations({
|
const { handleChangeQueryData } = useQueryOperations({
|
||||||
@@ -278,15 +288,13 @@ function K8sDaemonSetsList({
|
|||||||
handleChangeQueryData('filters', value);
|
handleChangeQueryData('filters', value);
|
||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
|
|
||||||
logEvent('Infra Monitoring: K8s list filters applied', {
|
logEvent('Infra Monitoring: K8s daemonSets list filters applied', {});
|
||||||
filters: value,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[handleChangeQueryData],
|
[handleChangeQueryData],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
logEvent('Infra Monitoring: K8s list page visited', {});
|
logEvent('Infra Monitoring: K8s daemonSets list page visited', {});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const selectedDaemonSetData = useMemo(() => {
|
const selectedDaemonSetData = useMemo(() => {
|
||||||
@@ -448,6 +456,8 @@ function K8sDaemonSetsList({
|
|||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
setGroupBy(groupBy);
|
setGroupBy(groupBy);
|
||||||
setExpandedRowKeys([]);
|
setExpandedRowKeys([]);
|
||||||
|
|
||||||
|
logEvent('Infra Monitoring: K8s daemonSets list group by changed', {});
|
||||||
},
|
},
|
||||||
[groupByFiltersData],
|
[groupByFiltersData],
|
||||||
);
|
);
|
||||||
@@ -463,6 +473,16 @@ function K8sDaemonSetsList({
|
|||||||
}
|
}
|
||||||
}, [groupByFiltersData]);
|
}, [groupByFiltersData]);
|
||||||
|
|
||||||
|
const onPaginationChange = (page: number, pageSize: number): void => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
setPageSize(pageSize);
|
||||||
|
logEvent('Infra Monitoring: K8s daemonSets list page number changed', {
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
numberOfPages,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="k8s-list">
|
<div className="k8s-list">
|
||||||
<K8sHeader
|
<K8sHeader
|
||||||
@@ -490,10 +510,7 @@ function K8sDaemonSetsList({
|
|||||||
total: totalCount,
|
total: totalCount,
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
hideOnSinglePage: false,
|
hideOnSinglePage: false,
|
||||||
onChange: (page, pageSize): void => {
|
onChange: onPaginationChange,
|
||||||
setCurrentPage(page);
|
|
||||||
setPageSize(pageSize);
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
scroll={{ x: true }}
|
scroll={{ x: true }}
|
||||||
loading={{
|
loading={{
|
||||||
|
|||||||
@@ -157,13 +157,9 @@ function DeploymentDetails({
|
|||||||
[deployment?.meta.k8s_deployment_name],
|
[deployment?.meta.k8s_deployment_name],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [logFilters, setLogFilters] = useState<IBuilderQuery['filters']>(
|
const [logAndTracesFilters, setLogAndTracesFilters] = useState<
|
||||||
initialFilters,
|
IBuilderQuery['filters']
|
||||||
);
|
>(initialFilters);
|
||||||
|
|
||||||
const [tracesFilters, setTracesFilters] = useState<IBuilderQuery['filters']>(
|
|
||||||
initialFilters,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [eventsFilters, setEventsFilters] = useState<IBuilderQuery['filters']>(
|
const [eventsFilters, setEventsFilters] = useState<IBuilderQuery['filters']>(
|
||||||
initialEventsFilters,
|
initialEventsFilters,
|
||||||
@@ -177,8 +173,7 @@ function DeploymentDetails({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLogFilters(initialFilters);
|
setLogAndTracesFilters(initialFilters);
|
||||||
setTracesFilters(initialFilters);
|
|
||||||
setEventsFilters(initialEventsFilters);
|
setEventsFilters(initialEventsFilters);
|
||||||
}, [initialFilters, initialEventsFilters]);
|
}, [initialFilters, initialEventsFilters]);
|
||||||
|
|
||||||
@@ -197,6 +192,10 @@ function DeploymentDetails({
|
|||||||
|
|
||||||
const handleTabChange = (e: RadioChangeEvent): void => {
|
const handleTabChange = (e: RadioChangeEvent): void => {
|
||||||
setSelectedView(e.target.value);
|
setSelectedView(e.target.value);
|
||||||
|
logEvent('Infra Monitoring: Deployments list details tab changed', {
|
||||||
|
deployment: deployment?.deploymentName,
|
||||||
|
view: e.target.value,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTimeChange = useCallback(
|
const handleTimeChange = useCallback(
|
||||||
@@ -220,6 +219,7 @@ function DeploymentDetails({
|
|||||||
logEvent('Infra Monitoring: Deployments list details time updated', {
|
logEvent('Infra Monitoring: Deployments list details time updated', {
|
||||||
deployment: deployment?.deploymentName,
|
deployment: deployment?.deploymentName,
|
||||||
interval,
|
interval,
|
||||||
|
view: selectedView,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -228,7 +228,7 @@ function DeploymentDetails({
|
|||||||
|
|
||||||
const handleChangeLogFilters = useCallback(
|
const handleChangeLogFilters = useCallback(
|
||||||
(value: IBuilderQuery['filters']) => {
|
(value: IBuilderQuery['filters']) => {
|
||||||
setLogFilters((prevFilters) => {
|
setLogAndTracesFilters((prevFilters) => {
|
||||||
const primaryFilters = prevFilters.items.filter((item) =>
|
const primaryFilters = prevFilters.items.filter((item) =>
|
||||||
[QUERY_KEYS.K8S_DEPLOYMENT_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes(
|
[QUERY_KEYS.K8S_DEPLOYMENT_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes(
|
||||||
item.key?.key ?? '',
|
item.key?.key ?? '',
|
||||||
@@ -266,7 +266,7 @@ function DeploymentDetails({
|
|||||||
|
|
||||||
const handleChangeTracesFilters = useCallback(
|
const handleChangeTracesFilters = useCallback(
|
||||||
(value: IBuilderQuery['filters']) => {
|
(value: IBuilderQuery['filters']) => {
|
||||||
setTracesFilters((prevFilters) => {
|
setLogAndTracesFilters((prevFilters) => {
|
||||||
const primaryFilters = prevFilters.items.filter((item) =>
|
const primaryFilters = prevFilters.items.filter((item) =>
|
||||||
[QUERY_KEYS.K8S_DEPLOYMENT_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes(
|
[QUERY_KEYS.K8S_DEPLOYMENT_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes(
|
||||||
item.key?.key ?? '',
|
item.key?.key ?? '',
|
||||||
@@ -350,8 +350,8 @@ function DeploymentDetails({
|
|||||||
|
|
||||||
if (selectedView === VIEW_TYPES.LOGS) {
|
if (selectedView === VIEW_TYPES.LOGS) {
|
||||||
const filtersWithoutPagination = {
|
const filtersWithoutPagination = {
|
||||||
...logFilters,
|
...logAndTracesFilters,
|
||||||
items: logFilters.items.filter((item) => item.key?.key !== 'id'),
|
items: logAndTracesFilters.items.filter((item) => item.key?.key !== 'id'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const compositeQuery = {
|
const compositeQuery = {
|
||||||
@@ -385,7 +385,7 @@ function DeploymentDetails({
|
|||||||
{
|
{
|
||||||
...initialQueryBuilderFormValuesMap.traces,
|
...initialQueryBuilderFormValuesMap.traces,
|
||||||
aggregateOperator: TracesAggregatorOperator.NOOP,
|
aggregateOperator: TracesAggregatorOperator.NOOP,
|
||||||
filters: tracesFilters,
|
filters: logAndTracesFilters,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -562,7 +562,7 @@ function DeploymentDetails({
|
|||||||
isModalTimeSelection={isModalTimeSelection}
|
isModalTimeSelection={isModalTimeSelection}
|
||||||
handleTimeChange={handleTimeChange}
|
handleTimeChange={handleTimeChange}
|
||||||
handleChangeLogFilters={handleChangeLogFilters}
|
handleChangeLogFilters={handleChangeLogFilters}
|
||||||
logFilters={logFilters}
|
logFilters={logAndTracesFilters}
|
||||||
selectedInterval={selectedInterval}
|
selectedInterval={selectedInterval}
|
||||||
queryKeyFilters={[
|
queryKeyFilters={[
|
||||||
QUERY_KEYS.K8S_DEPLOYMENT_NAME,
|
QUERY_KEYS.K8S_DEPLOYMENT_NAME,
|
||||||
@@ -578,9 +578,13 @@ function DeploymentDetails({
|
|||||||
isModalTimeSelection={isModalTimeSelection}
|
isModalTimeSelection={isModalTimeSelection}
|
||||||
handleTimeChange={handleTimeChange}
|
handleTimeChange={handleTimeChange}
|
||||||
handleChangeTracesFilters={handleChangeTracesFilters}
|
handleChangeTracesFilters={handleChangeTracesFilters}
|
||||||
tracesFilters={tracesFilters}
|
tracesFilters={logAndTracesFilters}
|
||||||
selectedInterval={selectedInterval}
|
selectedInterval={selectedInterval}
|
||||||
queryKey="deploymentTraces"
|
queryKey="deploymentTraces"
|
||||||
|
queryKeyFilters={[
|
||||||
|
QUERY_KEYS.K8S_DEPLOYMENT_NAME,
|
||||||
|
QUERY_KEYS.K8S_NAMESPACE_NAME,
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{selectedView === VIEW_TYPES.EVENTS && (
|
{selectedView === VIEW_TYPES.EVENTS && (
|
||||||
|
|||||||
@@ -245,6 +245,11 @@ function K8sDeploymentsList({
|
|||||||
}
|
}
|
||||||
}, [selectedRowData, fetchGroupedByRowData]);
|
}, [selectedRowData, fetchGroupedByRowData]);
|
||||||
|
|
||||||
|
const numberOfPages = useMemo(() => Math.ceil(totalCount / pageSize), [
|
||||||
|
totalCount,
|
||||||
|
pageSize,
|
||||||
|
]);
|
||||||
|
|
||||||
const handleTableChange: TableProps<K8sDeploymentsRowData>['onChange'] = useCallback(
|
const handleTableChange: TableProps<K8sDeploymentsRowData>['onChange'] = useCallback(
|
||||||
(
|
(
|
||||||
pagination: TablePaginationConfig,
|
pagination: TablePaginationConfig,
|
||||||
@@ -255,6 +260,11 @@ function K8sDeploymentsList({
|
|||||||
): void => {
|
): void => {
|
||||||
if (pagination.current) {
|
if (pagination.current) {
|
||||||
setCurrentPage(pagination.current);
|
setCurrentPage(pagination.current);
|
||||||
|
logEvent('Infra Monitoring: K8s deployments list page number changed', {
|
||||||
|
page: pagination.current,
|
||||||
|
pageSize,
|
||||||
|
numberOfPages,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('field' in sorter && sorter.order) {
|
if ('field' in sorter && sorter.order) {
|
||||||
@@ -266,7 +276,7 @@ function K8sDeploymentsList({
|
|||||||
setOrderBy(null);
|
setOrderBy(null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[],
|
[numberOfPages, pageSize],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { handleChangeQueryData } = useQueryOperations({
|
const { handleChangeQueryData } = useQueryOperations({
|
||||||
@@ -280,15 +290,13 @@ function K8sDeploymentsList({
|
|||||||
handleChangeQueryData('filters', value);
|
handleChangeQueryData('filters', value);
|
||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
|
|
||||||
logEvent('Infra Monitoring: K8s list filters applied', {
|
logEvent('Infra Monitoring: K8s deployments list filters applied', {});
|
||||||
filters: value,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[handleChangeQueryData],
|
[handleChangeQueryData],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
logEvent('Infra Monitoring: K8s list page visited', {});
|
logEvent('Infra Monitoring: K8s deployments list page visited', {});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const selectedDeploymentData = useMemo(() => {
|
const selectedDeploymentData = useMemo(() => {
|
||||||
@@ -452,6 +460,8 @@ function K8sDeploymentsList({
|
|||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
setGroupBy(groupBy);
|
setGroupBy(groupBy);
|
||||||
setExpandedRowKeys([]);
|
setExpandedRowKeys([]);
|
||||||
|
|
||||||
|
logEvent('Infra Monitoring: K8s deployments list group by changed', {});
|
||||||
},
|
},
|
||||||
[groupByFiltersData],
|
[groupByFiltersData],
|
||||||
);
|
);
|
||||||
@@ -467,6 +477,16 @@ function K8sDeploymentsList({
|
|||||||
}
|
}
|
||||||
}, [groupByFiltersData]);
|
}, [groupByFiltersData]);
|
||||||
|
|
||||||
|
const onPaginationChange = (page: number, pageSize: number): void => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
setPageSize(pageSize);
|
||||||
|
logEvent('Infra Monitoring: K8s deployments list page number changed', {
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
numberOfPages,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="k8s-list">
|
<div className="k8s-list">
|
||||||
<K8sHeader
|
<K8sHeader
|
||||||
@@ -494,10 +514,7 @@ function K8sDeploymentsList({
|
|||||||
total: totalCount,
|
total: totalCount,
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
hideOnSinglePage: false,
|
hideOnSinglePage: false,
|
||||||
onChange: (page, pageSize): void => {
|
onChange: onPaginationChange,
|
||||||
setCurrentPage(page);
|
|
||||||
setPageSize(pageSize);
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
scroll={{ x: true }}
|
scroll={{ x: true }}
|
||||||
loading={{
|
loading={{
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { useMemo } from 'react';
|
|||||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
|
import { filterOutPrimaryFilters } from '../utils';
|
||||||
import EntityLogs from './EntityLogs';
|
import EntityLogs from './EntityLogs';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -58,14 +59,14 @@ function EntityLogsDetailedView({
|
|||||||
...currentQuery.builder.queryData[0].aggregateAttribute,
|
...currentQuery.builder.queryData[0].aggregateAttribute,
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
items: [],
|
items: filterOutPrimaryFilters(logFilters.items, queryKeyFilters),
|
||||||
op: 'AND',
|
op: 'AND',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
[currentQuery],
|
[currentQuery, logFilters.items, queryKeyFilters],
|
||||||
);
|
);
|
||||||
|
|
||||||
const query = updatedCurrentQuery?.builder?.queryData[0] || null;
|
const query = updatedCurrentQuery?.builder?.queryData[0] || null;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
|||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
filterOutPrimaryFilters,
|
||||||
getEntityTracesQueryPayload,
|
getEntityTracesQueryPayload,
|
||||||
selectedEntityTracesColumns,
|
selectedEntityTracesColumns,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
@@ -44,6 +45,7 @@ interface Props {
|
|||||||
tracesFilters: IBuilderQuery['filters'];
|
tracesFilters: IBuilderQuery['filters'];
|
||||||
selectedInterval: Time;
|
selectedInterval: Time;
|
||||||
queryKey: string;
|
queryKey: string;
|
||||||
|
queryKeyFilters: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function EntityTraces({
|
function EntityTraces({
|
||||||
@@ -54,6 +56,7 @@ function EntityTraces({
|
|||||||
tracesFilters,
|
tracesFilters,
|
||||||
selectedInterval,
|
selectedInterval,
|
||||||
queryKey,
|
queryKey,
|
||||||
|
queryKeyFilters,
|
||||||
}: Props): JSX.Element {
|
}: Props): JSX.Element {
|
||||||
const [traces, setTraces] = useState<any[]>([]);
|
const [traces, setTraces] = useState<any[]>([]);
|
||||||
const [offset] = useState<number>(0);
|
const [offset] = useState<number>(0);
|
||||||
@@ -73,14 +76,14 @@ function EntityTraces({
|
|||||||
...currentQuery.builder.queryData[0].aggregateAttribute,
|
...currentQuery.builder.queryData[0].aggregateAttribute,
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
items: [],
|
items: filterOutPrimaryFilters(tracesFilters.items, queryKeyFilters),
|
||||||
op: 'AND',
|
op: 'AND',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
[currentQuery],
|
[currentQuery, queryKeyFilters, tracesFilters.items],
|
||||||
);
|
);
|
||||||
|
|
||||||
const query = updatedCurrentQuery?.builder?.queryData[0] || null;
|
const query = updatedCurrentQuery?.builder?.queryData[0] || null;
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ import {
|
|||||||
BaseAutocompleteData,
|
BaseAutocompleteData,
|
||||||
DataTypes,
|
DataTypes,
|
||||||
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
import {
|
||||||
|
IBuilderQuery,
|
||||||
|
TagFilterItem,
|
||||||
|
} from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { EQueryType } from 'types/common/dashboard';
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
import { nanoToMilli } from 'utils/timeUtils';
|
import { nanoToMilli } from 'utils/timeUtils';
|
||||||
@@ -301,3 +304,12 @@ export const getEntityTracesQueryPayload = (
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const filterOutPrimaryFilters = (
|
||||||
|
filters: TagFilterItem[],
|
||||||
|
primaryKeys: string[],
|
||||||
|
): TagFilterItem[] =>
|
||||||
|
filters.filter(
|
||||||
|
(filter) =>
|
||||||
|
!primaryKeys.includes(filter.key?.key ?? '') && filter.key?.key !== 'id',
|
||||||
|
);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { VerticalAlignTopOutlined } from '@ant-design/icons';
|
|||||||
import * as Sentry from '@sentry/react';
|
import * as Sentry from '@sentry/react';
|
||||||
import type { CollapseProps } from 'antd';
|
import type { CollapseProps } from 'antd';
|
||||||
import { Collapse, Tooltip, Typography } from 'antd';
|
import { Collapse, Tooltip, Typography } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import QuickFilters from 'components/QuickFilters/QuickFilters';
|
import QuickFilters from 'components/QuickFilters/QuickFilters';
|
||||||
import { QuickFiltersSource } from 'components/QuickFilters/types';
|
import { QuickFiltersSource } from 'components/QuickFilters/types';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
@@ -68,6 +69,11 @@ export default function InfraMonitoringK8s(): JSX.Element {
|
|||||||
// in infra monitoring k8s, we are using only one query, hence updating the 0th index of queryData
|
// in infra monitoring k8s, we are using only one query, hence updating the 0th index of queryData
|
||||||
handleChangeQueryData('filters', query.builder.queryData[0].filters);
|
handleChangeQueryData('filters', query.builder.queryData[0].filters);
|
||||||
setQuickFiltersLastUpdated(Date.now());
|
setQuickFiltersLastUpdated(Date.now());
|
||||||
|
|
||||||
|
logEvent(
|
||||||
|
`Infra Monitoring: K8s ${selectedCategory} list quick filters applied`,
|
||||||
|
{},
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const items: CollapseProps['items'] = [
|
const items: CollapseProps['items'] = [
|
||||||
|
|||||||
@@ -152,13 +152,9 @@ function JobDetails({
|
|||||||
[job?.meta.k8s_job_name],
|
[job?.meta.k8s_job_name],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [logFilters, setLogFilters] = useState<IBuilderQuery['filters']>(
|
const [logAndTracesFilters, setLogAndTracesFilters] = useState<
|
||||||
initialFilters,
|
IBuilderQuery['filters']
|
||||||
);
|
>(initialFilters);
|
||||||
|
|
||||||
const [tracesFilters, setTracesFilters] = useState<IBuilderQuery['filters']>(
|
|
||||||
initialFilters,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [eventsFilters, setEventsFilters] = useState<IBuilderQuery['filters']>(
|
const [eventsFilters, setEventsFilters] = useState<IBuilderQuery['filters']>(
|
||||||
initialEventsFilters,
|
initialEventsFilters,
|
||||||
@@ -172,8 +168,7 @@ function JobDetails({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLogFilters(initialFilters);
|
setLogAndTracesFilters(initialFilters);
|
||||||
setTracesFilters(initialFilters);
|
|
||||||
setEventsFilters(initialEventsFilters);
|
setEventsFilters(initialEventsFilters);
|
||||||
}, [initialFilters, initialEventsFilters]);
|
}, [initialFilters, initialEventsFilters]);
|
||||||
|
|
||||||
@@ -192,6 +187,10 @@ function JobDetails({
|
|||||||
|
|
||||||
const handleTabChange = (e: RadioChangeEvent): void => {
|
const handleTabChange = (e: RadioChangeEvent): void => {
|
||||||
setSelectedView(e.target.value);
|
setSelectedView(e.target.value);
|
||||||
|
logEvent('Infra Monitoring: Jobs list details tab changed', {
|
||||||
|
job: job?.jobName,
|
||||||
|
view: e.target.value,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTimeChange = useCallback(
|
const handleTimeChange = useCallback(
|
||||||
@@ -215,6 +214,7 @@ function JobDetails({
|
|||||||
logEvent('Infra Monitoring: Jobs list details time updated', {
|
logEvent('Infra Monitoring: Jobs list details time updated', {
|
||||||
job: job?.jobName,
|
job: job?.jobName,
|
||||||
interval,
|
interval,
|
||||||
|
view: selectedView,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -223,17 +223,16 @@ function JobDetails({
|
|||||||
|
|
||||||
const handleChangeLogFilters = useCallback(
|
const handleChangeLogFilters = useCallback(
|
||||||
(value: IBuilderQuery['filters']) => {
|
(value: IBuilderQuery['filters']) => {
|
||||||
setLogFilters((prevFilters) => {
|
setLogAndTracesFilters((prevFilters) => {
|
||||||
const primaryFilters = prevFilters.items.filter((item) =>
|
const primaryFilters = prevFilters.items.filter((item) =>
|
||||||
[QUERY_KEYS.K8S_STATEFUL_SET_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes(
|
[QUERY_KEYS.K8S_JOB_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes(
|
||||||
item.key?.key ?? '',
|
item.key?.key ?? '',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
const paginationFilter = value.items.find((item) => item.key?.key === 'id');
|
const paginationFilter = value.items.find((item) => item.key?.key === 'id');
|
||||||
const newFilters = value.items.filter(
|
const newFilters = value.items.filter(
|
||||||
(item) =>
|
(item) =>
|
||||||
item.key?.key !== 'id' &&
|
item.key?.key !== 'id' && item.key?.key !== QUERY_KEYS.K8S_JOB_NAME,
|
||||||
item.key?.key !== QUERY_KEYS.K8S_STATEFUL_SET_NAME,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
logEvent('Infra Monitoring: Jobs list details logs filters applied', {
|
logEvent('Infra Monitoring: Jobs list details logs filters applied', {
|
||||||
@@ -256,9 +255,9 @@ function JobDetails({
|
|||||||
|
|
||||||
const handleChangeTracesFilters = useCallback(
|
const handleChangeTracesFilters = useCallback(
|
||||||
(value: IBuilderQuery['filters']) => {
|
(value: IBuilderQuery['filters']) => {
|
||||||
setTracesFilters((prevFilters) => {
|
setLogAndTracesFilters((prevFilters) => {
|
||||||
const primaryFilters = prevFilters.items.filter((item) =>
|
const primaryFilters = prevFilters.items.filter((item) =>
|
||||||
[QUERY_KEYS.K8S_STATEFUL_SET_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes(
|
[QUERY_KEYS.K8S_JOB_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes(
|
||||||
item.key?.key ?? '',
|
item.key?.key ?? '',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -272,7 +271,7 @@ function JobDetails({
|
|||||||
items: [
|
items: [
|
||||||
...primaryFilters,
|
...primaryFilters,
|
||||||
...value.items.filter(
|
...value.items.filter(
|
||||||
(item) => item.key?.key !== QUERY_KEYS.K8S_STATEFUL_SET_NAME,
|
(item) => item.key?.key !== QUERY_KEYS.K8S_JOB_NAME,
|
||||||
),
|
),
|
||||||
].filter((item): item is TagFilterItem => item !== undefined),
|
].filter((item): item is TagFilterItem => item !== undefined),
|
||||||
};
|
};
|
||||||
@@ -330,8 +329,8 @@ function JobDetails({
|
|||||||
|
|
||||||
if (selectedView === VIEW_TYPES.LOGS) {
|
if (selectedView === VIEW_TYPES.LOGS) {
|
||||||
const filtersWithoutPagination = {
|
const filtersWithoutPagination = {
|
||||||
...logFilters,
|
...logAndTracesFilters,
|
||||||
items: logFilters.items.filter((item) => item.key?.key !== 'id'),
|
items: logAndTracesFilters.items.filter((item) => item.key?.key !== 'id'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const compositeQuery = {
|
const compositeQuery = {
|
||||||
@@ -365,7 +364,7 @@ function JobDetails({
|
|||||||
{
|
{
|
||||||
...initialQueryBuilderFormValuesMap.traces,
|
...initialQueryBuilderFormValuesMap.traces,
|
||||||
aggregateOperator: TracesAggregatorOperator.NOOP,
|
aggregateOperator: TracesAggregatorOperator.NOOP,
|
||||||
filters: tracesFilters,
|
filters: logAndTracesFilters,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -531,7 +530,7 @@ function JobDetails({
|
|||||||
isModalTimeSelection={isModalTimeSelection}
|
isModalTimeSelection={isModalTimeSelection}
|
||||||
handleTimeChange={handleTimeChange}
|
handleTimeChange={handleTimeChange}
|
||||||
handleChangeLogFilters={handleChangeLogFilters}
|
handleChangeLogFilters={handleChangeLogFilters}
|
||||||
logFilters={logFilters}
|
logFilters={logAndTracesFilters}
|
||||||
selectedInterval={selectedInterval}
|
selectedInterval={selectedInterval}
|
||||||
category={K8sCategory.JOBS}
|
category={K8sCategory.JOBS}
|
||||||
queryKey="jobLogs"
|
queryKey="jobLogs"
|
||||||
@@ -547,9 +546,13 @@ function JobDetails({
|
|||||||
isModalTimeSelection={isModalTimeSelection}
|
isModalTimeSelection={isModalTimeSelection}
|
||||||
handleTimeChange={handleTimeChange}
|
handleTimeChange={handleTimeChange}
|
||||||
handleChangeTracesFilters={handleChangeTracesFilters}
|
handleChangeTracesFilters={handleChangeTracesFilters}
|
||||||
tracesFilters={tracesFilters}
|
tracesFilters={logAndTracesFilters}
|
||||||
selectedInterval={selectedInterval}
|
selectedInterval={selectedInterval}
|
||||||
queryKey="jobTraces"
|
queryKey="jobTraces"
|
||||||
|
queryKeyFilters={[
|
||||||
|
QUERY_KEYS.K8S_JOB_NAME,
|
||||||
|
QUERY_KEYS.K8S_NAMESPACE_NAME,
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{selectedView === VIEW_TYPES.EVENTS && (
|
{selectedView === VIEW_TYPES.EVENTS && (
|
||||||
|
|||||||
@@ -234,6 +234,11 @@ function K8sJobsList({
|
|||||||
}
|
}
|
||||||
}, [selectedRowData, fetchGroupedByRowData]);
|
}, [selectedRowData, fetchGroupedByRowData]);
|
||||||
|
|
||||||
|
const numberOfPages = useMemo(() => Math.ceil(totalCount / pageSize), [
|
||||||
|
totalCount,
|
||||||
|
pageSize,
|
||||||
|
]);
|
||||||
|
|
||||||
const handleTableChange: TableProps<K8sJobsRowData>['onChange'] = useCallback(
|
const handleTableChange: TableProps<K8sJobsRowData>['onChange'] = useCallback(
|
||||||
(
|
(
|
||||||
pagination: TablePaginationConfig,
|
pagination: TablePaginationConfig,
|
||||||
@@ -242,6 +247,11 @@ function K8sJobsList({
|
|||||||
): void => {
|
): void => {
|
||||||
if (pagination.current) {
|
if (pagination.current) {
|
||||||
setCurrentPage(pagination.current);
|
setCurrentPage(pagination.current);
|
||||||
|
logEvent('Infra Monitoring: K8s jobs list page number changed', {
|
||||||
|
page: pagination.current,
|
||||||
|
pageSize,
|
||||||
|
numberOfPages,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('field' in sorter && sorter.order) {
|
if ('field' in sorter && sorter.order) {
|
||||||
@@ -253,7 +263,7 @@ function K8sJobsList({
|
|||||||
setOrderBy(null);
|
setOrderBy(null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[],
|
[numberOfPages, pageSize],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { handleChangeQueryData } = useQueryOperations({
|
const { handleChangeQueryData } = useQueryOperations({
|
||||||
@@ -267,15 +277,13 @@ function K8sJobsList({
|
|||||||
handleChangeQueryData('filters', value);
|
handleChangeQueryData('filters', value);
|
||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
|
|
||||||
logEvent('Infra Monitoring: K8s list filters applied', {
|
logEvent('Infra Monitoring: K8s jobs list filters applied', {});
|
||||||
filters: value,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[handleChangeQueryData],
|
[handleChangeQueryData],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
logEvent('Infra Monitoring: K8s list page visited', {});
|
logEvent('Infra Monitoring: K8s jobs list page visited', {});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const selectedJobData = useMemo(() => {
|
const selectedJobData = useMemo(() => {
|
||||||
@@ -424,6 +432,8 @@ function K8sJobsList({
|
|||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
setGroupBy(groupBy);
|
setGroupBy(groupBy);
|
||||||
setExpandedRowKeys([]);
|
setExpandedRowKeys([]);
|
||||||
|
|
||||||
|
logEvent('Infra Monitoring: K8s jobs list group by changed', {});
|
||||||
},
|
},
|
||||||
[groupByFiltersData],
|
[groupByFiltersData],
|
||||||
);
|
);
|
||||||
@@ -439,6 +449,16 @@ function K8sJobsList({
|
|||||||
}
|
}
|
||||||
}, [groupByFiltersData]);
|
}, [groupByFiltersData]);
|
||||||
|
|
||||||
|
const onPaginationChange = (page: number, pageSize: number): void => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
setPageSize(pageSize);
|
||||||
|
logEvent('Infra Monitoring: K8s jobs list page number changed', {
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
numberOfPages,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="k8s-list">
|
<div className="k8s-list">
|
||||||
<K8sHeader
|
<K8sHeader
|
||||||
@@ -466,10 +486,7 @@ function K8sJobsList({
|
|||||||
total: totalCount,
|
total: totalCount,
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
hideOnSinglePage: false,
|
hideOnSinglePage: false,
|
||||||
onChange: (page, pageSize): void => {
|
onChange: onPaginationChange,
|
||||||
setCurrentPage(page);
|
|
||||||
setPageSize(pageSize);
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
scroll={{ x: true }}
|
scroll={{ x: true }}
|
||||||
loading={{
|
loading={{
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
import './InfraMonitoringK8s.styles.scss';
|
import './InfraMonitoringK8s.styles.scss';
|
||||||
|
|
||||||
import { Button, Select } from 'antd';
|
import { Button, Select } from 'antd';
|
||||||
|
import { initialQueriesMap } from 'constants/queryBuilder';
|
||||||
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
|
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
|
||||||
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
|
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
|
||||||
import { Filter, SlidersHorizontal } from 'lucide-react';
|
import { Filter, SlidersHorizontal } from 'lucide-react';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
import { K8sCategory } from './constants';
|
import { K8sCategory } from './constants';
|
||||||
import K8sFiltersSidePanel from './K8sFiltersSidePanel/K8sFiltersSidePanel';
|
import K8sFiltersSidePanel from './K8sFiltersSidePanel/K8sFiltersSidePanel';
|
||||||
@@ -47,7 +48,7 @@ function K8sHeader({
|
|||||||
}: K8sHeaderProps): JSX.Element {
|
}: K8sHeaderProps): JSX.Element {
|
||||||
const [isFiltersSidePanelOpen, setIsFiltersSidePanelOpen] = useState(false);
|
const [isFiltersSidePanelOpen, setIsFiltersSidePanelOpen] = useState(false);
|
||||||
|
|
||||||
const { currentQuery } = useQueryBuilder();
|
const currentQuery = initialQueriesMap[DataSource.METRICS];
|
||||||
|
|
||||||
const updatedCurrentQuery = useMemo(
|
const updatedCurrentQuery = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
|||||||
@@ -242,6 +242,11 @@ function K8sNamespacesList({
|
|||||||
}
|
}
|
||||||
}, [selectedRowData, fetchGroupedByRowData]);
|
}, [selectedRowData, fetchGroupedByRowData]);
|
||||||
|
|
||||||
|
const numberOfPages = useMemo(() => Math.ceil(totalCount / pageSize), [
|
||||||
|
totalCount,
|
||||||
|
pageSize,
|
||||||
|
]);
|
||||||
|
|
||||||
const handleTableChange: TableProps<K8sNamespacesRowData>['onChange'] = useCallback(
|
const handleTableChange: TableProps<K8sNamespacesRowData>['onChange'] = useCallback(
|
||||||
(
|
(
|
||||||
pagination: TablePaginationConfig,
|
pagination: TablePaginationConfig,
|
||||||
@@ -252,6 +257,11 @@ function K8sNamespacesList({
|
|||||||
): void => {
|
): void => {
|
||||||
if (pagination.current) {
|
if (pagination.current) {
|
||||||
setCurrentPage(pagination.current);
|
setCurrentPage(pagination.current);
|
||||||
|
logEvent('Infra Monitoring: K8s namespaces list page number changed', {
|
||||||
|
page: pagination.current,
|
||||||
|
pageSize,
|
||||||
|
numberOfPages,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('field' in sorter && sorter.order) {
|
if ('field' in sorter && sorter.order) {
|
||||||
@@ -263,7 +273,7 @@ function K8sNamespacesList({
|
|||||||
setOrderBy(null);
|
setOrderBy(null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[],
|
[numberOfPages, pageSize],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { handleChangeQueryData } = useQueryOperations({
|
const { handleChangeQueryData } = useQueryOperations({
|
||||||
@@ -277,15 +287,13 @@ function K8sNamespacesList({
|
|||||||
handleChangeQueryData('filters', value);
|
handleChangeQueryData('filters', value);
|
||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
|
|
||||||
logEvent('Infra Monitoring: K8s list filters applied', {
|
logEvent('Infra Monitoring: K8s namespaces list filters applied', {});
|
||||||
filters: value,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[handleChangeQueryData],
|
[handleChangeQueryData],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
logEvent('Infra Monitoring: K8s list page visited', {});
|
logEvent('Infra Monitoring: K8s namespaces list page visited', {});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const selectedNamespaceData = useMemo(() => {
|
const selectedNamespaceData = useMemo(() => {
|
||||||
@@ -449,6 +457,8 @@ function K8sNamespacesList({
|
|||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
setGroupBy(groupBy);
|
setGroupBy(groupBy);
|
||||||
setExpandedRowKeys([]);
|
setExpandedRowKeys([]);
|
||||||
|
|
||||||
|
logEvent('Infra Monitoring: K8s namespaces list group by changed', {});
|
||||||
},
|
},
|
||||||
[groupByFiltersData],
|
[groupByFiltersData],
|
||||||
);
|
);
|
||||||
@@ -464,6 +474,16 @@ function K8sNamespacesList({
|
|||||||
}
|
}
|
||||||
}, [groupByFiltersData]);
|
}, [groupByFiltersData]);
|
||||||
|
|
||||||
|
const onPaginationChange = (page: number, pageSize: number): void => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
setPageSize(pageSize);
|
||||||
|
logEvent('Infra Monitoring: K8s namespaces list page number changed', {
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
numberOfPages,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="k8s-list">
|
<div className="k8s-list">
|
||||||
<K8sHeader
|
<K8sHeader
|
||||||
@@ -489,10 +509,7 @@ function K8sNamespacesList({
|
|||||||
total: totalCount,
|
total: totalCount,
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
hideOnSinglePage: false,
|
hideOnSinglePage: false,
|
||||||
onChange: (page, pageSize): void => {
|
onChange: onPaginationChange,
|
||||||
setCurrentPage(page);
|
|
||||||
setPageSize(pageSize);
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
scroll={{ x: true }}
|
scroll={{ x: true }}
|
||||||
loading={{
|
loading={{
|
||||||
|
|||||||
@@ -143,13 +143,9 @@ function NamespaceDetails({
|
|||||||
[namespace?.namespaceName],
|
[namespace?.namespaceName],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [logFilters, setLogFilters] = useState<IBuilderQuery['filters']>(
|
const [logAndTracesFilters, setLogAndTracesFilters] = useState<
|
||||||
initialFilters,
|
IBuilderQuery['filters']
|
||||||
);
|
>(initialFilters);
|
||||||
|
|
||||||
const [tracesFilters, setTracesFilters] = useState<IBuilderQuery['filters']>(
|
|
||||||
initialFilters,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [eventsFilters, setEventsFilters] = useState<IBuilderQuery['filters']>(
|
const [eventsFilters, setEventsFilters] = useState<IBuilderQuery['filters']>(
|
||||||
initialEventsFilters,
|
initialEventsFilters,
|
||||||
@@ -163,8 +159,7 @@ function NamespaceDetails({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLogFilters(initialFilters);
|
setLogAndTracesFilters(initialFilters);
|
||||||
setTracesFilters(initialFilters);
|
|
||||||
setEventsFilters(initialEventsFilters);
|
setEventsFilters(initialEventsFilters);
|
||||||
}, [initialFilters, initialEventsFilters]);
|
}, [initialFilters, initialEventsFilters]);
|
||||||
|
|
||||||
@@ -183,6 +178,10 @@ function NamespaceDetails({
|
|||||||
|
|
||||||
const handleTabChange = (e: RadioChangeEvent): void => {
|
const handleTabChange = (e: RadioChangeEvent): void => {
|
||||||
setSelectedView(e.target.value);
|
setSelectedView(e.target.value);
|
||||||
|
logEvent('Infra Monitoring: Namespaces list details tab changed', {
|
||||||
|
namespace: namespace?.namespaceName,
|
||||||
|
view: e.target.value,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTimeChange = useCallback(
|
const handleTimeChange = useCallback(
|
||||||
@@ -206,6 +205,7 @@ function NamespaceDetails({
|
|||||||
logEvent('Infra Monitoring: Namespaces list details time updated', {
|
logEvent('Infra Monitoring: Namespaces list details time updated', {
|
||||||
namespace: namespace?.namespaceName,
|
namespace: namespace?.namespaceName,
|
||||||
interval,
|
interval,
|
||||||
|
view: selectedView,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -214,7 +214,7 @@ function NamespaceDetails({
|
|||||||
|
|
||||||
const handleChangeLogFilters = useCallback(
|
const handleChangeLogFilters = useCallback(
|
||||||
(value: IBuilderQuery['filters']) => {
|
(value: IBuilderQuery['filters']) => {
|
||||||
setLogFilters((prevFilters) => {
|
setLogAndTracesFilters((prevFilters) => {
|
||||||
const primaryFilters = prevFilters.items.filter((item) =>
|
const primaryFilters = prevFilters.items.filter((item) =>
|
||||||
[QUERY_KEYS.K8S_NAMESPACE_NAME, QUERY_KEYS.K8S_CLUSTER_NAME].includes(
|
[QUERY_KEYS.K8S_NAMESPACE_NAME, QUERY_KEYS.K8S_CLUSTER_NAME].includes(
|
||||||
item.key?.key ?? '',
|
item.key?.key ?? '',
|
||||||
@@ -246,7 +246,7 @@ function NamespaceDetails({
|
|||||||
|
|
||||||
const handleChangeTracesFilters = useCallback(
|
const handleChangeTracesFilters = useCallback(
|
||||||
(value: IBuilderQuery['filters']) => {
|
(value: IBuilderQuery['filters']) => {
|
||||||
setTracesFilters((prevFilters) => {
|
setLogAndTracesFilters((prevFilters) => {
|
||||||
const primaryFilters = prevFilters.items.filter((item) =>
|
const primaryFilters = prevFilters.items.filter((item) =>
|
||||||
[QUERY_KEYS.K8S_NAMESPACE_NAME, QUERY_KEYS.K8S_CLUSTER_NAME].includes(
|
[QUERY_KEYS.K8S_NAMESPACE_NAME, QUERY_KEYS.K8S_CLUSTER_NAME].includes(
|
||||||
item.key?.key ?? '',
|
item.key?.key ?? '',
|
||||||
@@ -326,8 +326,8 @@ function NamespaceDetails({
|
|||||||
|
|
||||||
if (selectedView === VIEW_TYPES.LOGS) {
|
if (selectedView === VIEW_TYPES.LOGS) {
|
||||||
const filtersWithoutPagination = {
|
const filtersWithoutPagination = {
|
||||||
...logFilters,
|
...logAndTracesFilters,
|
||||||
items: logFilters.items.filter((item) => item.key?.key !== 'id'),
|
items: logAndTracesFilters.items.filter((item) => item.key?.key !== 'id'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const compositeQuery = {
|
const compositeQuery = {
|
||||||
@@ -361,7 +361,7 @@ function NamespaceDetails({
|
|||||||
{
|
{
|
||||||
...initialQueryBuilderFormValuesMap.traces,
|
...initialQueryBuilderFormValuesMap.traces,
|
||||||
aggregateOperator: TracesAggregatorOperator.NOOP,
|
aggregateOperator: TracesAggregatorOperator.NOOP,
|
||||||
filters: tracesFilters,
|
filters: logAndTracesFilters,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -527,7 +527,7 @@ function NamespaceDetails({
|
|||||||
isModalTimeSelection={isModalTimeSelection}
|
isModalTimeSelection={isModalTimeSelection}
|
||||||
handleTimeChange={handleTimeChange}
|
handleTimeChange={handleTimeChange}
|
||||||
handleChangeLogFilters={handleChangeLogFilters}
|
handleChangeLogFilters={handleChangeLogFilters}
|
||||||
logFilters={logFilters}
|
logFilters={logAndTracesFilters}
|
||||||
selectedInterval={selectedInterval}
|
selectedInterval={selectedInterval}
|
||||||
queryKey="namespaceLogs"
|
queryKey="namespaceLogs"
|
||||||
category={K8sCategory.NAMESPACES}
|
category={K8sCategory.NAMESPACES}
|
||||||
@@ -540,9 +540,10 @@ function NamespaceDetails({
|
|||||||
isModalTimeSelection={isModalTimeSelection}
|
isModalTimeSelection={isModalTimeSelection}
|
||||||
handleTimeChange={handleTimeChange}
|
handleTimeChange={handleTimeChange}
|
||||||
handleChangeTracesFilters={handleChangeTracesFilters}
|
handleChangeTracesFilters={handleChangeTracesFilters}
|
||||||
tracesFilters={tracesFilters}
|
tracesFilters={logAndTracesFilters}
|
||||||
selectedInterval={selectedInterval}
|
selectedInterval={selectedInterval}
|
||||||
queryKey="namespaceTraces"
|
queryKey="namespaceTraces"
|
||||||
|
queryKeyFilters={[QUERY_KEYS.K8S_NAMESPACE_NAME]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{selectedView === VIEW_TYPES.EVENTS && (
|
{selectedView === VIEW_TYPES.EVENTS && (
|
||||||
|
|||||||
@@ -233,6 +233,11 @@ function K8sNodesList({
|
|||||||
}
|
}
|
||||||
}, [selectedRowData, fetchGroupedByRowData]);
|
}, [selectedRowData, fetchGroupedByRowData]);
|
||||||
|
|
||||||
|
const numberOfPages = useMemo(() => Math.ceil(totalCount / pageSize), [
|
||||||
|
totalCount,
|
||||||
|
pageSize,
|
||||||
|
]);
|
||||||
|
|
||||||
const handleTableChange: TableProps<K8sNodesRowData>['onChange'] = useCallback(
|
const handleTableChange: TableProps<K8sNodesRowData>['onChange'] = useCallback(
|
||||||
(
|
(
|
||||||
pagination: TablePaginationConfig,
|
pagination: TablePaginationConfig,
|
||||||
@@ -241,6 +246,11 @@ function K8sNodesList({
|
|||||||
): void => {
|
): void => {
|
||||||
if (pagination.current) {
|
if (pagination.current) {
|
||||||
setCurrentPage(pagination.current);
|
setCurrentPage(pagination.current);
|
||||||
|
logEvent('Infra Monitoring: K8s nodes list page number changed', {
|
||||||
|
page: pagination.current,
|
||||||
|
pageSize,
|
||||||
|
numberOfPages,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('field' in sorter && sorter.order) {
|
if ('field' in sorter && sorter.order) {
|
||||||
@@ -252,7 +262,7 @@ function K8sNodesList({
|
|||||||
setOrderBy(null);
|
setOrderBy(null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[],
|
[numberOfPages, pageSize],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { handleChangeQueryData } = useQueryOperations({
|
const { handleChangeQueryData } = useQueryOperations({
|
||||||
@@ -266,15 +276,13 @@ function K8sNodesList({
|
|||||||
handleChangeQueryData('filters', value);
|
handleChangeQueryData('filters', value);
|
||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
|
|
||||||
logEvent('Infra Monitoring: K8s list filters applied', {
|
logEvent('Infra Monitoring: K8s nodes list filters applied', {});
|
||||||
filters: value,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[handleChangeQueryData],
|
[handleChangeQueryData],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
logEvent('Infra Monitoring: K8s list page visited', {});
|
logEvent('Infra Monitoring: K8s nodes list page visited', {});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const selectedNodeData = useMemo(() => {
|
const selectedNodeData = useMemo(() => {
|
||||||
@@ -427,6 +435,8 @@ function K8sNodesList({
|
|||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
setGroupBy(groupBy);
|
setGroupBy(groupBy);
|
||||||
setExpandedRowKeys([]);
|
setExpandedRowKeys([]);
|
||||||
|
|
||||||
|
logEvent('Infra Monitoring: K8s nodes list group by changed', {});
|
||||||
},
|
},
|
||||||
[groupByFiltersData],
|
[groupByFiltersData],
|
||||||
);
|
);
|
||||||
@@ -442,6 +452,16 @@ function K8sNodesList({
|
|||||||
}
|
}
|
||||||
}, [groupByFiltersData]);
|
}, [groupByFiltersData]);
|
||||||
|
|
||||||
|
const onPaginationChange = (page: number, pageSize: number): void => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
setPageSize(pageSize);
|
||||||
|
logEvent('Infra Monitoring: K8s nodes list page number changed', {
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
numberOfPages,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="k8s-list">
|
<div className="k8s-list">
|
||||||
<K8sHeader
|
<K8sHeader
|
||||||
@@ -467,10 +487,7 @@ function K8sNodesList({
|
|||||||
total: totalCount,
|
total: totalCount,
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
hideOnSinglePage: false,
|
hideOnSinglePage: false,
|
||||||
onChange: (page, pageSize): void => {
|
onChange: onPaginationChange,
|
||||||
setCurrentPage(page);
|
|
||||||
setPageSize(pageSize);
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
scroll={{ x: true }}
|
scroll={{ x: true }}
|
||||||
loading={{
|
loading={{
|
||||||
|
|||||||
@@ -141,13 +141,9 @@ function NodeDetails({
|
|||||||
[node?.meta.k8s_node_name],
|
[node?.meta.k8s_node_name],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [logFilters, setLogFilters] = useState<IBuilderQuery['filters']>(
|
const [logAndTracesFilters, setLogAndTracesFilters] = useState<
|
||||||
initialFilters,
|
IBuilderQuery['filters']
|
||||||
);
|
>(initialFilters);
|
||||||
|
|
||||||
const [tracesFilters, setTracesFilters] = useState<IBuilderQuery['filters']>(
|
|
||||||
initialFilters,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [eventsFilters, setEventsFilters] = useState<IBuilderQuery['filters']>(
|
const [eventsFilters, setEventsFilters] = useState<IBuilderQuery['filters']>(
|
||||||
initialEventsFilters,
|
initialEventsFilters,
|
||||||
@@ -161,8 +157,7 @@ function NodeDetails({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLogFilters(initialFilters);
|
setLogAndTracesFilters(initialFilters);
|
||||||
setTracesFilters(initialFilters);
|
|
||||||
setEventsFilters(initialEventsFilters);
|
setEventsFilters(initialEventsFilters);
|
||||||
}, [initialFilters, initialEventsFilters]);
|
}, [initialFilters, initialEventsFilters]);
|
||||||
|
|
||||||
@@ -181,6 +176,10 @@ function NodeDetails({
|
|||||||
|
|
||||||
const handleTabChange = (e: RadioChangeEvent): void => {
|
const handleTabChange = (e: RadioChangeEvent): void => {
|
||||||
setSelectedView(e.target.value);
|
setSelectedView(e.target.value);
|
||||||
|
logEvent('Infra Monitoring: Nodes list details tab changed', {
|
||||||
|
node: node?.nodeUID,
|
||||||
|
view: e.target.value,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTimeChange = useCallback(
|
const handleTimeChange = useCallback(
|
||||||
@@ -204,6 +203,7 @@ function NodeDetails({
|
|||||||
logEvent('Infra Monitoring: Nodes list details time updated', {
|
logEvent('Infra Monitoring: Nodes list details time updated', {
|
||||||
node: node?.nodeUID,
|
node: node?.nodeUID,
|
||||||
interval,
|
interval,
|
||||||
|
view: selectedView,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -212,7 +212,7 @@ function NodeDetails({
|
|||||||
|
|
||||||
const handleChangeLogFilters = useCallback(
|
const handleChangeLogFilters = useCallback(
|
||||||
(value: IBuilderQuery['filters']) => {
|
(value: IBuilderQuery['filters']) => {
|
||||||
setLogFilters((prevFilters) => {
|
setLogAndTracesFilters((prevFilters) => {
|
||||||
const primaryFilters = prevFilters.items.filter((item) =>
|
const primaryFilters = prevFilters.items.filter((item) =>
|
||||||
[QUERY_KEYS.K8S_NODE_NAME, QUERY_KEYS.K8S_CLUSTER_NAME].includes(
|
[QUERY_KEYS.K8S_NODE_NAME, QUERY_KEYS.K8S_CLUSTER_NAME].includes(
|
||||||
item.key?.key ?? '',
|
item.key?.key ?? '',
|
||||||
@@ -246,7 +246,7 @@ function NodeDetails({
|
|||||||
|
|
||||||
const handleChangeTracesFilters = useCallback(
|
const handleChangeTracesFilters = useCallback(
|
||||||
(value: IBuilderQuery['filters']) => {
|
(value: IBuilderQuery['filters']) => {
|
||||||
setTracesFilters((prevFilters) => {
|
setLogAndTracesFilters((prevFilters) => {
|
||||||
const primaryFilters = prevFilters.items.filter((item) =>
|
const primaryFilters = prevFilters.items.filter((item) =>
|
||||||
[QUERY_KEYS.K8S_NODE_NAME, QUERY_KEYS.K8S_CLUSTER_NAME].includes(
|
[QUERY_KEYS.K8S_NODE_NAME, QUERY_KEYS.K8S_CLUSTER_NAME].includes(
|
||||||
item.key?.key ?? '',
|
item.key?.key ?? '',
|
||||||
@@ -322,8 +322,8 @@ function NodeDetails({
|
|||||||
|
|
||||||
if (selectedView === VIEW_TYPES.LOGS) {
|
if (selectedView === VIEW_TYPES.LOGS) {
|
||||||
const filtersWithoutPagination = {
|
const filtersWithoutPagination = {
|
||||||
...logFilters,
|
...logAndTracesFilters,
|
||||||
items: logFilters.items.filter((item) => item.key?.key !== 'id'),
|
items: logAndTracesFilters.items.filter((item) => item.key?.key !== 'id'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const compositeQuery = {
|
const compositeQuery = {
|
||||||
@@ -357,7 +357,7 @@ function NodeDetails({
|
|||||||
{
|
{
|
||||||
...initialQueryBuilderFormValuesMap.traces,
|
...initialQueryBuilderFormValuesMap.traces,
|
||||||
aggregateOperator: TracesAggregatorOperator.NOOP,
|
aggregateOperator: TracesAggregatorOperator.NOOP,
|
||||||
filters: tracesFilters,
|
filters: logAndTracesFilters,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -521,7 +521,7 @@ function NodeDetails({
|
|||||||
isModalTimeSelection={isModalTimeSelection}
|
isModalTimeSelection={isModalTimeSelection}
|
||||||
handleTimeChange={handleTimeChange}
|
handleTimeChange={handleTimeChange}
|
||||||
handleChangeLogFilters={handleChangeLogFilters}
|
handleChangeLogFilters={handleChangeLogFilters}
|
||||||
logFilters={logFilters}
|
logFilters={logAndTracesFilters}
|
||||||
selectedInterval={selectedInterval}
|
selectedInterval={selectedInterval}
|
||||||
queryKeyFilters={[QUERY_KEYS.K8S_NODE_NAME, QUERY_KEYS.K8S_CLUSTER_NAME]}
|
queryKeyFilters={[QUERY_KEYS.K8S_NODE_NAME, QUERY_KEYS.K8S_CLUSTER_NAME]}
|
||||||
queryKey="nodeLogs"
|
queryKey="nodeLogs"
|
||||||
@@ -534,8 +534,9 @@ function NodeDetails({
|
|||||||
isModalTimeSelection={isModalTimeSelection}
|
isModalTimeSelection={isModalTimeSelection}
|
||||||
handleTimeChange={handleTimeChange}
|
handleTimeChange={handleTimeChange}
|
||||||
handleChangeTracesFilters={handleChangeTracesFilters}
|
handleChangeTracesFilters={handleChangeTracesFilters}
|
||||||
tracesFilters={tracesFilters}
|
tracesFilters={logAndTracesFilters}
|
||||||
selectedInterval={selectedInterval}
|
selectedInterval={selectedInterval}
|
||||||
|
queryKeyFilters={[QUERY_KEYS.K8S_NODE_NAME, QUERY_KEYS.K8S_CLUSTER_NAME]}
|
||||||
queryKey="nodeTraces"
|
queryKey="nodeTraces"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -248,6 +248,11 @@ function K8sPodsList({
|
|||||||
groupBy,
|
groupBy,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const numberOfPages = useMemo(() => Math.ceil(totalCount / pageSize), [
|
||||||
|
totalCount,
|
||||||
|
pageSize,
|
||||||
|
]);
|
||||||
|
|
||||||
const handleTableChange: TableProps<K8sPodsRowData>['onChange'] = useCallback(
|
const handleTableChange: TableProps<K8sPodsRowData>['onChange'] = useCallback(
|
||||||
(
|
(
|
||||||
pagination: TablePaginationConfig,
|
pagination: TablePaginationConfig,
|
||||||
@@ -256,6 +261,11 @@ function K8sPodsList({
|
|||||||
): void => {
|
): void => {
|
||||||
if (pagination.current) {
|
if (pagination.current) {
|
||||||
setCurrentPage(pagination.current);
|
setCurrentPage(pagination.current);
|
||||||
|
logEvent('Infra Monitoring: K8s pods list page number changed', {
|
||||||
|
page: pagination.current,
|
||||||
|
pageSize,
|
||||||
|
numberOfPages,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('field' in sorter && sorter.order) {
|
if ('field' in sorter && sorter.order) {
|
||||||
@@ -267,7 +277,7 @@ function K8sPodsList({
|
|||||||
setOrderBy(null);
|
setOrderBy(null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[],
|
[numberOfPages, pageSize],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { handleChangeQueryData } = useQueryOperations({
|
const { handleChangeQueryData } = useQueryOperations({
|
||||||
@@ -281,9 +291,7 @@ function K8sPodsList({
|
|||||||
handleChangeQueryData('filters', value);
|
handleChangeQueryData('filters', value);
|
||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
|
|
||||||
logEvent('Infra Monitoring: K8s list filters applied', {
|
logEvent('Infra Monitoring: K8s pods list filters applied', {});
|
||||||
filters: value,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[handleChangeQueryData],
|
[handleChangeQueryData],
|
||||||
);
|
);
|
||||||
@@ -308,12 +316,14 @@ function K8sPodsList({
|
|||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
setGroupBy(groupBy);
|
setGroupBy(groupBy);
|
||||||
setExpandedRowKeys([]);
|
setExpandedRowKeys([]);
|
||||||
|
|
||||||
|
logEvent('Infra Monitoring: K8s pods list group by changed', {});
|
||||||
},
|
},
|
||||||
[groupByFiltersData],
|
[groupByFiltersData],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
logEvent('Infra Monitoring: K8s list page visited', {});
|
logEvent('Infra Monitoring: K8s pods list page visited', {});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const selectedPodData = useMemo(() => {
|
const selectedPodData = useMemo(() => {
|
||||||
@@ -350,7 +360,7 @@ function K8sPodsList({
|
|||||||
handleGroupByRowClick(record);
|
handleGroupByRowClick(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
logEvent('Infra Monitoring: K8s list item clicked', {
|
logEvent('Infra Monitoring: K8s pods list item clicked', {
|
||||||
podUID: record.podUID,
|
podUID: record.podUID,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -499,6 +509,16 @@ function K8sPodsList({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onPaginationChange = (page: number, pageSize: number): void => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
setPageSize(pageSize);
|
||||||
|
logEvent('Infra Monitoring: K8s pods list page number changed', {
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
numberOfPages,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="k8s-list">
|
<div className="k8s-list">
|
||||||
<K8sHeader
|
<K8sHeader
|
||||||
@@ -530,10 +550,7 @@ function K8sPodsList({
|
|||||||
total: totalCount,
|
total: totalCount,
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
hideOnSinglePage: false,
|
hideOnSinglePage: false,
|
||||||
onChange: (page, pageSize): void => {
|
onChange: onPaginationChange,
|
||||||
setCurrentPage(page);
|
|
||||||
setPageSize(pageSize);
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
loading={{
|
loading={{
|
||||||
spinning: isFetching || isLoading,
|
spinning: isFetching || isLoading,
|
||||||
|
|||||||
@@ -158,13 +158,9 @@ function PodDetails({
|
|||||||
[pod?.meta.k8s_pod_name],
|
[pod?.meta.k8s_pod_name],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [logFilters, setLogFilters] = useState<IBuilderQuery['filters']>(
|
const [logsAndTracesFilters, setLogsAndTracesFilters] = useState<
|
||||||
initialFilters,
|
IBuilderQuery['filters']
|
||||||
);
|
>(initialFilters);
|
||||||
|
|
||||||
const [tracesFilters, setTracesFilters] = useState<IBuilderQuery['filters']>(
|
|
||||||
initialFilters,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [eventsFilters, setEventsFilters] = useState<IBuilderQuery['filters']>(
|
const [eventsFilters, setEventsFilters] = useState<IBuilderQuery['filters']>(
|
||||||
initialEventsFilters,
|
initialEventsFilters,
|
||||||
@@ -178,8 +174,7 @@ function PodDetails({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLogFilters(initialFilters);
|
setLogsAndTracesFilters(initialFilters);
|
||||||
setTracesFilters(initialFilters);
|
|
||||||
setEventsFilters(initialEventsFilters);
|
setEventsFilters(initialEventsFilters);
|
||||||
}, [initialFilters, initialEventsFilters]);
|
}, [initialFilters, initialEventsFilters]);
|
||||||
|
|
||||||
@@ -198,6 +193,10 @@ function PodDetails({
|
|||||||
|
|
||||||
const handleTabChange = (e: RadioChangeEvent): void => {
|
const handleTabChange = (e: RadioChangeEvent): void => {
|
||||||
setSelectedView(e.target.value);
|
setSelectedView(e.target.value);
|
||||||
|
logEvent('Infra Monitoring: Pods list details tab changed', {
|
||||||
|
pod: pod?.podUID,
|
||||||
|
view: e.target.value,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTimeChange = useCallback(
|
const handleTimeChange = useCallback(
|
||||||
@@ -221,6 +220,7 @@ function PodDetails({
|
|||||||
logEvent('Infra Monitoring: Pods list details time updated', {
|
logEvent('Infra Monitoring: Pods list details time updated', {
|
||||||
pod: pod?.podUID,
|
pod: pod?.podUID,
|
||||||
interval,
|
interval,
|
||||||
|
view: selectedView,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -229,7 +229,7 @@ function PodDetails({
|
|||||||
|
|
||||||
const handleChangeLogFilters = useCallback(
|
const handleChangeLogFilters = useCallback(
|
||||||
(value: IBuilderQuery['filters']) => {
|
(value: IBuilderQuery['filters']) => {
|
||||||
setLogFilters((prevFilters) => {
|
setLogsAndTracesFilters((prevFilters) => {
|
||||||
const primaryFilters = prevFilters.items.filter((item) =>
|
const primaryFilters = prevFilters.items.filter((item) =>
|
||||||
[
|
[
|
||||||
QUERY_KEYS.K8S_POD_NAME,
|
QUERY_KEYS.K8S_POD_NAME,
|
||||||
@@ -265,7 +265,7 @@ function PodDetails({
|
|||||||
|
|
||||||
const handleChangeTracesFilters = useCallback(
|
const handleChangeTracesFilters = useCallback(
|
||||||
(value: IBuilderQuery['filters']) => {
|
(value: IBuilderQuery['filters']) => {
|
||||||
setTracesFilters((prevFilters) => {
|
setLogsAndTracesFilters((prevFilters) => {
|
||||||
const primaryFilters = prevFilters.items.filter((item) =>
|
const primaryFilters = prevFilters.items.filter((item) =>
|
||||||
[
|
[
|
||||||
QUERY_KEYS.K8S_POD_NAME,
|
QUERY_KEYS.K8S_POD_NAME,
|
||||||
@@ -343,8 +343,8 @@ function PodDetails({
|
|||||||
|
|
||||||
if (selectedView === VIEW_TYPES.LOGS) {
|
if (selectedView === VIEW_TYPES.LOGS) {
|
||||||
const filtersWithoutPagination = {
|
const filtersWithoutPagination = {
|
||||||
...logFilters,
|
...logsAndTracesFilters,
|
||||||
items: logFilters.items.filter((item) => item.key?.key !== 'id'),
|
items: logsAndTracesFilters.items.filter((item) => item.key?.key !== 'id'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const compositeQuery = {
|
const compositeQuery = {
|
||||||
@@ -378,7 +378,7 @@ function PodDetails({
|
|||||||
{
|
{
|
||||||
...initialQueryBuilderFormValuesMap.traces,
|
...initialQueryBuilderFormValuesMap.traces,
|
||||||
aggregateOperator: TracesAggregatorOperator.NOOP,
|
aggregateOperator: TracesAggregatorOperator.NOOP,
|
||||||
filters: tracesFilters,
|
filters: logsAndTracesFilters,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -559,7 +559,7 @@ function PodDetails({
|
|||||||
isModalTimeSelection={isModalTimeSelection}
|
isModalTimeSelection={isModalTimeSelection}
|
||||||
handleTimeChange={handleTimeChange}
|
handleTimeChange={handleTimeChange}
|
||||||
handleChangeLogFilters={handleChangeLogFilters}
|
handleChangeLogFilters={handleChangeLogFilters}
|
||||||
logFilters={logFilters}
|
logFilters={logsAndTracesFilters}
|
||||||
selectedInterval={selectedInterval}
|
selectedInterval={selectedInterval}
|
||||||
queryKeyFilters={[
|
queryKeyFilters={[
|
||||||
QUERY_KEYS.K8S_POD_NAME,
|
QUERY_KEYS.K8S_POD_NAME,
|
||||||
@@ -576,9 +576,14 @@ function PodDetails({
|
|||||||
isModalTimeSelection={isModalTimeSelection}
|
isModalTimeSelection={isModalTimeSelection}
|
||||||
handleTimeChange={handleTimeChange}
|
handleTimeChange={handleTimeChange}
|
||||||
handleChangeTracesFilters={handleChangeTracesFilters}
|
handleChangeTracesFilters={handleChangeTracesFilters}
|
||||||
tracesFilters={tracesFilters}
|
tracesFilters={logsAndTracesFilters}
|
||||||
selectedInterval={selectedInterval}
|
selectedInterval={selectedInterval}
|
||||||
queryKey="podTraces"
|
queryKey="podTraces"
|
||||||
|
queryKeyFilters={[
|
||||||
|
QUERY_KEYS.K8S_POD_NAME,
|
||||||
|
QUERY_KEYS.K8S_CLUSTER_NAME,
|
||||||
|
QUERY_KEYS.K8S_NAMESPACE_NAME,
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -245,6 +245,11 @@ function K8sStatefulSetsList({
|
|||||||
}
|
}
|
||||||
}, [selectedRowData, fetchGroupedByRowData]);
|
}, [selectedRowData, fetchGroupedByRowData]);
|
||||||
|
|
||||||
|
const numberOfPages = useMemo(() => Math.ceil(totalCount / pageSize), [
|
||||||
|
totalCount,
|
||||||
|
pageSize,
|
||||||
|
]);
|
||||||
|
|
||||||
const handleTableChange: TableProps<K8sStatefulSetsRowData>['onChange'] = useCallback(
|
const handleTableChange: TableProps<K8sStatefulSetsRowData>['onChange'] = useCallback(
|
||||||
(
|
(
|
||||||
pagination: TablePaginationConfig,
|
pagination: TablePaginationConfig,
|
||||||
@@ -255,6 +260,11 @@ function K8sStatefulSetsList({
|
|||||||
): void => {
|
): void => {
|
||||||
if (pagination.current) {
|
if (pagination.current) {
|
||||||
setCurrentPage(pagination.current);
|
setCurrentPage(pagination.current);
|
||||||
|
logEvent('Infra Monitoring: K8s statefulSets list page number changed', {
|
||||||
|
page: pagination.current,
|
||||||
|
pageSize,
|
||||||
|
numberOfPages,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('field' in sorter && sorter.order) {
|
if ('field' in sorter && sorter.order) {
|
||||||
@@ -266,7 +276,7 @@ function K8sStatefulSetsList({
|
|||||||
setOrderBy(null);
|
setOrderBy(null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[],
|
[numberOfPages, pageSize],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { handleChangeQueryData } = useQueryOperations({
|
const { handleChangeQueryData } = useQueryOperations({
|
||||||
@@ -280,15 +290,13 @@ function K8sStatefulSetsList({
|
|||||||
handleChangeQueryData('filters', value);
|
handleChangeQueryData('filters', value);
|
||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
|
|
||||||
logEvent('Infra Monitoring: K8s list filters applied', {
|
logEvent('Infra Monitoring: K8s statefulSets list filters applied', {});
|
||||||
filters: value,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[handleChangeQueryData],
|
[handleChangeQueryData],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
logEvent('Infra Monitoring: K8s list page visited', {});
|
logEvent('Infra Monitoring: K8s statefulSets list page visited', {});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const selectedStatefulSetData = useMemo(() => {
|
const selectedStatefulSetData = useMemo(() => {
|
||||||
@@ -449,6 +457,8 @@ function K8sStatefulSetsList({
|
|||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
setGroupBy(groupBy);
|
setGroupBy(groupBy);
|
||||||
setExpandedRowKeys([]);
|
setExpandedRowKeys([]);
|
||||||
|
|
||||||
|
logEvent('Infra Monitoring: K8s statefulSets list group by changed', {});
|
||||||
},
|
},
|
||||||
[groupByFiltersData],
|
[groupByFiltersData],
|
||||||
);
|
);
|
||||||
@@ -464,6 +474,16 @@ function K8sStatefulSetsList({
|
|||||||
}
|
}
|
||||||
}, [groupByFiltersData]);
|
}, [groupByFiltersData]);
|
||||||
|
|
||||||
|
const onPaginationChange = (page: number, pageSize: number): void => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
setPageSize(pageSize);
|
||||||
|
logEvent('Infra Monitoring: K8s statefulSets list page number changed', {
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
numberOfPages,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="k8s-list">
|
<div className="k8s-list">
|
||||||
<K8sHeader
|
<K8sHeader
|
||||||
@@ -491,10 +511,7 @@ function K8sStatefulSetsList({
|
|||||||
total: totalCount,
|
total: totalCount,
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
hideOnSinglePage: false,
|
hideOnSinglePage: false,
|
||||||
onChange: (page, pageSize): void => {
|
onChange: onPaginationChange,
|
||||||
setCurrentPage(page);
|
|
||||||
setPageSize(pageSize);
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
scroll={{ x: true }}
|
scroll={{ x: true }}
|
||||||
loading={{
|
loading={{
|
||||||
|
|||||||
@@ -158,13 +158,9 @@ function StatefulSetDetails({
|
|||||||
[statefulSet?.meta.k8s_statefulset_name],
|
[statefulSet?.meta.k8s_statefulset_name],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [logFilters, setLogFilters] = useState<IBuilderQuery['filters']>(
|
const [logAndTracesFilters, setLogAndTracesFilters] = useState<
|
||||||
initialFilters,
|
IBuilderQuery['filters']
|
||||||
);
|
>(initialFilters);
|
||||||
|
|
||||||
const [tracesFilters, setTracesFilters] = useState<IBuilderQuery['filters']>(
|
|
||||||
initialFilters,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [eventsFilters, setEventsFilters] = useState<IBuilderQuery['filters']>(
|
const [eventsFilters, setEventsFilters] = useState<IBuilderQuery['filters']>(
|
||||||
initialEventsFilters,
|
initialEventsFilters,
|
||||||
@@ -178,8 +174,7 @@ function StatefulSetDetails({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLogFilters(initialFilters);
|
setLogAndTracesFilters(initialFilters);
|
||||||
setTracesFilters(initialFilters);
|
|
||||||
setEventsFilters(initialEventsFilters);
|
setEventsFilters(initialEventsFilters);
|
||||||
}, [initialFilters, initialEventsFilters]);
|
}, [initialFilters, initialEventsFilters]);
|
||||||
|
|
||||||
@@ -198,6 +193,10 @@ function StatefulSetDetails({
|
|||||||
|
|
||||||
const handleTabChange = (e: RadioChangeEvent): void => {
|
const handleTabChange = (e: RadioChangeEvent): void => {
|
||||||
setSelectedView(e.target.value);
|
setSelectedView(e.target.value);
|
||||||
|
logEvent('Infra Monitoring: StatefulSets list details tab changed', {
|
||||||
|
statefulSet: statefulSet?.statefulSetName,
|
||||||
|
view: e.target.value,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTimeChange = useCallback(
|
const handleTimeChange = useCallback(
|
||||||
@@ -221,6 +220,7 @@ function StatefulSetDetails({
|
|||||||
logEvent('Infra Monitoring: StatefulSets list details time updated', {
|
logEvent('Infra Monitoring: StatefulSets list details time updated', {
|
||||||
statefulSet: statefulSet?.statefulSetName,
|
statefulSet: statefulSet?.statefulSetName,
|
||||||
interval,
|
interval,
|
||||||
|
view: selectedView,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -229,7 +229,7 @@ function StatefulSetDetails({
|
|||||||
|
|
||||||
const handleChangeLogFilters = useCallback(
|
const handleChangeLogFilters = useCallback(
|
||||||
(value: IBuilderQuery['filters']) => {
|
(value: IBuilderQuery['filters']) => {
|
||||||
setLogFilters((prevFilters) => {
|
setLogAndTracesFilters((prevFilters) => {
|
||||||
const primaryFilters = prevFilters.items.filter((item) =>
|
const primaryFilters = prevFilters.items.filter((item) =>
|
||||||
[QUERY_KEYS.K8S_STATEFUL_SET_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes(
|
[QUERY_KEYS.K8S_STATEFUL_SET_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes(
|
||||||
item.key?.key ?? '',
|
item.key?.key ?? '',
|
||||||
@@ -265,7 +265,7 @@ function StatefulSetDetails({
|
|||||||
|
|
||||||
const handleChangeTracesFilters = useCallback(
|
const handleChangeTracesFilters = useCallback(
|
||||||
(value: IBuilderQuery['filters']) => {
|
(value: IBuilderQuery['filters']) => {
|
||||||
setTracesFilters((prevFilters) => {
|
setLogAndTracesFilters((prevFilters) => {
|
||||||
const primaryFilters = prevFilters.items.filter((item) =>
|
const primaryFilters = prevFilters.items.filter((item) =>
|
||||||
[QUERY_KEYS.K8S_STATEFUL_SET_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes(
|
[QUERY_KEYS.K8S_STATEFUL_SET_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes(
|
||||||
item.key?.key ?? '',
|
item.key?.key ?? '',
|
||||||
@@ -345,8 +345,8 @@ function StatefulSetDetails({
|
|||||||
|
|
||||||
if (selectedView === VIEW_TYPES.LOGS) {
|
if (selectedView === VIEW_TYPES.LOGS) {
|
||||||
const filtersWithoutPagination = {
|
const filtersWithoutPagination = {
|
||||||
...logFilters,
|
...logAndTracesFilters,
|
||||||
items: logFilters.items.filter((item) => item.key?.key !== 'id'),
|
items: logAndTracesFilters.items.filter((item) => item.key?.key !== 'id'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const compositeQuery = {
|
const compositeQuery = {
|
||||||
@@ -380,7 +380,7 @@ function StatefulSetDetails({
|
|||||||
{
|
{
|
||||||
...initialQueryBuilderFormValuesMap.traces,
|
...initialQueryBuilderFormValuesMap.traces,
|
||||||
aggregateOperator: TracesAggregatorOperator.NOOP,
|
aggregateOperator: TracesAggregatorOperator.NOOP,
|
||||||
filters: tracesFilters,
|
filters: logAndTracesFilters,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -546,7 +546,7 @@ function StatefulSetDetails({
|
|||||||
isModalTimeSelection={isModalTimeSelection}
|
isModalTimeSelection={isModalTimeSelection}
|
||||||
handleTimeChange={handleTimeChange}
|
handleTimeChange={handleTimeChange}
|
||||||
handleChangeLogFilters={handleChangeLogFilters}
|
handleChangeLogFilters={handleChangeLogFilters}
|
||||||
logFilters={logFilters}
|
logFilters={logAndTracesFilters}
|
||||||
selectedInterval={selectedInterval}
|
selectedInterval={selectedInterval}
|
||||||
queryKey="statefulsetLogs"
|
queryKey="statefulsetLogs"
|
||||||
category={K8sCategory.STATEFULSETS}
|
category={K8sCategory.STATEFULSETS}
|
||||||
@@ -562,9 +562,13 @@ function StatefulSetDetails({
|
|||||||
isModalTimeSelection={isModalTimeSelection}
|
isModalTimeSelection={isModalTimeSelection}
|
||||||
handleTimeChange={handleTimeChange}
|
handleTimeChange={handleTimeChange}
|
||||||
handleChangeTracesFilters={handleChangeTracesFilters}
|
handleChangeTracesFilters={handleChangeTracesFilters}
|
||||||
tracesFilters={tracesFilters}
|
tracesFilters={logAndTracesFilters}
|
||||||
selectedInterval={selectedInterval}
|
selectedInterval={selectedInterval}
|
||||||
queryKey="statefulsetTraces"
|
queryKey="statefulsetTraces"
|
||||||
|
queryKeyFilters={[
|
||||||
|
QUERY_KEYS.K8S_STATEFUL_SET_NAME,
|
||||||
|
QUERY_KEYS.K8S_NAMESPACE_NAME,
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{selectedView === VIEW_TYPES.EVENTS && (
|
{selectedView === VIEW_TYPES.EVENTS && (
|
||||||
|
|||||||
@@ -237,6 +237,11 @@ function K8sVolumesList({
|
|||||||
}
|
}
|
||||||
}, [selectedRowData, fetchGroupedByRowData]);
|
}, [selectedRowData, fetchGroupedByRowData]);
|
||||||
|
|
||||||
|
const numberOfPages = useMemo(() => Math.ceil(totalCount / pageSize), [
|
||||||
|
totalCount,
|
||||||
|
pageSize,
|
||||||
|
]);
|
||||||
|
|
||||||
const handleTableChange: TableProps<K8sVolumesRowData>['onChange'] = useCallback(
|
const handleTableChange: TableProps<K8sVolumesRowData>['onChange'] = useCallback(
|
||||||
(
|
(
|
||||||
pagination: TablePaginationConfig,
|
pagination: TablePaginationConfig,
|
||||||
@@ -245,6 +250,11 @@ function K8sVolumesList({
|
|||||||
): void => {
|
): void => {
|
||||||
if (pagination.current) {
|
if (pagination.current) {
|
||||||
setCurrentPage(pagination.current);
|
setCurrentPage(pagination.current);
|
||||||
|
logEvent('Infra Monitoring: K8s volumes list page number changed', {
|
||||||
|
page: pagination.current,
|
||||||
|
pageSize,
|
||||||
|
numberOfPages,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('field' in sorter && sorter.order) {
|
if ('field' in sorter && sorter.order) {
|
||||||
@@ -256,7 +266,7 @@ function K8sVolumesList({
|
|||||||
setOrderBy(null);
|
setOrderBy(null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[],
|
[numberOfPages, pageSize],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { handleChangeQueryData } = useQueryOperations({
|
const { handleChangeQueryData } = useQueryOperations({
|
||||||
@@ -270,15 +280,13 @@ function K8sVolumesList({
|
|||||||
handleChangeQueryData('filters', value);
|
handleChangeQueryData('filters', value);
|
||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
|
|
||||||
logEvent('Infra Monitoring: K8s list filters applied', {
|
logEvent('Infra Monitoring: K8s volumes list filters applied', {});
|
||||||
filters: value,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[handleChangeQueryData],
|
[handleChangeQueryData],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
logEvent('Infra Monitoring: K8s list page visited', {});
|
logEvent('Infra Monitoring: K8s volumes list page visited', {});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const selectedVolumeData = useMemo(() => {
|
const selectedVolumeData = useMemo(() => {
|
||||||
@@ -434,6 +442,8 @@ function K8sVolumesList({
|
|||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
setGroupBy(groupBy);
|
setGroupBy(groupBy);
|
||||||
setExpandedRowKeys([]);
|
setExpandedRowKeys([]);
|
||||||
|
|
||||||
|
logEvent('Infra Monitoring: K8s volumes list group by changed', {});
|
||||||
},
|
},
|
||||||
[groupByFiltersData],
|
[groupByFiltersData],
|
||||||
);
|
);
|
||||||
@@ -449,6 +459,16 @@ function K8sVolumesList({
|
|||||||
}
|
}
|
||||||
}, [groupByFiltersData]);
|
}, [groupByFiltersData]);
|
||||||
|
|
||||||
|
const onPaginationChange = (page: number, pageSize: number): void => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
setPageSize(pageSize);
|
||||||
|
logEvent('Infra Monitoring: K8s volumes list page number changed', {
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
numberOfPages,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="k8s-list">
|
<div className="k8s-list">
|
||||||
<K8sHeader
|
<K8sHeader
|
||||||
@@ -476,10 +496,7 @@ function K8sVolumesList({
|
|||||||
total: totalCount,
|
total: totalCount,
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
hideOnSinglePage: false,
|
hideOnSinglePage: false,
|
||||||
onChange: (page, pageSize): void => {
|
onChange: onPaginationChange,
|
||||||
setCurrentPage(page);
|
|
||||||
setPageSize(pageSize);
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
scroll={{ x: true }}
|
scroll={{ x: true }}
|
||||||
loading={{
|
loading={{
|
||||||
|
|||||||
12
frontend/src/container/MetricsExplorer/Explorer/Explorer.tsx
Normal file
12
frontend/src/container/MetricsExplorer/Explorer/Explorer.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import * as Sentry from '@sentry/react';
|
||||||
|
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
|
||||||
|
|
||||||
|
function Explorer(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
|
||||||
|
Explorer
|
||||||
|
</Sentry.ErrorBoundary>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Explorer;
|
||||||
3
frontend/src/container/MetricsExplorer/Explorer/index.ts
Normal file
3
frontend/src/container/MetricsExplorer/Explorer/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import Explorer from './Explorer';
|
||||||
|
|
||||||
|
export default Explorer;
|
||||||
12
frontend/src/container/MetricsExplorer/Summary/Summary.tsx
Normal file
12
frontend/src/container/MetricsExplorer/Summary/Summary.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import * as Sentry from '@sentry/react';
|
||||||
|
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
|
||||||
|
|
||||||
|
function Summary(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
|
||||||
|
Summary
|
||||||
|
</Sentry.ErrorBoundary>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Summary;
|
||||||
3
frontend/src/container/MetricsExplorer/Summary/index.ts
Normal file
3
frontend/src/container/MetricsExplorer/Summary/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import Summary from './Summary';
|
||||||
|
|
||||||
|
export default Summary;
|
||||||
12
frontend/src/container/MetricsExplorer/Views/Views.tsx
Normal file
12
frontend/src/container/MetricsExplorer/Views/Views.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import * as Sentry from '@sentry/react';
|
||||||
|
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
|
||||||
|
|
||||||
|
function Views(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
|
||||||
|
Views
|
||||||
|
</Sentry.ErrorBoundary>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Views;
|
||||||
3
frontend/src/container/MetricsExplorer/Views/index.ts
Normal file
3
frontend/src/container/MetricsExplorer/Views/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import Views from './Views';
|
||||||
|
|
||||||
|
export default Views;
|
||||||
92
frontend/src/container/NewWidget/__test__/NewWidget.test.tsx
Normal file
92
frontend/src/container/NewWidget/__test__/NewWidget.test.tsx
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
// This test suite covers several important scenarios:
|
||||||
|
// - Empty layout - widget should be placed at origin (0,0)
|
||||||
|
// - Empty layout with custom dimensions
|
||||||
|
// - Placing widget next to an existing widget when there's space in the last row
|
||||||
|
// - Placing widget at bottom when the last row is full
|
||||||
|
// - Handling multiple rows correctly
|
||||||
|
// - Handling widgets with different heights
|
||||||
|
|
||||||
|
import { placeWidgetAtBottom } from '../utils';
|
||||||
|
|
||||||
|
describe('placeWidgetAtBottom', () => {
|
||||||
|
it('should place widget at (0,0) when layout is empty', () => {
|
||||||
|
const result = placeWidgetAtBottom('widget1', []);
|
||||||
|
expect(result).toEqual({
|
||||||
|
i: 'widget1',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
w: 6,
|
||||||
|
h: 6,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should place widget at (0,0) with custom dimensions when layout is empty', () => {
|
||||||
|
const result = placeWidgetAtBottom('widget1', [], 4, 8);
|
||||||
|
expect(result).toEqual({
|
||||||
|
i: 'widget1',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
w: 4,
|
||||||
|
h: 8,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should place widget next to existing widget in last row if space available', () => {
|
||||||
|
const existingLayout = [{ i: 'widget1', x: 0, y: 0, w: 6, h: 6 }];
|
||||||
|
const result = placeWidgetAtBottom('widget2', existingLayout);
|
||||||
|
expect(result).toEqual({
|
||||||
|
i: 'widget2',
|
||||||
|
x: 6,
|
||||||
|
y: 0,
|
||||||
|
w: 6,
|
||||||
|
h: 6,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should place widget at bottom when last row is full', () => {
|
||||||
|
const existingLayout = [
|
||||||
|
{ i: 'widget1', x: 0, y: 0, w: 6, h: 6 },
|
||||||
|
{ i: 'widget2', x: 6, y: 0, w: 6, h: 6 },
|
||||||
|
];
|
||||||
|
const result = placeWidgetAtBottom('widget3', existingLayout);
|
||||||
|
expect(result).toEqual({
|
||||||
|
i: 'widget3',
|
||||||
|
x: 0,
|
||||||
|
y: 6,
|
||||||
|
w: 6,
|
||||||
|
h: 6,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle multiple rows correctly', () => {
|
||||||
|
const existingLayout = [
|
||||||
|
{ i: 'widget1', x: 0, y: 0, w: 6, h: 6 },
|
||||||
|
{ i: 'widget2', x: 6, y: 0, w: 6, h: 6 },
|
||||||
|
{ i: 'widget3', x: 0, y: 6, w: 6, h: 6 },
|
||||||
|
];
|
||||||
|
const result = placeWidgetAtBottom('widget4', existingLayout);
|
||||||
|
expect(result).toEqual({
|
||||||
|
i: 'widget4',
|
||||||
|
x: 6,
|
||||||
|
y: 6,
|
||||||
|
w: 6,
|
||||||
|
h: 6,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle widgets with different heights', () => {
|
||||||
|
const existingLayout = [
|
||||||
|
{ i: 'widget1', x: 0, y: 0, w: 6, h: 8 },
|
||||||
|
{ i: 'widget2', x: 6, y: 0, w: 6, h: 4 },
|
||||||
|
];
|
||||||
|
const result = placeWidgetAtBottom('widget3', existingLayout);
|
||||||
|
// y = 2 here as later the react-grid-layout will add 2px to the y value while adjusting the layout
|
||||||
|
expect(result).toEqual({
|
||||||
|
i: 'widget3',
|
||||||
|
x: 6,
|
||||||
|
y: 2,
|
||||||
|
w: 6,
|
||||||
|
h: 6,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -58,6 +58,7 @@ import {
|
|||||||
getDefaultWidgetData,
|
getDefaultWidgetData,
|
||||||
getIsQueryModified,
|
getIsQueryModified,
|
||||||
handleQueryChange,
|
handleQueryChange,
|
||||||
|
placeWidgetAtBottom,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||||
@@ -363,20 +364,14 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const widgetId = query.get('widgetId');
|
const widgetId = query.get('widgetId') || '';
|
||||||
let updatedLayout = selectedDashboard.data.layout || [];
|
let updatedLayout = selectedDashboard.data.layout || [];
|
||||||
|
|
||||||
if (isNewDashboard) {
|
if (isNewDashboard) {
|
||||||
updatedLayout = [
|
const newLayoutItem = placeWidgetAtBottom(widgetId, updatedLayout);
|
||||||
{
|
updatedLayout = [...updatedLayout, newLayoutItem];
|
||||||
i: widgetId || '',
|
|
||||||
w: 6,
|
|
||||||
x: 0,
|
|
||||||
h: 6,
|
|
||||||
y: 0,
|
|
||||||
},
|
|
||||||
...updatedLayout,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const dashboard: Dashboard = {
|
const dashboard: Dashboard = {
|
||||||
...selectedDashboard,
|
...selectedDashboard,
|
||||||
uuid: selectedDashboard.uuid,
|
uuid: selectedDashboard.uuid,
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import {
|
|||||||
PANEL_TYPES_INITIAL_QUERY,
|
PANEL_TYPES_INITIAL_QUERY,
|
||||||
} from 'container/NewDashboard/ComponentsSlider/constants';
|
} from 'container/NewDashboard/ComponentsSlider/constants';
|
||||||
import { categoryToSupport } from 'container/QueryBuilder/filters/BuilderUnitsFilter/config';
|
import { categoryToSupport } from 'container/QueryBuilder/filters/BuilderUnitsFilter/config';
|
||||||
import { cloneDeep, isEmpty, isEqual, set, unset } from 'lodash-es';
|
import { cloneDeep, defaultTo, isEmpty, isEqual, set, unset } from 'lodash-es';
|
||||||
|
import { Layout } from 'react-grid-layout';
|
||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
import { IBuilderQuery, Query } from 'types/api/queryBuilder/queryBuilderData';
|
import { IBuilderQuery, Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { EQueryType } from 'types/common/dashboard';
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
@@ -575,3 +576,58 @@ export const unitOptions = (columnUnit: string): DefaultOptionType[] => {
|
|||||||
options: getCategorySelectOptionByName(filteredCategory),
|
options: getCategorySelectOptionByName(filteredCategory),
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const placeWidgetAtBottom = (
|
||||||
|
widgetId: string,
|
||||||
|
layout: Layout[],
|
||||||
|
widgetWidth?: number,
|
||||||
|
widgetHeight?: number,
|
||||||
|
): Layout => {
|
||||||
|
if (layout.length === 0) {
|
||||||
|
return { i: widgetId, x: 0, y: 0, w: widgetWidth || 6, h: widgetHeight || 6 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the maximum Y coordinate and height
|
||||||
|
const { maxY } = layout.reduce(
|
||||||
|
(acc, curr) => ({
|
||||||
|
maxY: Math.max(acc.maxY, curr.y + curr.h),
|
||||||
|
}),
|
||||||
|
{ maxY: 0 },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check for available space in the last row
|
||||||
|
const lastRowWidgets = layout.filter((item) => item.y + item.h === maxY);
|
||||||
|
const occupiedXInLastRow = lastRowWidgets.reduce(
|
||||||
|
(acc, widget) => acc + widget.w,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
// If there's space in the last row (total width < 12)
|
||||||
|
if (occupiedXInLastRow < 12) {
|
||||||
|
// Find the rightmost X coordinate in the last row
|
||||||
|
const maxXInLastRow = lastRowWidgets.reduce(
|
||||||
|
(acc, widget) => Math.max(acc, widget.x + widget.w),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
// If there's enough space for a 6-width widget
|
||||||
|
if (maxXInLastRow + defaultTo(widgetWidth, 6) <= 12) {
|
||||||
|
return {
|
||||||
|
i: widgetId,
|
||||||
|
x: maxXInLastRow,
|
||||||
|
y: maxY - (widgetHeight || 6), // Align with the last row
|
||||||
|
w: widgetWidth || 6,
|
||||||
|
h: widgetHeight || 6,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no space in last row, place at the bottom
|
||||||
|
return {
|
||||||
|
i: widgetId,
|
||||||
|
x: 0,
|
||||||
|
y: maxY,
|
||||||
|
w: widgetWidth || 6,
|
||||||
|
h: widgetHeight || 6,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="line-clamped-text"
|
class="line-clamped-wrapper__text"
|
||||||
>
|
>
|
||||||
demo-app
|
demo-app
|
||||||
</div>
|
</div>
|
||||||
@@ -277,7 +277,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="line-clamped-text"
|
class="line-clamped-wrapper__text"
|
||||||
>
|
>
|
||||||
4.35 s
|
4.35 s
|
||||||
</div>
|
</div>
|
||||||
@@ -292,7 +292,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="line-clamped-text"
|
class="line-clamped-wrapper__text"
|
||||||
>
|
>
|
||||||
customer
|
customer
|
||||||
</div>
|
</div>
|
||||||
@@ -303,7 +303,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="line-clamped-text"
|
class="line-clamped-wrapper__text"
|
||||||
>
|
>
|
||||||
431 ms
|
431 ms
|
||||||
</div>
|
</div>
|
||||||
@@ -318,7 +318,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="line-clamped-text"
|
class="line-clamped-wrapper__text"
|
||||||
>
|
>
|
||||||
mysql
|
mysql
|
||||||
</div>
|
</div>
|
||||||
@@ -329,7 +329,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="line-clamped-text"
|
class="line-clamped-wrapper__text"
|
||||||
>
|
>
|
||||||
431 ms
|
431 ms
|
||||||
</div>
|
</div>
|
||||||
@@ -344,7 +344,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="line-clamped-text"
|
class="line-clamped-wrapper__text"
|
||||||
>
|
>
|
||||||
frontend
|
frontend
|
||||||
</div>
|
</div>
|
||||||
@@ -355,7 +355,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="line-clamped-text"
|
class="line-clamped-wrapper__text"
|
||||||
>
|
>
|
||||||
287 ms
|
287 ms
|
||||||
</div>
|
</div>
|
||||||
@@ -370,7 +370,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="line-clamped-text"
|
class="line-clamped-wrapper__text"
|
||||||
>
|
>
|
||||||
driver
|
driver
|
||||||
</div>
|
</div>
|
||||||
@@ -381,7 +381,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="line-clamped-text"
|
class="line-clamped-wrapper__text"
|
||||||
>
|
>
|
||||||
230 ms
|
230 ms
|
||||||
</div>
|
</div>
|
||||||
@@ -396,7 +396,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="line-clamped-text"
|
class="line-clamped-wrapper__text"
|
||||||
>
|
>
|
||||||
route
|
route
|
||||||
</div>
|
</div>
|
||||||
@@ -407,7 +407,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="line-clamped-text"
|
class="line-clamped-wrapper__text"
|
||||||
>
|
>
|
||||||
66.4 ms
|
66.4 ms
|
||||||
</div>
|
</div>
|
||||||
@@ -422,7 +422,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="line-clamped-text"
|
class="line-clamped-wrapper__text"
|
||||||
>
|
>
|
||||||
redis
|
redis
|
||||||
</div>
|
</div>
|
||||||
@@ -433,7 +433,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="line-clamped-text"
|
class="line-clamped-wrapper__text"
|
||||||
>
|
>
|
||||||
31.3 ms
|
31.3 ms
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -82,6 +82,13 @@ const menuItems: SidebarItem[] = [
|
|||||||
label: 'Logs',
|
label: 'Logs',
|
||||||
icon: <ScrollText size={16} />,
|
icon: <ScrollText size={16} />,
|
||||||
},
|
},
|
||||||
|
// TODO - Enable this when the metrics explorer feature is read for release
|
||||||
|
// {
|
||||||
|
// key: ROUTES.METRICS_EXPLORER,
|
||||||
|
// label: 'Metrics',
|
||||||
|
// icon: <BarChart2 size={16} />,
|
||||||
|
// isNew: true,
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
key: ROUTES.INFRASTRUCTURE_MONITORING_HOSTS,
|
key: ROUTES.INFRASTRUCTURE_MONITORING_HOSTS,
|
||||||
label: 'Infra Monitoring',
|
label: 'Infra Monitoring',
|
||||||
|
|||||||
@@ -219,6 +219,9 @@ export const routesToSkip = [
|
|||||||
ROUTES.INFRASTRUCTURE_MONITORING_HOSTS,
|
ROUTES.INFRASTRUCTURE_MONITORING_HOSTS,
|
||||||
ROUTES.SOMETHING_WENT_WRONG,
|
ROUTES.SOMETHING_WENT_WRONG,
|
||||||
ROUTES.INFRASTRUCTURE_MONITORING_KUBERNETES,
|
ROUTES.INFRASTRUCTURE_MONITORING_KUBERNETES,
|
||||||
|
ROUTES.METRICS_EXPLORER,
|
||||||
|
ROUTES.METRICS_EXPLORER_EXPLORER,
|
||||||
|
ROUTES.METRICS_EXPLORER_VIEWS,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const routesToDisable = [ROUTES.LOGS_EXPLORER, ROUTES.LIVE_LOGS];
|
export const routesToDisable = [ROUTES.LOGS_EXPLORER, ROUTES.LIVE_LOGS];
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { getMs } from 'container/Trace/Filters/Panel/PanelBody/Duration/util';
|
|||||||
import { formUrlParams } from 'container/TraceDetail/utils';
|
import { formUrlParams } from 'container/TraceDetail/utils';
|
||||||
import { TimestampInput } from 'hooks/useTimezoneFormatter/useTimezoneFormatter';
|
import { TimestampInput } from 'hooks/useTimezoneFormatter/useTimezoneFormatter';
|
||||||
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
||||||
|
import LineClampedText from 'periscope/components/LineClampedText/LineClampedText';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { ILog } from 'types/api/logs/log';
|
import { ILog } from 'types/api/logs/log';
|
||||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
@@ -113,7 +114,9 @@ export const getListColumns = (
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<BlockLink to={getTraceLink(item)} openInNewTab={false}>
|
<BlockLink to={getTraceLink(item)} openInNewTab={false}>
|
||||||
<Typography data-testid={key}>{value}</Typography>
|
<Typography data-testid={key}>
|
||||||
|
<LineClampedText text={value} lines={3} />
|
||||||
|
</Typography>
|
||||||
</BlockLink>
|
</BlockLink>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { convertKeysToColumnFields } from 'container/LogsExplorerList/utils';
|
import { convertKeysToColumnFields } from 'container/LogsExplorerList/utils';
|
||||||
|
import { placeWidgetAtBottom } from 'container/NewWidget/utils';
|
||||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
@@ -22,20 +23,16 @@ export const addEmptyWidgetInDashboardJSONWithQuery = (
|
|||||||
...convertKeysToColumnFields(selectedColumns || []),
|
...convertKeysToColumnFields(selectedColumns || []),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const newLayoutItem = placeWidgetAtBottom(
|
||||||
|
widgetId,
|
||||||
|
dashboard?.data?.layout || [],
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...dashboard,
|
...dashboard,
|
||||||
data: {
|
data: {
|
||||||
...dashboard.data,
|
...dashboard.data,
|
||||||
layout: [
|
layout: [...(dashboard?.data?.layout || []), newLayoutItem],
|
||||||
{
|
|
||||||
i: widgetId,
|
|
||||||
w: 6,
|
|
||||||
x: 0,
|
|
||||||
h: 6,
|
|
||||||
y: 0,
|
|
||||||
},
|
|
||||||
...(dashboard?.data?.layout || []),
|
|
||||||
],
|
|
||||||
widgets: [
|
widgets: [
|
||||||
...(dashboard?.data?.widgets || []),
|
...(dashboard?.data?.widgets || []),
|
||||||
{
|
{
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user