Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7554bce11c | ||
|
|
9a6fcb6b1d | ||
|
|
8ef4c0bcdd | ||
|
|
ded2c98167 | ||
|
|
22d4c53a43 | ||
|
|
7a4156a3b7 | ||
|
|
8844144c01 | ||
|
|
f8ec850670 | ||
|
|
ee76cf6294 | ||
|
|
2bf534b56f | ||
|
|
c37d6c3785 | ||
|
|
17c61a61ec | ||
|
|
ec7c99dd26 | ||
|
|
03220fcf11 | ||
|
|
233589b867 | ||
|
|
0946bcd5fc | ||
|
|
1fc0461c0f | ||
|
|
86b725757c | ||
|
|
b6004ce157 | ||
|
|
87c244ccfa | ||
|
|
557ebcec79 | ||
|
|
6363c71442 | ||
|
|
74c4a36e26 | ||
|
|
abb8b2b122 | ||
|
|
c35f2ec0aa | ||
|
|
3b22698e35 | ||
|
|
900752b6e2 | ||
|
|
f47c23032c | ||
|
|
7ad489ebb4 | ||
|
|
3d03ad52b1 | ||
|
|
37349786f1 | ||
|
|
c6ac8df707 | ||
|
|
ae3d4fece8 | ||
|
|
d63ae429bd | ||
|
|
171cea14e2 | ||
|
|
3d5dc05c08 | ||
|
|
d14bd7386e | ||
|
|
afd893b8b5 | ||
|
|
10141a207b | ||
|
|
daebea701d | ||
|
|
7fb29ad2ee | ||
|
|
e61b0a4d67 | ||
|
|
ff392ba883 |
@@ -1,6 +1,7 @@
|
||||
version: "3.9"
|
||||
|
||||
x-clickhouse-defaults: &clickhouse-defaults
|
||||
x-clickhouse-defaults:
|
||||
&clickhouse-defaults
|
||||
image: clickhouse/clickhouse-server:22.8.8-alpine
|
||||
tty: true
|
||||
deploy:
|
||||
@@ -16,7 +17,14 @@ x-clickhouse-defaults: &clickhouse-defaults
|
||||
max-file: "3"
|
||||
healthcheck:
|
||||
# "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'"
|
||||
test: ["CMD", "wget", "--spider", "-q", "localhost:8123/ping"]
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"wget",
|
||||
"--spider",
|
||||
"-q",
|
||||
"localhost:8123/ping"
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
@@ -26,7 +34,8 @@ x-clickhouse-defaults: &clickhouse-defaults
|
||||
soft: 262144
|
||||
hard: 262144
|
||||
|
||||
x-clickhouse-depend: &clickhouse-depend
|
||||
x-clickhouse-depend:
|
||||
&clickhouse-depend
|
||||
depends_on:
|
||||
- clickhouse
|
||||
# - clickhouse-2
|
||||
@@ -124,7 +133,7 @@ services:
|
||||
# - ./data/clickhouse-3/:/var/lib/clickhouse/
|
||||
|
||||
alertmanager:
|
||||
image: signoz/alertmanager:0.23.1
|
||||
image: signoz/alertmanager:0.23.2
|
||||
volumes:
|
||||
- ./data/alertmanager:/data
|
||||
command:
|
||||
@@ -137,8 +146,8 @@ services:
|
||||
condition: on-failure
|
||||
|
||||
query-service:
|
||||
image: signoz/query-service:0.25.2
|
||||
command: ["-config=/root/config/prometheus.yml"]
|
||||
image: signoz/query-service:0.26.0
|
||||
command: [ "-config=/root/config/prometheus.yml" ]
|
||||
# ports:
|
||||
# - "6060:6060" # pprof port
|
||||
# - "8080:8080" # query-service port
|
||||
@@ -156,7 +165,14 @@ services:
|
||||
- TELEMETRY_ENABLED=true
|
||||
- DEPLOYMENT_TYPE=docker-swarm
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--spider", "-q", "localhost:8080/api/v1/health"]
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"wget",
|
||||
"--spider",
|
||||
"-q",
|
||||
"localhost:8080/api/v1/health"
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
@@ -166,7 +182,7 @@ services:
|
||||
<<: *clickhouse-depend
|
||||
|
||||
frontend:
|
||||
image: signoz/frontend:0.25.2
|
||||
image: signoz/frontend:0.26.0
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
@@ -179,8 +195,12 @@ services:
|
||||
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
||||
|
||||
otel-collector:
|
||||
image: signoz/signoz-otel-collector:0.79.4
|
||||
command: ["--config=/etc/otel-collector-config.yaml", "--feature-gates=-pkg.translator.prometheus.NormalizeName"]
|
||||
image: signoz/signoz-otel-collector:0.79.5
|
||||
command:
|
||||
[
|
||||
"--config=/etc/otel-collector-config.yaml",
|
||||
"--feature-gates=-pkg.translator.prometheus.NormalizeName"
|
||||
]
|
||||
user: root # required for reading docker container logs
|
||||
volumes:
|
||||
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
||||
@@ -191,8 +211,8 @@ services:
|
||||
- LOW_CARDINAL_EXCEPTION_GROUPING=false
|
||||
ports:
|
||||
# - "1777:1777" # pprof extension
|
||||
- "4317:4317" # OTLP gRPC receiver
|
||||
- "4318:4318" # OTLP HTTP receiver
|
||||
- "4317:4317" # OTLP gRPC receiver
|
||||
- "4318:4318" # OTLP HTTP receiver
|
||||
# - "8888:8888" # OtelCollector internal metrics
|
||||
# - "8889:8889" # signoz spanmetrics exposed by the agent
|
||||
# - "9411:9411" # Zipkin port
|
||||
@@ -208,8 +228,12 @@ services:
|
||||
<<: *clickhouse-depend
|
||||
|
||||
otel-collector-metrics:
|
||||
image: signoz/signoz-otel-collector:0.79.4
|
||||
command: ["--config=/etc/otel-collector-metrics-config.yaml", "--feature-gates=-pkg.translator.prometheus.NormalizeName"]
|
||||
image: signoz/signoz-otel-collector:0.79.5
|
||||
command:
|
||||
[
|
||||
"--config=/etc/otel-collector-metrics-config.yaml",
|
||||
"--feature-gates=-pkg.translator.prometheus.NormalizeName"
|
||||
]
|
||||
volumes:
|
||||
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
|
||||
# ports:
|
||||
@@ -222,9 +246,22 @@ services:
|
||||
condition: on-failure
|
||||
<<: *clickhouse-depend
|
||||
|
||||
logspout:
|
||||
image: "gliderlabs/logspout:v3.2.14"
|
||||
volumes:
|
||||
- /etc/hostname:/etc/host_hostname:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
command: syslog+tcp://otel-collector:2255
|
||||
depends_on:
|
||||
- otel-collector
|
||||
deploy:
|
||||
mode: global
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
|
||||
hotrod:
|
||||
image: jaegertracing/example-hotrod:1.30
|
||||
command: ["all"]
|
||||
command: [ "all" ]
|
||||
environment:
|
||||
- JAEGER_ENDPOINT=http://otel-collector:14268/api/traces
|
||||
logging:
|
||||
|
||||
@@ -1,29 +1,21 @@
|
||||
receivers:
|
||||
filelog/dockercontainers:
|
||||
include: [ "/var/lib/docker/containers/*/*.log" ]
|
||||
start_at: end
|
||||
include_file_path: true
|
||||
include_file_name: false
|
||||
tcplog/docker:
|
||||
listen_address: "0.0.0.0:2255"
|
||||
operators:
|
||||
- type: json_parser
|
||||
id: parser-docker
|
||||
output: extract_metadata_from_filepath
|
||||
timestamp:
|
||||
parse_from: attributes.time
|
||||
layout: '%Y-%m-%dT%H:%M:%S.%LZ'
|
||||
- type: regex_parser
|
||||
id: extract_metadata_from_filepath
|
||||
regex: '^.*containers/(?P<container_id>[^_]+)/.*log$'
|
||||
parse_from: attributes["log.file.path"]
|
||||
output: parse_body
|
||||
- type: move
|
||||
id: parse_body
|
||||
from: attributes.log
|
||||
to: body
|
||||
output: time
|
||||
- type: remove
|
||||
id: time
|
||||
field: attributes.time
|
||||
- type: regex_parser
|
||||
regex: '^<([0-9]+)>[0-9]+ (?P<timestamp>[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?) (?P<container_id>\S+) (?P<container_name>\S+) [0-9]+ - -( (?P<body>.*))?'
|
||||
timestamp:
|
||||
parse_from: attributes.timestamp
|
||||
layout: '%Y-%m-%dT%H:%M:%S.%LZ'
|
||||
- type: move
|
||||
from: attributes["body"]
|
||||
to: body
|
||||
- type: remove
|
||||
field: attributes.timestamp
|
||||
# please remove names from below if you want to collect logs from them
|
||||
- type: filter
|
||||
id: signoz_logs_filter
|
||||
expr: 'attributes.container_name matches "^signoz_(logspout|frontend|alertmanager|query-service|otel-collector|otel-collector-metrics|clickhouse|zookeeper)"'
|
||||
opencensus:
|
||||
endpoint: 0.0.0.0:55678
|
||||
otlp/spanmetrics:
|
||||
@@ -69,6 +61,40 @@ receivers:
|
||||
job_name: otel-collector
|
||||
|
||||
processors:
|
||||
logstransform/internal:
|
||||
operators:
|
||||
- type: trace_parser
|
||||
if: '"trace_id" in attributes or "span_id" in attributes'
|
||||
trace_id:
|
||||
parse_from: attributes.trace_id
|
||||
span_id:
|
||||
parse_from: attributes.span_id
|
||||
output: remove_trace_id
|
||||
- type: trace_parser
|
||||
if: '"traceId" in attributes or "spanId" in attributes'
|
||||
trace_id:
|
||||
parse_from: attributes.traceId
|
||||
span_id:
|
||||
parse_from: attributes.spanId
|
||||
output: remove_traceId
|
||||
- id: remove_traceId
|
||||
type: remove
|
||||
if: '"traceId" in attributes'
|
||||
field: attributes.traceId
|
||||
output: remove_spanId
|
||||
- id: remove_spanId
|
||||
type: remove
|
||||
if: '"spanId" in attributes'
|
||||
field: attributes.spanId
|
||||
- id: remove_trace_id
|
||||
type: remove
|
||||
if: '"trace_id" in attributes'
|
||||
field: attributes.trace_id
|
||||
output: remove_span_id
|
||||
- id: remove_span_id
|
||||
type: remove
|
||||
if: '"span_id" in attributes'
|
||||
field: attributes.span_id
|
||||
batch:
|
||||
send_batch_size: 10000
|
||||
send_batch_max_size: 11000
|
||||
@@ -166,6 +192,6 @@ service:
|
||||
receivers: [otlp/spanmetrics]
|
||||
exporters: [prometheus]
|
||||
logs:
|
||||
receivers: [otlp, filelog/dockercontainers]
|
||||
processors: [batch]
|
||||
receivers: [otlp, tcplog/docker]
|
||||
processors: [logstransform/internal, batch]
|
||||
exporters: [clickhouselogsexporter]
|
||||
|
||||
@@ -3,7 +3,7 @@ version: "2.4"
|
||||
services:
|
||||
clickhouse:
|
||||
image: clickhouse/clickhouse-server:22.8.8-alpine
|
||||
container_name: clickhouse
|
||||
container_name: signoz-clickhouse
|
||||
# ports:
|
||||
# - "9000:9000"
|
||||
# - "8123:8123"
|
||||
@@ -20,14 +20,21 @@ services:
|
||||
max-file: "3"
|
||||
healthcheck:
|
||||
# "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'"
|
||||
test: ["CMD", "wget", "--spider", "-q", "localhost:8123/ping"]
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"wget",
|
||||
"--spider",
|
||||
"-q",
|
||||
"localhost:8123/ping"
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
alertmanager:
|
||||
container_name: alertmanager
|
||||
image: signoz/alertmanager:0.23.1
|
||||
container_name: signoz-alertmanager
|
||||
image: signoz/alertmanager:0.23.2
|
||||
volumes:
|
||||
- ./data/alertmanager:/data
|
||||
depends_on:
|
||||
@@ -40,9 +47,13 @@ services:
|
||||
|
||||
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
|
||||
otel-collector:
|
||||
container_name: otel-collector
|
||||
image: signoz/signoz-otel-collector:0.79.4
|
||||
command: ["--config=/etc/otel-collector-config.yaml", "--feature-gates=-pkg.translator.prometheus.NormalizeName"]
|
||||
container_name: signoz-otel-collector
|
||||
image: signoz/signoz-otel-collector:0.79.5
|
||||
command:
|
||||
[
|
||||
"--config=/etc/otel-collector-config.yaml",
|
||||
"--feature-gates=-pkg.translator.prometheus.NormalizeName"
|
||||
]
|
||||
# user: root # required for reading docker container logs
|
||||
volumes:
|
||||
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
||||
@@ -50,8 +61,8 @@ services:
|
||||
- OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux
|
||||
ports:
|
||||
# - "1777:1777" # pprof extension
|
||||
- "4317:4317" # OTLP gRPC receiver
|
||||
- "4318:4318" # OTLP HTTP receiver
|
||||
- "4317:4317" # OTLP gRPC receiver
|
||||
- "4318:4318" # OTLP HTTP receiver
|
||||
# - "8888:8888" # OtelCollector internal metrics
|
||||
# - "8889:8889" # signoz spanmetrics exposed by the agent
|
||||
# - "9411:9411" # Zipkin port
|
||||
@@ -66,9 +77,13 @@ services:
|
||||
condition: service_healthy
|
||||
|
||||
otel-collector-metrics:
|
||||
container_name: otel-collector-metrics
|
||||
image: signoz/signoz-otel-collector:0.79.4
|
||||
command: ["--config=/etc/otel-collector-metrics-config.yaml", "--feature-gates=-pkg.translator.prometheus.NormalizeName"]
|
||||
container_name: signoz-otel-collector-metrics
|
||||
image: signoz/signoz-otel-collector:0.79.5
|
||||
command:
|
||||
[
|
||||
"--config=/etc/otel-collector-metrics-config.yaml",
|
||||
"--feature-gates=-pkg.translator.prometheus.NormalizeName"
|
||||
]
|
||||
volumes:
|
||||
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
|
||||
# ports:
|
||||
@@ -81,6 +96,17 @@ services:
|
||||
clickhouse:
|
||||
condition: service_healthy
|
||||
|
||||
logspout:
|
||||
image: "gliderlabs/logspout:v3.2.14"
|
||||
container_name: signoz-logspout
|
||||
volumes:
|
||||
- /etc/hostname:/etc/host_hostname:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
command: syslog+tcp://otel-collector:2255
|
||||
depends_on:
|
||||
- otel-collector
|
||||
restart: on-failure
|
||||
|
||||
hotrod:
|
||||
image: jaegertracing/example-hotrod:1.30
|
||||
container_name: hotrod
|
||||
@@ -88,7 +114,7 @@ services:
|
||||
options:
|
||||
max-size: 50m
|
||||
max-file: "3"
|
||||
command: ["all"]
|
||||
command: [ "all" ]
|
||||
environment:
|
||||
- JAEGER_ENDPOINT=http://otel-collector:14268/api/traces
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ services:
|
||||
args:
|
||||
LDFLAGS: ""
|
||||
TARGETPLATFORM: "${LOCAL_GOOS}/${LOCAL_GOARCH}"
|
||||
container_name: query-service
|
||||
container_name: signoz-query-service
|
||||
environment:
|
||||
- ClickHouseUrl=tcp://clickhouse:9000
|
||||
- ALERTMANAGER_API_PREFIX=http://alertmanager:9093/api/
|
||||
@@ -22,13 +22,20 @@ services:
|
||||
- ./prometheus.yml:/root/config/prometheus.yml
|
||||
- ../dashboards:/root/config/dashboards
|
||||
- ./data/signoz/:/var/lib/signoz/
|
||||
command: ["-config=/root/config/prometheus.yml"]
|
||||
command: [ "-config=/root/config/prometheus.yml" ]
|
||||
ports:
|
||||
- "6060:6060"
|
||||
- "8080:8080"
|
||||
restart: on-failure
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--spider", "-q", "localhost:8080/api/v1/health"]
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"wget",
|
||||
"--spider",
|
||||
"-q",
|
||||
"localhost:8080/api/v1/health"
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
@@ -43,7 +50,7 @@ services:
|
||||
args:
|
||||
TARGETOS: "${LOCAL_GOOS}"
|
||||
TARGETPLATFORM: "${LOCAL_GOARCH}"
|
||||
container_name: frontend
|
||||
container_name: signoz-frontend
|
||||
environment:
|
||||
- FRONTEND_API_ENDPOINT=http://query-service:8080
|
||||
restart: on-failure
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
version: "2.4"
|
||||
|
||||
x-clickhouse-defaults: &clickhouse-defaults
|
||||
x-clickhouse-defaults:
|
||||
&clickhouse-defaults
|
||||
restart: on-failure
|
||||
image: clickhouse/clickhouse-server:22.8.8-alpine
|
||||
tty: true
|
||||
@@ -14,7 +15,14 @@ x-clickhouse-defaults: &clickhouse-defaults
|
||||
max-file: "3"
|
||||
healthcheck:
|
||||
# "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'"
|
||||
test: ["CMD", "wget", "--spider", "-q", "localhost:8123/ping"]
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"wget",
|
||||
"--spider",
|
||||
"-q",
|
||||
"localhost:8123/ping"
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
@@ -24,7 +32,8 @@ x-clickhouse-defaults: &clickhouse-defaults
|
||||
soft: 262144
|
||||
hard: 262144
|
||||
|
||||
x-clickhouse-depend: &clickhouse-depend
|
||||
x-clickhouse-depend:
|
||||
&clickhouse-depend
|
||||
depends_on:
|
||||
clickhouse:
|
||||
condition: service_healthy
|
||||
@@ -37,7 +46,7 @@ services:
|
||||
|
||||
zookeeper-1:
|
||||
image: bitnami/zookeeper:3.7.1
|
||||
container_name: zookeeper-1
|
||||
container_name: signoz-zookeeper-1
|
||||
hostname: zookeeper-1
|
||||
user: root
|
||||
ports:
|
||||
@@ -54,7 +63,7 @@ services:
|
||||
|
||||
# zookeeper-2:
|
||||
# image: bitnami/zookeeper:3.7.0
|
||||
# container_name: zookeeper-2
|
||||
# container_name: signoz-zookeeper-2
|
||||
# hostname: zookeeper-2
|
||||
# user: root
|
||||
# ports:
|
||||
@@ -71,7 +80,7 @@ services:
|
||||
|
||||
# zookeeper-3:
|
||||
# image: bitnami/zookeeper:3.7.0
|
||||
# container_name: zookeeper-3
|
||||
# container_name: signoz-zookeeper-3
|
||||
# hostname: zookeeper-3
|
||||
# user: root
|
||||
# ports:
|
||||
@@ -88,7 +97,7 @@ services:
|
||||
|
||||
clickhouse:
|
||||
<<: *clickhouse-defaults
|
||||
container_name: clickhouse
|
||||
container_name: signoz-clickhouse
|
||||
hostname: clickhouse
|
||||
ports:
|
||||
- "9000:9000"
|
||||
@@ -105,7 +114,7 @@ services:
|
||||
|
||||
# clickhouse-2:
|
||||
# <<: *clickhouse-defaults
|
||||
# container_name: clickhouse-2
|
||||
# container_name: signoz-clickhouse-2
|
||||
# hostname: clickhouse-2
|
||||
# ports:
|
||||
# - "9001:9000"
|
||||
@@ -120,10 +129,10 @@ services:
|
||||
# - ./data/clickhouse-2/:/var/lib/clickhouse/
|
||||
# - ./user_scripts:/var/lib/clickhouse/user_scripts/
|
||||
|
||||
|
||||
|
||||
# clickhouse-3:
|
||||
# <<: *clickhouse-defaults
|
||||
# container_name: clickhouse-3
|
||||
# container_name: signoz-clickhouse-3
|
||||
# hostname: clickhouse-3
|
||||
# ports:
|
||||
# - "9002:9000"
|
||||
@@ -139,7 +148,8 @@ services:
|
||||
# - ./user_scripts:/var/lib/clickhouse/user_scripts/
|
||||
|
||||
alertmanager:
|
||||
image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.1}
|
||||
image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.2}
|
||||
container_name: signoz-alertmanager
|
||||
volumes:
|
||||
- ./data/alertmanager:/data
|
||||
depends_on:
|
||||
@@ -153,9 +163,9 @@ services:
|
||||
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
|
||||
|
||||
query-service:
|
||||
image: signoz/query-service:${DOCKER_TAG:-0.25.2}
|
||||
container_name: query-service
|
||||
command: ["-config=/root/config/prometheus.yml"]
|
||||
image: signoz/query-service:${DOCKER_TAG:-0.26.0}
|
||||
container_name: signoz-query-service
|
||||
command: [ "-config=/root/config/prometheus.yml" ]
|
||||
# ports:
|
||||
# - "6060:6060" # pprof port
|
||||
# - "8080:8080" # query-service port
|
||||
@@ -174,15 +184,22 @@ services:
|
||||
- DEPLOYMENT_TYPE=docker-standalone-amd
|
||||
restart: on-failure
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--spider", "-q", "localhost:8080/api/v1/health"]
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"wget",
|
||||
"--spider",
|
||||
"-q",
|
||||
"localhost:8080/api/v1/health"
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
<<: *clickhouse-depend
|
||||
|
||||
frontend:
|
||||
image: signoz/frontend:${DOCKER_TAG:-0.25.2}
|
||||
container_name: frontend
|
||||
image: signoz/frontend:${DOCKER_TAG:-0.26.0}
|
||||
container_name: signoz-frontend
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
- alertmanager
|
||||
@@ -193,8 +210,13 @@ services:
|
||||
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
||||
|
||||
otel-collector:
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.79.4}
|
||||
command: ["--config=/etc/otel-collector-config.yaml", "--feature-gates=-pkg.translator.prometheus.NormalizeName"]
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.79.5}
|
||||
container_name: signoz-otel-collector
|
||||
command:
|
||||
[
|
||||
"--config=/etc/otel-collector-config.yaml",
|
||||
"--feature-gates=-pkg.translator.prometheus.NormalizeName"
|
||||
]
|
||||
user: root # required for reading docker container logs
|
||||
volumes:
|
||||
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
||||
@@ -205,8 +227,8 @@ services:
|
||||
- LOW_CARDINAL_EXCEPTION_GROUPING=false
|
||||
ports:
|
||||
# - "1777:1777" # pprof extension
|
||||
- "4317:4317" # OTLP gRPC receiver
|
||||
- "4318:4318" # OTLP HTTP receiver
|
||||
- "4317:4317" # OTLP gRPC receiver
|
||||
- "4318:4318" # OTLP HTTP receiver
|
||||
# - "8888:8888" # OtelCollector internal metrics
|
||||
# - "8889:8889" # signoz spanmetrics exposed by the agent
|
||||
# - "9411:9411" # Zipkin port
|
||||
@@ -219,8 +241,13 @@ services:
|
||||
<<: *clickhouse-depend
|
||||
|
||||
otel-collector-metrics:
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.79.4}
|
||||
command: ["--config=/etc/otel-collector-metrics-config.yaml", "--feature-gates=-pkg.translator.prometheus.NormalizeName"]
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.79.5}
|
||||
container_name: signoz-otel-collector-metrics
|
||||
command:
|
||||
[
|
||||
"--config=/etc/otel-collector-metrics-config.yaml",
|
||||
"--feature-gates=-pkg.translator.prometheus.NormalizeName"
|
||||
]
|
||||
volumes:
|
||||
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
|
||||
# ports:
|
||||
@@ -231,6 +258,17 @@ services:
|
||||
restart: on-failure
|
||||
<<: *clickhouse-depend
|
||||
|
||||
logspout:
|
||||
image: "gliderlabs/logspout:v3.2.14"
|
||||
container_name: signoz-logspout
|
||||
volumes:
|
||||
- /etc/hostname:/etc/host_hostname:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
command: syslog+tcp://otel-collector:2255
|
||||
depends_on:
|
||||
- otel-collector
|
||||
restart: on-failure
|
||||
|
||||
hotrod:
|
||||
image: jaegertracing/example-hotrod:1.30
|
||||
container_name: hotrod
|
||||
@@ -238,7 +276,7 @@ services:
|
||||
options:
|
||||
max-size: 50m
|
||||
max-file: "3"
|
||||
command: ["all"]
|
||||
command: [ "all" ]
|
||||
environment:
|
||||
- JAEGER_ENDPOINT=http://otel-collector:14268/api/traces
|
||||
|
||||
|
||||
@@ -1,29 +1,21 @@
|
||||
receivers:
|
||||
filelog/dockercontainers:
|
||||
include: [ "/var/lib/docker/containers/*/*.log" ]
|
||||
start_at: end
|
||||
include_file_path: true
|
||||
include_file_name: false
|
||||
tcplog/docker:
|
||||
listen_address: "0.0.0.0:2255"
|
||||
operators:
|
||||
- type: json_parser
|
||||
id: parser-docker
|
||||
output: extract_metadata_from_filepath
|
||||
timestamp:
|
||||
parse_from: attributes.time
|
||||
layout: '%Y-%m-%dT%H:%M:%S.%LZ'
|
||||
- type: regex_parser
|
||||
id: extract_metadata_from_filepath
|
||||
regex: '^.*containers/(?P<container_id>[^_]+)/.*log$'
|
||||
parse_from: attributes["log.file.path"]
|
||||
output: parse_body
|
||||
- type: move
|
||||
id: parse_body
|
||||
from: attributes.log
|
||||
to: body
|
||||
output: time
|
||||
- type: remove
|
||||
id: time
|
||||
field: attributes.time
|
||||
- type: regex_parser
|
||||
regex: '^<([0-9]+)>[0-9]+ (?P<timestamp>[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?) (?P<container_id>\S+) (?P<container_name>\S+) [0-9]+ - -( (?P<body>.*))?'
|
||||
timestamp:
|
||||
parse_from: attributes.timestamp
|
||||
layout: '%Y-%m-%dT%H:%M:%S.%LZ'
|
||||
- type: move
|
||||
from: attributes["body"]
|
||||
to: body
|
||||
- type: remove
|
||||
field: attributes.timestamp
|
||||
# please remove names from below if you want to collect logs from them
|
||||
- type: filter
|
||||
id: signoz_logs_filter
|
||||
expr: 'attributes.container_name matches "^signoz-(logspout|frontend|alertmanager|query-service|otel-collector|otel-collector-metrics|clickhouse|zookeeper)"'
|
||||
opencensus:
|
||||
endpoint: 0.0.0.0:55678
|
||||
otlp/spanmetrics:
|
||||
@@ -205,6 +197,6 @@ service:
|
||||
receivers: [otlp/spanmetrics]
|
||||
exporters: [prometheus]
|
||||
logs:
|
||||
receivers: [otlp, filelog/dockercontainers]
|
||||
receivers: [otlp, tcplog/docker]
|
||||
processors: [logstransform/internal, batch]
|
||||
exporters: [clickhouselogsexporter]
|
||||
@@ -36,9 +36,9 @@ is_mac() {
|
||||
[[ $OSTYPE == darwin* ]]
|
||||
}
|
||||
|
||||
# is_arm64(){
|
||||
# [[ `uname -m` == 'arm64' ]]
|
||||
# }
|
||||
is_arm64(){
|
||||
[[ `uname -m` == 'arm64' || `uname -m` == 'aarch64' ]]
|
||||
}
|
||||
|
||||
check_os() {
|
||||
if is_mac; then
|
||||
@@ -48,6 +48,16 @@ check_os() {
|
||||
return
|
||||
fi
|
||||
|
||||
if is_arm64; then
|
||||
arch="arm64"
|
||||
arch_official="aarch64"
|
||||
else
|
||||
arch="amd64"
|
||||
arch_official="x86_64"
|
||||
fi
|
||||
|
||||
platform=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
os_name="$(cat /etc/*-release | awk -F= '$1 == "NAME" { gsub(/"/, ""); print $2; exit }')"
|
||||
|
||||
case "$os_name" in
|
||||
@@ -143,7 +153,7 @@ install_docker() {
|
||||
$apt_cmd install software-properties-common gnupg-agent
|
||||
curl -fsSL "https://download.docker.com/linux/$os/gpg" | $sudo_cmd apt-key add -
|
||||
$sudo_cmd add-apt-repository \
|
||||
"deb [arch=amd64] https://download.docker.com/linux/$os $(lsb_release -cs) stable"
|
||||
"deb [arch=$arch] https://download.docker.com/linux/$os $(lsb_release -cs) stable"
|
||||
$apt_cmd update
|
||||
echo "Installing docker"
|
||||
$apt_cmd install docker-ce docker-ce-cli containerd.io
|
||||
@@ -178,12 +188,20 @@ install_docker() {
|
||||
|
||||
}
|
||||
|
||||
compose_version () {
|
||||
local compose_version
|
||||
compose_version="$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep 'tag_name' | cut -d\" -f4)"
|
||||
echo "${compose_version:-v2.18.1}"
|
||||
}
|
||||
|
||||
install_docker_compose() {
|
||||
if [[ $package_manager == "apt-get" || $package_manager == "zypper" || $package_manager == "yum" ]]; then
|
||||
if [[ ! -f /usr/bin/docker-compose ]];then
|
||||
echo "++++++++++++++++++++++++"
|
||||
echo "Installing docker-compose"
|
||||
$sudo_cmd curl -L "https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
compose_url="https://github.com/docker/compose/releases/download/$(compose_version)/docker-compose-$platform-$arch_official"
|
||||
echo "Downloading docker-compose from $compose_url"
|
||||
$sudo_cmd curl -L "$compose_url" -o /usr/local/bin/docker-compose
|
||||
$sudo_cmd chmod +x /usr/local/bin/docker-compose
|
||||
$sudo_cmd ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
|
||||
echo "docker-compose installed!"
|
||||
|
||||
@@ -39,6 +39,9 @@ COPY --from=builder /go/src/github.com/signoz/signoz/ee/query-service/bin/query-
|
||||
# copy prometheus YAML config
|
||||
COPY pkg/query-service/config/prometheus.yml /root/config/prometheus.yml
|
||||
|
||||
# Make query-service executable for non-root users
|
||||
RUN chmod 755 /root /root/query-service
|
||||
|
||||
# run the binary
|
||||
ENTRYPOINT ["./query-service"]
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"go.signoz.io/signoz/ee/query-service/dao"
|
||||
@@ -20,6 +21,9 @@ type APIHandlerOptions struct {
|
||||
SkipConfig *basemodel.SkipConfig
|
||||
PreferDelta bool
|
||||
PreferSpanMetrics bool
|
||||
MaxIdleConns int
|
||||
MaxOpenConns int
|
||||
DialTimeout time.Duration
|
||||
AppDao dao.ModelDao
|
||||
RulesManager *rules.Manager
|
||||
FeatureFlags baseint.FeatureLookup
|
||||
@@ -40,6 +44,9 @@ func NewAPIHandler(opts APIHandlerOptions) (*APIHandler, error) {
|
||||
SkipConfig: opts.SkipConfig,
|
||||
PerferDelta: opts.PreferDelta,
|
||||
PreferSpanMetrics: opts.PreferSpanMetrics,
|
||||
MaxIdleConns: opts.MaxIdleConns,
|
||||
MaxOpenConns: opts.MaxOpenConns,
|
||||
DialTimeout: opts.DialTimeout,
|
||||
AppDao: opts.AppDao,
|
||||
RuleManager: opts.RulesManager,
|
||||
FeatureFlags: opts.FeatureFlags,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
@@ -15,8 +17,15 @@ type ClickhouseReader struct {
|
||||
*basechr.ClickHouseReader
|
||||
}
|
||||
|
||||
func NewDataConnector(localDB *sqlx.DB, promConfigPath string, lm interfaces.FeatureLookup) *ClickhouseReader {
|
||||
ch := basechr.NewReader(localDB, promConfigPath, lm)
|
||||
func NewDataConnector(
|
||||
localDB *sqlx.DB,
|
||||
promConfigPath string,
|
||||
lm interfaces.FeatureLookup,
|
||||
maxIdleConns int,
|
||||
maxOpenConns int,
|
||||
dialTimeout time.Duration,
|
||||
) *ClickhouseReader {
|
||||
ch := basechr.NewReader(localDB, promConfigPath, lm, maxIdleConns, maxOpenConns, dialTimeout)
|
||||
return &ClickhouseReader{
|
||||
conn: ch.GetConn(),
|
||||
appdb: localDB,
|
||||
|
||||
@@ -59,6 +59,9 @@ type ServerOptions struct {
|
||||
RuleRepoURL string
|
||||
PreferDelta bool
|
||||
PreferSpanMetrics bool
|
||||
MaxIdleConns int
|
||||
MaxOpenConns int
|
||||
DialTimeout time.Duration
|
||||
}
|
||||
|
||||
// Server runs HTTP api service
|
||||
@@ -122,7 +125,14 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
|
||||
storage := os.Getenv("STORAGE")
|
||||
if storage == "clickhouse" {
|
||||
zap.S().Info("Using ClickHouse as datastore ...")
|
||||
qb := db.NewDataConnector(localDB, serverOptions.PromConfigPath, lm)
|
||||
qb := db.NewDataConnector(
|
||||
localDB,
|
||||
serverOptions.PromConfigPath,
|
||||
lm,
|
||||
serverOptions.MaxIdleConns,
|
||||
serverOptions.MaxOpenConns,
|
||||
serverOptions.DialTimeout,
|
||||
)
|
||||
go qb.Start(readerReady)
|
||||
reader = qb
|
||||
} else {
|
||||
@@ -184,6 +194,9 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
|
||||
SkipConfig: skipConfig,
|
||||
PreferDelta: serverOptions.PreferDelta,
|
||||
PreferSpanMetrics: serverOptions.PreferSpanMetrics,
|
||||
MaxIdleConns: serverOptions.MaxIdleConns,
|
||||
MaxOpenConns: serverOptions.MaxOpenConns,
|
||||
DialTimeout: serverOptions.DialTimeout,
|
||||
AppDao: modelDao,
|
||||
RulesManager: rm,
|
||||
FeatureFlags: lm,
|
||||
|
||||
@@ -86,11 +86,18 @@ func main() {
|
||||
var preferDelta bool
|
||||
var preferSpanMetrics bool
|
||||
|
||||
var maxIdleConns int
|
||||
var maxOpenConns int
|
||||
var dialTimeout time.Duration
|
||||
|
||||
flag.StringVar(&promConfigPath, "config", "./config/prometheus.yml", "(prometheus config to read metrics)")
|
||||
flag.StringVar(&skipTopLvlOpsPath, "skip-top-level-ops", "", "(config file to skip top level operations)")
|
||||
flag.BoolVar(&disableRules, "rules.disable", false, "(disable rule evaluation)")
|
||||
flag.BoolVar(&preferDelta, "prefer-delta", false, "(prefer delta over cumulative metrics)")
|
||||
flag.BoolVar(&preferSpanMetrics, "prefer-span-metrics", false, "(prefer span metrics for service level metrics)")
|
||||
flag.IntVar(&maxIdleConns, "max-idle-conns", 50, "(number of connections to maintain in the pool.)")
|
||||
flag.IntVar(&maxOpenConns, "max-open-conns", 100, "(max connections for use at any time.)")
|
||||
flag.DurationVar(&dialTimeout, "dial-timeout", 5*time.Second, "(the maximum time to establish a connection.)")
|
||||
flag.StringVar(&ruleRepoURL, "rules.repo-url", baseconst.AlertHelpPage, "(host address used to build rule link in alert messages)")
|
||||
flag.BoolVar(&enableQueryServiceLogOTLPExport, "enable.query.service.log.otlp.export", false, "(enable query service log otlp export)")
|
||||
flag.Parse()
|
||||
@@ -111,6 +118,9 @@ func main() {
|
||||
PrivateHostPort: baseconst.PrivateHostPort,
|
||||
DisableRules: disableRules,
|
||||
RuleRepoURL: ruleRepoURL,
|
||||
MaxIdleConns: maxIdleConns,
|
||||
MaxOpenConns: maxOpenConns,
|
||||
DialTimeout: dialTimeout,
|
||||
}
|
||||
|
||||
// Read the jwt secret key
|
||||
|
||||
@@ -60,6 +60,34 @@ var BasicPlan = basemodel.FeatureSet{
|
||||
UsageLimit: 5,
|
||||
Route: "",
|
||||
},
|
||||
basemodel.Feature{
|
||||
Name: basemodel.AlertChannelSlack,
|
||||
Active: true,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
basemodel.Feature{
|
||||
Name: basemodel.AlertChannelWebhook,
|
||||
Active: true,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
basemodel.Feature{
|
||||
Name: basemodel.AlertChannelPagerduty,
|
||||
Active: true,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
basemodel.Feature{
|
||||
Name: basemodel.AlertChannelMsTeams,
|
||||
Active: false,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
basemodel.Feature{
|
||||
Name: basemodel.UseSpanMetrics,
|
||||
Active: false,
|
||||
@@ -112,6 +140,34 @@ var ProPlan = basemodel.FeatureSet{
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
basemodel.Feature{
|
||||
Name: basemodel.AlertChannelSlack,
|
||||
Active: true,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
basemodel.Feature{
|
||||
Name: basemodel.AlertChannelWebhook,
|
||||
Active: true,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
basemodel.Feature{
|
||||
Name: basemodel.AlertChannelPagerduty,
|
||||
Active: true,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
basemodel.Feature{
|
||||
Name: basemodel.AlertChannelMsTeams,
|
||||
Active: true,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
basemodel.Feature{
|
||||
Name: basemodel.UseSpanMetrics,
|
||||
Active: false,
|
||||
@@ -164,6 +220,34 @@ var EnterprisePlan = basemodel.FeatureSet{
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
basemodel.Feature{
|
||||
Name: basemodel.AlertChannelSlack,
|
||||
Active: true,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
basemodel.Feature{
|
||||
Name: basemodel.AlertChannelWebhook,
|
||||
Active: true,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
basemodel.Feature{
|
||||
Name: basemodel.AlertChannelPagerduty,
|
||||
Active: true,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
basemodel.Feature{
|
||||
Name: basemodel.AlertChannelMsTeams,
|
||||
Active: true,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
basemodel.Feature{
|
||||
Name: basemodel.UseSpanMetrics,
|
||||
Active: false,
|
||||
|
||||
@@ -24,7 +24,7 @@ COPY . .
|
||||
RUN yarn build
|
||||
|
||||
|
||||
FROM nginx:1.18-alpine
|
||||
FROM nginx:1.24.0-alpine
|
||||
|
||||
COPY conf/default.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
|
||||
@@ -1,112 +1,113 @@
|
||||
{
|
||||
"target_missing": "Please enter a threshold to proceed",
|
||||
"rule_test_fired": "Test notification sent successfully",
|
||||
"no_alerts_found": "No alerts found during the evaluation. This happens when rule condition is unsatisfied. You may adjust the rule threshold and retry.",
|
||||
"button_testrule": "Test Notification",
|
||||
"label_channel_select": "Notification Channels",
|
||||
"placeholder_channel_select": "select one or more channels",
|
||||
"channel_select_tooltip": "Leave empty to send this alert on all the configured channels",
|
||||
"preview_chart_unexpected_error": "An unexpeced error occurred updating the chart, please check your query.",
|
||||
"preview_chart_threshold_label": "Threshold",
|
||||
"placeholder_label_key_pair": "Click here to enter a label (key value pairs)",
|
||||
"button_yes": "Yes",
|
||||
"button_no": "No",
|
||||
"remove_label_confirm": "This action will remove all the labels. Do you want to proceed?",
|
||||
"remove_label_success": "Labels cleared",
|
||||
"alert_form_step1": "Step 1 - Define the metric",
|
||||
"alert_form_step2": "Step 2 - Define Alert Conditions",
|
||||
"alert_form_step3": "Step 3 - Alert Configuration",
|
||||
"metric_query_max_limit": "Can not create query. You can create maximum of 5 queries",
|
||||
"confirm_save_title": "Save Changes",
|
||||
"confirm_save_content_part1": "Your alert built with",
|
||||
"confirm_save_content_part2": "query will be saved. Press OK to confirm.",
|
||||
"unexpected_error": "Sorry, an unexpected error occurred. Please contact your admin",
|
||||
"rule_created": "Rule created successfully",
|
||||
"rule_edited": "Rule edited successfully",
|
||||
"expression_missing": "expression is missing in {{where}}",
|
||||
"metricname_missing": "metric name is missing in {{where}}",
|
||||
"condition_required": "at least one metric condition is required",
|
||||
"alertname_required": "alert name is required",
|
||||
"promql_required": "promql expression is required when query format is set to PromQL",
|
||||
"chquery_required": "query is required when query format is set to ClickHouse",
|
||||
"button_savechanges": "Save Rule",
|
||||
"button_createrule": "Create Rule",
|
||||
"button_returntorules": "Return to rules",
|
||||
"button_cancelchanges": "Cancel",
|
||||
"button_discard": "Discard",
|
||||
"text_condition1": "Send a notification when the metric is",
|
||||
"text_condition2": "the threshold",
|
||||
"text_condition3": "during the last",
|
||||
"option_5min": "5 mins",
|
||||
"option_10min": "10 mins",
|
||||
"option_15min": "15 mins",
|
||||
"option_60min": "60 mins",
|
||||
"option_4hours": "4 hours",
|
||||
"option_24hours": "24 hours",
|
||||
"field_threshold": "Alert Threshold",
|
||||
"option_allthetimes": "all the times",
|
||||
"option_atleastonce": "at least once",
|
||||
"option_onaverage": "on average",
|
||||
"option_intotal": "in total",
|
||||
"option_above": "above",
|
||||
"option_below": "below",
|
||||
"option_equal": "is equal to",
|
||||
"option_notequal": "not equal to",
|
||||
"button_query": "Query",
|
||||
"button_formula": "Formula",
|
||||
"tab_qb": "Query Builder",
|
||||
"tab_promql": "PromQL",
|
||||
"tab_chquery": "ClickHouse Query",
|
||||
"title_confirm": "Confirm",
|
||||
"button_ok": "Yes",
|
||||
"button_cancel": "No",
|
||||
"field_promql_expr": "PromQL Expression",
|
||||
"field_alert_name": "Alert Name",
|
||||
"field_alert_desc": "Alert Description",
|
||||
"field_labels": "Labels",
|
||||
"field_severity": "Severity",
|
||||
"option_critical": "Critical",
|
||||
"option_error": "Error",
|
||||
"option_warning": "Warning",
|
||||
"option_info": "Info",
|
||||
"user_guide_headline": "Steps to create an Alert",
|
||||
"user_guide_qb_step1": "Step 1 - Define the metric",
|
||||
"user_guide_qb_step1a": "Choose a metric which you want to create an alert on",
|
||||
"user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed",
|
||||
"user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric",
|
||||
"user_guide_qb_step1d": "Create a formula based on Queries if needed",
|
||||
"user_guide_qb_step2": "Step 2 - Define Alert Conditions",
|
||||
"user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value",
|
||||
"user_guide_qb_step2b": "Enter the Alert threshold",
|
||||
"user_guide_qb_step3": "Step 3 -Alert Configuration",
|
||||
"user_guide_qb_step3a": "Set alert severity, name and descriptions",
|
||||
"user_guide_qb_step3b": "Add tags to the alert in the Label field if needed",
|
||||
"user_guide_pql_step1": "Step 1 - Define the metric",
|
||||
"user_guide_pql_step1a": "Write a PromQL query for the metric",
|
||||
"user_guide_pql_step1b": "Format the legends based on labels you want to highlight",
|
||||
"user_guide_pql_step2": "Step 2 - Define Alert Conditions",
|
||||
"user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value",
|
||||
"user_guide_pql_step2b": "Enter the Alert threshold",
|
||||
"user_guide_pql_step3": "Step 3 -Alert Configuration",
|
||||
"user_guide_pql_step3a": "Set alert severity, name and descriptions",
|
||||
"user_guide_pql_step3b": "Add tags to the alert in the Label field if needed",
|
||||
"user_guide_ch_step1": "Step 1 - Define the metric",
|
||||
"user_guide_ch_step1a": "Write a Clickhouse query for alert evaluation. Follow <0>this tutorial</0> to learn about query format and supported vars.",
|
||||
"user_guide_ch_step1b": "Format the legends based on labels you want to highlight in the preview chart",
|
||||
"user_guide_ch_step2": "Step 2 - Define Alert Conditions",
|
||||
"user_guide_ch_step2a": "Select the threshold type and whether you want to alert above/below a value",
|
||||
"user_guide_ch_step2b": "Enter the Alert threshold",
|
||||
"user_guide_ch_step3": "Step 3 -Alert Configuration",
|
||||
"user_guide_ch_step3a": "Set alert severity, name and descriptions",
|
||||
"user_guide_ch_step3b": "Add tags to the alert in the Label field if needed",
|
||||
"user_tooltip_more_help": "More details on how to create alerts",
|
||||
"choose_alert_type": "Choose a type for the alert:",
|
||||
"metric_based_alert": "Metric based Alert",
|
||||
"metric_based_alert_desc": "Send a notification when a condition occurs in the metric data",
|
||||
"log_based_alert": "Log-based Alert",
|
||||
"log_based_alert_desc": "Send a notification when a condition occurs in the logs data.",
|
||||
"traces_based_alert": "Trace-based Alert",
|
||||
"traces_based_alert_desc": "Send a notification when a condition occurs in the traces data.",
|
||||
"exceptions_based_alert": "Exceptions-based Alert",
|
||||
"exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data."
|
||||
}
|
||||
"target_missing": "Please enter a threshold to proceed",
|
||||
"rule_test_fired": "Test notification sent successfully",
|
||||
"no_alerts_found": "No alerts found during the evaluation. This happens when rule condition is unsatisfied. You may adjust the rule threshold and retry.",
|
||||
"button_testrule": "Test Notification",
|
||||
"label_channel_select": "Notification Channels",
|
||||
"placeholder_channel_select": "select one or more channels",
|
||||
"channel_select_tooltip": "Leave empty to send this alert on all the configured channels",
|
||||
"preview_chart_unexpected_error": "An unexpeced error occurred updating the chart, please check your query.",
|
||||
"preview_chart_threshold_label": "Threshold",
|
||||
"placeholder_label_key_pair": "Click here to enter a label (key value pairs)",
|
||||
"button_yes": "Yes",
|
||||
"button_no": "No",
|
||||
"remove_label_confirm": "This action will remove all the labels. Do you want to proceed?",
|
||||
"remove_label_success": "Labels cleared",
|
||||
"alert_form_step1": "Step 1 - Define the metric",
|
||||
"alert_form_step2": "Step 2 - Define Alert Conditions",
|
||||
"alert_form_step3": "Step 3 - Alert Configuration",
|
||||
"metric_query_max_limit": "Can not create query. You can create maximum of 5 queries",
|
||||
"confirm_save_title": "Save Changes",
|
||||
"confirm_save_content_part1": "Your alert built with",
|
||||
"confirm_save_content_part2": "query will be saved. Press OK to confirm.",
|
||||
"unexpected_error": "Sorry, an unexpected error occurred. Please contact your admin",
|
||||
"rule_created": "Rule created successfully",
|
||||
"rule_edited": "Rule edited successfully",
|
||||
"expression_missing": "expression is missing in {{where}}",
|
||||
"metricname_missing": "metric name is missing in {{where}}",
|
||||
"condition_required": "at least one metric condition is required",
|
||||
"alertname_required": "alert name is required",
|
||||
"promql_required": "promql expression is required when query format is set to PromQL",
|
||||
"chquery_required": "query is required when query format is set to ClickHouse",
|
||||
"button_savechanges": "Save Rule",
|
||||
"button_createrule": "Create Rule",
|
||||
"button_returntorules": "Return to rules",
|
||||
"button_cancelchanges": "Cancel",
|
||||
"button_discard": "Discard",
|
||||
"text_condition1": "Send a notification when the metric is",
|
||||
"text_condition2": "the threshold",
|
||||
"text_condition3": "during the last",
|
||||
"option_5min": "5 mins",
|
||||
"option_10min": "10 mins",
|
||||
"option_15min": "15 mins",
|
||||
"option_60min": "60 mins",
|
||||
"option_4hours": "4 hours",
|
||||
"option_24hours": "24 hours",
|
||||
"field_threshold": "Alert Threshold",
|
||||
"option_allthetimes": "all the times",
|
||||
"option_atleastonce": "at least once",
|
||||
"option_onaverage": "on average",
|
||||
"option_intotal": "in total",
|
||||
"option_above": "above",
|
||||
"option_below": "below",
|
||||
"option_equal": "is equal to",
|
||||
"option_notequal": "not equal to",
|
||||
"button_query": "Query",
|
||||
"button_formula": "Formula",
|
||||
"tab_qb": "Query Builder",
|
||||
"tab_promql": "PromQL",
|
||||
"tab_chquery": "ClickHouse Query",
|
||||
"title_confirm": "Confirm",
|
||||
"button_ok": "Yes",
|
||||
"button_cancel": "No",
|
||||
"field_promql_expr": "PromQL Expression",
|
||||
"field_alert_name": "Alert Name",
|
||||
"field_alert_desc": "Alert Description",
|
||||
"field_labels": "Labels",
|
||||
"field_severity": "Severity",
|
||||
"option_critical": "Critical",
|
||||
"option_error": "Error",
|
||||
"option_warning": "Warning",
|
||||
"option_info": "Info",
|
||||
"user_guide_headline": "Steps to create an Alert",
|
||||
"user_guide_qb_step1": "Step 1 - Define the metric",
|
||||
"user_guide_qb_step1a": "Choose a metric which you want to create an alert on",
|
||||
"user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed",
|
||||
"user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric",
|
||||
"user_guide_qb_step1d": "Create a formula based on Queries if needed",
|
||||
"user_guide_qb_step2": "Step 2 - Define Alert Conditions",
|
||||
"user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value",
|
||||
"user_guide_qb_step2b": "Enter the Alert threshold",
|
||||
"user_guide_qb_step3": "Step 3 -Alert Configuration",
|
||||
"user_guide_qb_step3a": "Set alert severity, name and descriptions",
|
||||
"user_guide_qb_step3b": "Add tags to the alert in the Label field if needed",
|
||||
"user_guide_pql_step1": "Step 1 - Define the metric",
|
||||
"user_guide_pql_step1a": "Write a PromQL query for the metric",
|
||||
"user_guide_pql_step1b": "Format the legends based on labels you want to highlight",
|
||||
"user_guide_pql_step2": "Step 2 - Define Alert Conditions",
|
||||
"user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value",
|
||||
"user_guide_pql_step2b": "Enter the Alert threshold",
|
||||
"user_guide_pql_step3": "Step 3 -Alert Configuration",
|
||||
"user_guide_pql_step3a": "Set alert severity, name and descriptions",
|
||||
"user_guide_pql_step3b": "Add tags to the alert in the Label field if needed",
|
||||
"user_guide_ch_step1": "Step 1 - Define the metric",
|
||||
"user_guide_ch_step1a": "Write a Clickhouse query for alert evaluation. Follow <0>this tutorial</0> to learn about query format and supported vars.",
|
||||
"user_guide_ch_step1b": "Format the legends based on labels you want to highlight in the preview chart",
|
||||
"user_guide_ch_step2": "Step 2 - Define Alert Conditions",
|
||||
"user_guide_ch_step2a": "Select the threshold type and whether you want to alert above/below a value",
|
||||
"user_guide_ch_step2b": "Enter the Alert threshold",
|
||||
"user_guide_ch_step3": "Step 3 -Alert Configuration",
|
||||
"user_guide_ch_step3a": "Set alert severity, name and descriptions",
|
||||
"user_guide_ch_step3b": "Add tags to the alert in the Label field if needed",
|
||||
"user_tooltip_more_help": "More details on how to create alerts",
|
||||
"choose_alert_type": "Choose a type for the alert:",
|
||||
"metric_based_alert": "Metric based Alert",
|
||||
"metric_based_alert_desc": "Send a notification when a condition occurs in the metric data",
|
||||
"log_based_alert": "Log-based Alert",
|
||||
"log_based_alert_desc": "Send a notification when a condition occurs in the logs data.",
|
||||
"traces_based_alert": "Trace-based Alert",
|
||||
"traces_based_alert_desc": "Send a notification when a condition occurs in the traces data.",
|
||||
"exceptions_based_alert": "Exceptions-based Alert",
|
||||
"exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data.",
|
||||
"field_unit": "Threshold unit"
|
||||
}
|
||||
|
||||
@@ -1,112 +1,113 @@
|
||||
{
|
||||
"target_missing": "Please enter a threshold to proceed",
|
||||
"rule_test_fired": "Test notification sent successfully",
|
||||
"no_alerts_found": "No alerts found during the evaluation. This happens when rule condition is unsatisfied. You may adjust the rule threshold and retry.",
|
||||
"button_testrule": "Test Notification",
|
||||
"label_channel_select": "Notification Channels",
|
||||
"placeholder_channel_select": "select one or more channels",
|
||||
"channel_select_tooltip": "Leave empty to send this alert on all the configured channels",
|
||||
"preview_chart_unexpected_error": "An unexpeced error occurred updating the chart, please check your query.",
|
||||
"preview_chart_threshold_label": "Threshold",
|
||||
"placeholder_label_key_pair": "Click here to enter a label (key value pairs)",
|
||||
"button_yes": "Yes",
|
||||
"button_no": "No",
|
||||
"remove_label_confirm": "This action will remove all the labels. Do you want to proceed?",
|
||||
"remove_label_success": "Labels cleared",
|
||||
"alert_form_step1": "Step 1 - Define the metric",
|
||||
"alert_form_step2": "Step 2 - Define Alert Conditions",
|
||||
"alert_form_step3": "Step 3 - Alert Configuration",
|
||||
"metric_query_max_limit": "Can not create query. You can create maximum of 5 queries",
|
||||
"confirm_save_title": "Save Changes",
|
||||
"confirm_save_content_part1": "Your alert built with",
|
||||
"confirm_save_content_part2": "query will be saved. Press OK to confirm.",
|
||||
"unexpected_error": "Sorry, an unexpected error occurred. Please contact your admin",
|
||||
"rule_created": "Rule created successfully",
|
||||
"rule_edited": "Rule edited successfully",
|
||||
"expression_missing": "expression is missing in {{where}}",
|
||||
"metricname_missing": "metric name is missing in {{where}}",
|
||||
"condition_required": "at least one metric condition is required",
|
||||
"alertname_required": "alert name is required",
|
||||
"promql_required": "promql expression is required when query format is set to PromQL",
|
||||
"chquery_required": "query is required when query format is set to ClickHouse",
|
||||
"button_savechanges": "Save Rule",
|
||||
"button_createrule": "Create Rule",
|
||||
"button_returntorules": "Return to rules",
|
||||
"button_cancelchanges": "Cancel",
|
||||
"button_discard": "Discard",
|
||||
"text_condition1": "Send a notification when the metric is",
|
||||
"text_condition2": "the threshold",
|
||||
"text_condition3": "during the last",
|
||||
"option_5min": "5 mins",
|
||||
"option_10min": "10 mins",
|
||||
"option_15min": "15 mins",
|
||||
"option_60min": "60 mins",
|
||||
"option_4hours": "4 hours",
|
||||
"option_24hours": "24 hours",
|
||||
"field_threshold": "Alert Threshold",
|
||||
"option_allthetimes": "all the times",
|
||||
"option_atleastonce": "at least once",
|
||||
"option_onaverage": "on average",
|
||||
"option_intotal": "in total",
|
||||
"option_above": "above",
|
||||
"option_below": "below",
|
||||
"option_equal": "is equal to",
|
||||
"option_notequal": "not equal to",
|
||||
"button_query": "Query",
|
||||
"button_formula": "Formula",
|
||||
"tab_qb": "Query Builder",
|
||||
"tab_promql": "PromQL",
|
||||
"tab_chquery": "ClickHouse Query",
|
||||
"title_confirm": "Confirm",
|
||||
"button_ok": "Yes",
|
||||
"button_cancel": "No",
|
||||
"field_promql_expr": "PromQL Expression",
|
||||
"field_alert_name": "Alert Name",
|
||||
"field_alert_desc": "Alert Description",
|
||||
"field_labels": "Labels",
|
||||
"field_severity": "Severity",
|
||||
"option_critical": "Critical",
|
||||
"option_error": "Error",
|
||||
"option_warning": "Warning",
|
||||
"option_info": "Info",
|
||||
"user_guide_headline": "Steps to create an Alert",
|
||||
"user_guide_qb_step1": "Step 1 - Define the metric",
|
||||
"user_guide_qb_step1a": "Choose a metric which you want to create an alert on",
|
||||
"user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed",
|
||||
"user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric",
|
||||
"user_guide_qb_step1d": "Create a formula based on Queries if needed",
|
||||
"user_guide_qb_step2": "Step 2 - Define Alert Conditions",
|
||||
"user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value",
|
||||
"user_guide_qb_step2b": "Enter the Alert threshold",
|
||||
"user_guide_qb_step3": "Step 3 -Alert Configuration",
|
||||
"user_guide_qb_step3a": "Set alert severity, name and descriptions",
|
||||
"user_guide_qb_step3b": "Add tags to the alert in the Label field if needed",
|
||||
"user_guide_pql_step1": "Step 1 - Define the metric",
|
||||
"user_guide_pql_step1a": "Write a PromQL query for the metric",
|
||||
"user_guide_pql_step1b": "Format the legends based on labels you want to highlight",
|
||||
"user_guide_pql_step2": "Step 2 - Define Alert Conditions",
|
||||
"user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value",
|
||||
"user_guide_pql_step2b": "Enter the Alert threshold",
|
||||
"user_guide_pql_step3": "Step 3 -Alert Configuration",
|
||||
"user_guide_pql_step3a": "Set alert severity, name and descriptions",
|
||||
"user_guide_pql_step3b": "Add tags to the alert in the Label field if needed",
|
||||
"user_guide_ch_step1": "Step 1 - Define the metric",
|
||||
"user_guide_ch_step1a": "Write a Clickhouse query for alert evaluation. Follow <0>this tutorial</0> to learn about query format and supported vars.",
|
||||
"user_guide_ch_step1b": "Format the legends based on labels you want to highlight in the preview chart",
|
||||
"user_guide_ch_step2": "Step 2 - Define Alert Conditions",
|
||||
"user_guide_ch_step2a": "Select the threshold type and whether you want to alert above/below a value",
|
||||
"user_guide_ch_step2b": "Enter the Alert threshold",
|
||||
"user_guide_ch_step3": "Step 3 -Alert Configuration",
|
||||
"user_guide_ch_step3a": "Set alert severity, name and descriptions",
|
||||
"user_guide_ch_step3b": "Add tags to the alert in the Label field if needed",
|
||||
"user_tooltip_more_help": "More details on how to create alerts",
|
||||
"choose_alert_type": "Choose a type for the alert:",
|
||||
"metric_based_alert": "Metric based Alert",
|
||||
"metric_based_alert_desc": "Send a notification when a condition occurs in the metric data",
|
||||
"log_based_alert": "Log-based Alert",
|
||||
"log_based_alert_desc": "Send a notification when a condition occurs in the logs data.",
|
||||
"traces_based_alert": "Trace-based Alert",
|
||||
"traces_based_alert_desc": "Send a notification when a condition occurs in the traces data.",
|
||||
"exceptions_based_alert": "Exceptions-based Alert",
|
||||
"exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data."
|
||||
}
|
||||
"target_missing": "Please enter a threshold to proceed",
|
||||
"rule_test_fired": "Test notification sent successfully",
|
||||
"no_alerts_found": "No alerts found during the evaluation. This happens when rule condition is unsatisfied. You may adjust the rule threshold and retry.",
|
||||
"button_testrule": "Test Notification",
|
||||
"label_channel_select": "Notification Channels",
|
||||
"placeholder_channel_select": "select one or more channels",
|
||||
"channel_select_tooltip": "Leave empty to send this alert on all the configured channels",
|
||||
"preview_chart_unexpected_error": "An unexpeced error occurred updating the chart, please check your query.",
|
||||
"preview_chart_threshold_label": "Threshold",
|
||||
"placeholder_label_key_pair": "Click here to enter a label (key value pairs)",
|
||||
"button_yes": "Yes",
|
||||
"button_no": "No",
|
||||
"remove_label_confirm": "This action will remove all the labels. Do you want to proceed?",
|
||||
"remove_label_success": "Labels cleared",
|
||||
"alert_form_step1": "Step 1 - Define the metric",
|
||||
"alert_form_step2": "Step 2 - Define Alert Conditions",
|
||||
"alert_form_step3": "Step 3 - Alert Configuration",
|
||||
"metric_query_max_limit": "Can not create query. You can create maximum of 5 queries",
|
||||
"confirm_save_title": "Save Changes",
|
||||
"confirm_save_content_part1": "Your alert built with",
|
||||
"confirm_save_content_part2": "query will be saved. Press OK to confirm.",
|
||||
"unexpected_error": "Sorry, an unexpected error occurred. Please contact your admin",
|
||||
"rule_created": "Rule created successfully",
|
||||
"rule_edited": "Rule edited successfully",
|
||||
"expression_missing": "expression is missing in {{where}}",
|
||||
"metricname_missing": "metric name is missing in {{where}}",
|
||||
"condition_required": "at least one metric condition is required",
|
||||
"alertname_required": "alert name is required",
|
||||
"promql_required": "promql expression is required when query format is set to PromQL",
|
||||
"chquery_required": "query is required when query format is set to ClickHouse",
|
||||
"button_savechanges": "Save Rule",
|
||||
"button_createrule": "Create Rule",
|
||||
"button_returntorules": "Return to rules",
|
||||
"button_cancelchanges": "Cancel",
|
||||
"button_discard": "Discard",
|
||||
"text_condition1": "Send a notification when the metric is",
|
||||
"text_condition2": "the threshold",
|
||||
"text_condition3": "during the last",
|
||||
"option_5min": "5 mins",
|
||||
"option_10min": "10 mins",
|
||||
"option_15min": "15 mins",
|
||||
"option_60min": "60 mins",
|
||||
"option_4hours": "4 hours",
|
||||
"option_24hours": "24 hours",
|
||||
"field_threshold": "Alert Threshold",
|
||||
"option_allthetimes": "all the times",
|
||||
"option_atleastonce": "at least once",
|
||||
"option_onaverage": "on average",
|
||||
"option_intotal": "in total",
|
||||
"option_above": "above",
|
||||
"option_below": "below",
|
||||
"option_equal": "is equal to",
|
||||
"option_notequal": "not equal to",
|
||||
"button_query": "Query",
|
||||
"button_formula": "Formula",
|
||||
"tab_qb": "Query Builder",
|
||||
"tab_promql": "PromQL",
|
||||
"tab_chquery": "ClickHouse Query",
|
||||
"title_confirm": "Confirm",
|
||||
"button_ok": "Yes",
|
||||
"button_cancel": "No",
|
||||
"field_promql_expr": "PromQL Expression",
|
||||
"field_alert_name": "Alert Name",
|
||||
"field_alert_desc": "Alert Description",
|
||||
"field_labels": "Labels",
|
||||
"field_severity": "Severity",
|
||||
"option_critical": "Critical",
|
||||
"option_error": "Error",
|
||||
"option_warning": "Warning",
|
||||
"option_info": "Info",
|
||||
"user_guide_headline": "Steps to create an Alert",
|
||||
"user_guide_qb_step1": "Step 1 - Define the metric",
|
||||
"user_guide_qb_step1a": "Choose a metric which you want to create an alert on",
|
||||
"user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed",
|
||||
"user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric",
|
||||
"user_guide_qb_step1d": "Create a formula based on Queries if needed",
|
||||
"user_guide_qb_step2": "Step 2 - Define Alert Conditions",
|
||||
"user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value",
|
||||
"user_guide_qb_step2b": "Enter the Alert threshold",
|
||||
"user_guide_qb_step3": "Step 3 -Alert Configuration",
|
||||
"user_guide_qb_step3a": "Set alert severity, name and descriptions",
|
||||
"user_guide_qb_step3b": "Add tags to the alert in the Label field if needed",
|
||||
"user_guide_pql_step1": "Step 1 - Define the metric",
|
||||
"user_guide_pql_step1a": "Write a PromQL query for the metric",
|
||||
"user_guide_pql_step1b": "Format the legends based on labels you want to highlight",
|
||||
"user_guide_pql_step2": "Step 2 - Define Alert Conditions",
|
||||
"user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value",
|
||||
"user_guide_pql_step2b": "Enter the Alert threshold",
|
||||
"user_guide_pql_step3": "Step 3 -Alert Configuration",
|
||||
"user_guide_pql_step3a": "Set alert severity, name and descriptions",
|
||||
"user_guide_pql_step3b": "Add tags to the alert in the Label field if needed",
|
||||
"user_guide_ch_step1": "Step 1 - Define the metric",
|
||||
"user_guide_ch_step1a": "Write a Clickhouse query for alert evaluation. Follow <0>this tutorial</0> to learn about query format and supported vars.",
|
||||
"user_guide_ch_step1b": "Format the legends based on labels you want to highlight in the preview chart",
|
||||
"user_guide_ch_step2": "Step 2 - Define Alert Conditions",
|
||||
"user_guide_ch_step2a": "Select the threshold type and whether you want to alert above/below a value",
|
||||
"user_guide_ch_step2b": "Enter the Alert threshold",
|
||||
"user_guide_ch_step3": "Step 3 -Alert Configuration",
|
||||
"user_guide_ch_step3a": "Set alert severity, name and descriptions",
|
||||
"user_guide_ch_step3b": "Add tags to the alert in the Label field if needed",
|
||||
"user_tooltip_more_help": "More details on how to create alerts",
|
||||
"choose_alert_type": "Choose a type for the alert:",
|
||||
"metric_based_alert": "Metric based Alert",
|
||||
"metric_based_alert_desc": "Send a notification when a condition occurs in the metric data",
|
||||
"log_based_alert": "Log-based Alert",
|
||||
"log_based_alert_desc": "Send a notification when a condition occurs in the logs data.",
|
||||
"traces_based_alert": "Trace-based Alert",
|
||||
"traces_based_alert_desc": "Send a notification when a condition occurs in the traces data.",
|
||||
"exceptions_based_alert": "Exceptions-based Alert",
|
||||
"exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data.",
|
||||
"field_unit": "Threshold unit"
|
||||
}
|
||||
|
||||
34
frontend/src/api/channels/createMsTeams.ts
Normal file
34
frontend/src/api/channels/createMsTeams.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createMsTeams';
|
||||
|
||||
const create = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post('/channels', {
|
||||
name: props.name,
|
||||
msteams_configs: [
|
||||
{
|
||||
send_resolved: true,
|
||||
webhook_url: props.webhook_url,
|
||||
title: props.title,
|
||||
text: props.text,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
export default create;
|
||||
34
frontend/src/api/channels/editMsTeams.ts
Normal file
34
frontend/src/api/channels/editMsTeams.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/editMsTeams';
|
||||
|
||||
const editMsTeams = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.put(`/channels/${props.id}`, {
|
||||
name: props.name,
|
||||
msteams_configs: [
|
||||
{
|
||||
send_resolved: true,
|
||||
webhook_url: props.webhook_url,
|
||||
title: props.title,
|
||||
text: props.text,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
export default editMsTeams;
|
||||
34
frontend/src/api/channels/testMsTeams.ts
Normal file
34
frontend/src/api/channels/testMsTeams.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createMsTeams';
|
||||
|
||||
const testMsTeams = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post('/testChannel', {
|
||||
name: props.name,
|
||||
msteams_configs: [
|
||||
{
|
||||
send_resolved: true,
|
||||
webhook_url: props.webhook_url,
|
||||
title: props.title,
|
||||
text: props.text,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
export default testMsTeams;
|
||||
16
frontend/src/api/metrics/ApDex/apDexSettings.ts
Normal file
16
frontend/src/api/metrics/ApDex/apDexSettings.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import axios from 'api';
|
||||
import {
|
||||
ApDexPayloadAndSettingsProps,
|
||||
SetApDexPayloadProps,
|
||||
} from 'types/api/metrics/getApDex';
|
||||
|
||||
export const setApDexSettings = async ({
|
||||
servicename,
|
||||
threshold,
|
||||
excludeStatusCode,
|
||||
}: ApDexPayloadAndSettingsProps): Promise<SetApDexPayloadProps> =>
|
||||
axios.post('/settings/apdex', {
|
||||
servicename,
|
||||
threshold,
|
||||
excludeStatusCode,
|
||||
});
|
||||
8
frontend/src/api/metrics/ApDex/getApDexSettings.ts
Normal file
8
frontend/src/api/metrics/ApDex/getApDexSettings.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import axios from 'api';
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { ApDexPayloadAndSettingsProps } from 'types/api/metrics/getApDex';
|
||||
|
||||
export const getApDexSettings = (
|
||||
servicename: string,
|
||||
): Promise<AxiosResponse<ApDexPayloadAndSettingsProps[]>> =>
|
||||
axios.get(`/settings/apdex?services=${servicename}`);
|
||||
8
frontend/src/api/metrics/ApDex/getMetricMeta.ts
Normal file
8
frontend/src/api/metrics/ApDex/getMetricMeta.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import axios from 'api';
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { MetricMetaProps } from 'types/api/metrics/getApDex';
|
||||
|
||||
export const getMetricMeta = (
|
||||
metricName: string,
|
||||
): Promise<AxiosResponse<MetricMetaProps>> =>
|
||||
axios.get(`/metric_meta?metricName=${metricName}`);
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
BarElement,
|
||||
CategoryScale,
|
||||
Chart,
|
||||
ChartType,
|
||||
Decimation,
|
||||
Filler,
|
||||
Legend,
|
||||
@@ -18,6 +17,7 @@ import {
|
||||
Tooltip,
|
||||
} from 'chart.js';
|
||||
import annotationPlugin from 'chartjs-plugin-annotation';
|
||||
import { generateGridTitle } from 'container/GridPanelSwitch/utils';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
import {
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useMemo,
|
||||
useRef,
|
||||
} from 'react';
|
||||
|
||||
@@ -83,6 +84,7 @@ const Graph = forwardRef<ToggleGraphProps | undefined, GraphProps>(
|
||||
const nearestDatasetIndex = useRef<null | number>(null);
|
||||
const chartRef = useRef<HTMLCanvasElement>(null);
|
||||
const isDarkMode = useIsDarkMode();
|
||||
const gridTitle = useMemo(() => generateGridTitle(title), [title]);
|
||||
|
||||
const currentTheme = isDarkMode ? 'dark' : 'light';
|
||||
const xAxisTimeUnit = useXAxisTimeUnit(data); // Computes the relevant time unit for x axis by analyzing the time stamp data
|
||||
@@ -119,7 +121,7 @@ const Graph = forwardRef<ToggleGraphProps | undefined, GraphProps>(
|
||||
const options: CustomChartOptions = getGraphOptions(
|
||||
animate,
|
||||
staticLine,
|
||||
title,
|
||||
gridTitle,
|
||||
nearestDatasetIndex,
|
||||
yAxisUnit,
|
||||
onDragSelect,
|
||||
@@ -154,7 +156,7 @@ const Graph = forwardRef<ToggleGraphProps | undefined, GraphProps>(
|
||||
}, [
|
||||
animate,
|
||||
staticLine,
|
||||
title,
|
||||
gridTitle,
|
||||
yAxisUnit,
|
||||
onDragSelect,
|
||||
dragSelectColor,
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
ChartType,
|
||||
TimeUnit,
|
||||
} from 'chart.js';
|
||||
import { ForwardedRef } from 'react';
|
||||
import { ForwardedRef, ReactNode } from 'react';
|
||||
|
||||
import {
|
||||
dragSelectPluginId,
|
||||
@@ -49,7 +49,7 @@ export interface GraphProps {
|
||||
animate?: boolean;
|
||||
type: ChartType;
|
||||
data: Chart['data'];
|
||||
title?: string;
|
||||
title?: ReactNode;
|
||||
isStacked?: boolean;
|
||||
onClickHandler?: GraphOnClickHandler;
|
||||
name: string;
|
||||
|
||||
@@ -10,6 +10,7 @@ export type LogsTableViewProps = {
|
||||
logs: ILog[];
|
||||
fields: IField[];
|
||||
linesPerRow: number;
|
||||
onClickExpand?: (log: ILog) => void;
|
||||
};
|
||||
|
||||
export type UseTableViewResult = {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { grey } from '@ant-design/colors';
|
||||
import { QuestionCircleFilled } from '@ant-design/icons';
|
||||
import { blue, grey } from '@ant-design/colors';
|
||||
import {
|
||||
QuestionCircleFilled,
|
||||
QuestionCircleOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Tooltip } from 'antd';
|
||||
import { themeColors } from 'constants/theme';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
@@ -7,7 +10,12 @@ import { useMemo } from 'react';
|
||||
|
||||
import { style } from './styles';
|
||||
|
||||
function TextToolTip({ text, url }: TextToolTipProps): JSX.Element {
|
||||
function TextToolTip({
|
||||
text,
|
||||
url,
|
||||
useFilledIcon = true,
|
||||
urlText,
|
||||
}: TextToolTipProps): JSX.Element {
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
const overlay = useMemo(
|
||||
@@ -16,12 +24,12 @@ function TextToolTip({ text, url }: TextToolTipProps): JSX.Element {
|
||||
{`${text} `}
|
||||
{url && (
|
||||
<a href={url} rel="noopener noreferrer" target="_blank">
|
||||
here
|
||||
{urlText || 'here'}
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
[text, url],
|
||||
[text, url, urlText],
|
||||
);
|
||||
|
||||
const iconStyle = useMemo(
|
||||
@@ -32,19 +40,35 @@ function TextToolTip({ text, url }: TextToolTipProps): JSX.Element {
|
||||
[isDarkMode],
|
||||
);
|
||||
|
||||
const iconOutlinedStyle = useMemo(
|
||||
() => ({
|
||||
...style,
|
||||
color: isDarkMode ? themeColors.navyBlue : blue[0],
|
||||
}),
|
||||
[isDarkMode],
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip overlay={overlay}>
|
||||
<QuestionCircleFilled style={iconStyle} />
|
||||
{useFilledIcon ? (
|
||||
<QuestionCircleFilled style={iconStyle} />
|
||||
) : (
|
||||
<QuestionCircleOutlined style={iconOutlinedStyle} />
|
||||
)}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
TextToolTip.defaultProps = {
|
||||
url: '',
|
||||
urlText: '',
|
||||
useFilledIcon: true,
|
||||
};
|
||||
interface TextToolTipProps {
|
||||
url?: string;
|
||||
text: string;
|
||||
useFilledIcon?: boolean;
|
||||
urlText?: string;
|
||||
}
|
||||
|
||||
export default TextToolTip;
|
||||
|
||||
31
frontend/src/components/Upgrade/UpgradePrompt.tsx
Normal file
31
frontend/src/components/Upgrade/UpgradePrompt.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Alert, Space } from 'antd';
|
||||
import { SIGNOZ_UPGRADE_PLAN_URL } from 'constants/app';
|
||||
|
||||
type UpgradePromptProps = {
|
||||
title?: string;
|
||||
};
|
||||
|
||||
function UpgradePrompt({ title }: UpgradePromptProps): JSX.Element {
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<Alert
|
||||
message={title}
|
||||
description={
|
||||
<div>
|
||||
This feature is available for paid plans only.{' '}
|
||||
<a href={SIGNOZ_UPGRADE_PLAN_URL} target="_blank" rel="noreferrer">
|
||||
Click here
|
||||
</a>{' '}
|
||||
to Upgrade
|
||||
</div>
|
||||
}
|
||||
type="warning"
|
||||
/>{' '}
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
||||
UpgradePrompt.defaultProps = {
|
||||
title: 'Upgrade to a Paid Plan',
|
||||
};
|
||||
export default UpgradePrompt;
|
||||
5
frontend/src/constants/apDex.ts
Normal file
5
frontend/src/constants/apDex.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const apDexToolTipText =
|
||||
"Apdex is a way to measure your users' satisfaction with the response time of your web service. It's represented as a score from 0-1.";
|
||||
export const apDexToolTipUrl =
|
||||
'https://signoz.io/docs/userguide/metrics/#apdex?utm_source=product&utm_medium=frontend&utm_campaign=apdex';
|
||||
export const apDexToolTipUrlText = 'Learn more about Apdex.';
|
||||
@@ -1,6 +1,12 @@
|
||||
// keep this consistent with backend constants.go
|
||||
export enum FeatureKeys {
|
||||
SSO = 'SSO',
|
||||
ENTERPRISE_PLAN = 'ENTERPRISE_PLAN',
|
||||
BASIC_PLAN = 'BASIC_PLAN',
|
||||
ALERT_CHANNEL_SLACK = 'ALERT_CHANNEL_SLACK',
|
||||
ALERT_CHANNEL_WEBHOOK = 'ALERT_CHANNEL_WEBHOOK',
|
||||
ALERT_CHANNEL_PAGERDUTY = 'ALERT_CHANNEL_PAGERDUTY',
|
||||
ALERT_CHANNEL_MSTEAMS = 'ALERT_CHANNEL_MSTEAMS',
|
||||
DurationSort = 'DurationSort',
|
||||
TimestampSort = 'TimestampSort',
|
||||
SMART_TRACE_DETAIL = 'SMART_TRACE_DETAIL',
|
||||
@@ -9,4 +15,5 @@ export enum FeatureKeys {
|
||||
QUERY_BUILDER_ALERTS = 'QUERY_BUILDER_ALERTS',
|
||||
DISABLE_UPSELL = 'DISABLE_UPSELL',
|
||||
USE_SPAN_METRICS = 'USE_SPAN_METRICS',
|
||||
OSS = 'OSS',
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
QuestionCircleFilled,
|
||||
QuestionCircleOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Dropdown, Space } from 'antd';
|
||||
import { Space } from 'antd';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
@@ -13,6 +13,7 @@ import { ConfigProps } from 'types/api/dynamicConfigs/getDynamicConfigs';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
|
||||
import HelpToolTip from './Config';
|
||||
import { ConfigDropdown } from './styles';
|
||||
|
||||
function DynamicConfigDropdown({
|
||||
frontendId,
|
||||
@@ -53,19 +54,17 @@ function DynamicConfigDropdown({
|
||||
const DropDownIcon = isHelpDropDownOpen ? CaretUpFilled : CaretDownFilled;
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
<ConfigDropdown
|
||||
onOpenChange={onToggleHandler}
|
||||
trigger={['click']}
|
||||
menu={menu}
|
||||
open={isHelpDropDownOpen}
|
||||
>
|
||||
<Space align="center">
|
||||
<Icon
|
||||
style={{ fontSize: 26, color: 'white', paddingTop: 26, cursor: 'pointer' }}
|
||||
/>
|
||||
<Icon style={{ fontSize: 26, color: 'white', paddingTop: 26 }} />
|
||||
<DropDownIcon style={{ color: 'white' }} />
|
||||
</Space>
|
||||
</Dropdown>
|
||||
</ConfigDropdown>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
6
frontend/src/container/ConfigDropdown/styles.ts
Normal file
6
frontend/src/container/ConfigDropdown/styles.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { Dropdown } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const ConfigDropdown = styled(Dropdown)`
|
||||
cursor: pointer;
|
||||
`;
|
||||
@@ -63,10 +63,16 @@ export const ValidatePagerChannel = (p: PagerChannel): string => {
|
||||
return '';
|
||||
};
|
||||
|
||||
export type ChannelType = 'slack' | 'email' | 'webhook' | 'pagerduty';
|
||||
export type ChannelType =
|
||||
| 'slack'
|
||||
| 'email'
|
||||
| 'webhook'
|
||||
| 'pagerduty'
|
||||
| 'msteams';
|
||||
export const SlackType: ChannelType = 'slack';
|
||||
export const WebhookType: ChannelType = 'webhook';
|
||||
export const PagerType: ChannelType = 'pagerduty';
|
||||
export const MsTeamsType: ChannelType = 'msteams';
|
||||
|
||||
// LabelFilterStatement will be used for preparing filter conditions / matchers
|
||||
export interface LabelFilterStatement {
|
||||
@@ -81,3 +87,9 @@ export interface LabelFilterStatement {
|
||||
// filter value
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface MsTeamsChannel extends Channel {
|
||||
webhook_url?: string;
|
||||
title?: string;
|
||||
text?: string;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Form } from 'antd';
|
||||
import createMsTeamsApi from 'api/channels/createMsTeams';
|
||||
import createPagerApi from 'api/channels/createPager';
|
||||
import createSlackApi from 'api/channels/createSlack';
|
||||
import createWebhookApi from 'api/channels/createWebhook';
|
||||
import testMsTeamsApi from 'api/channels/testMsTeams';
|
||||
import testPagerApi from 'api/channels/testPager';
|
||||
import testSlackApi from 'api/channels/testSlack';
|
||||
import testWebhookApi from 'api/channels/testWebhook';
|
||||
@@ -14,6 +16,8 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import {
|
||||
ChannelType,
|
||||
MsTeamsChannel,
|
||||
MsTeamsType,
|
||||
PagerChannel,
|
||||
PagerType,
|
||||
SlackChannel,
|
||||
@@ -33,7 +37,7 @@ function CreateAlertChannels({
|
||||
const [formInstance] = Form.useForm();
|
||||
|
||||
const [selectedConfig, setSelectedConfig] = useState<
|
||||
Partial<SlackChannel & WebhookChannel & PagerChannel>
|
||||
Partial<SlackChannel & WebhookChannel & PagerChannel & MsTeamsChannel>
|
||||
>({
|
||||
text: `{{ range .Alerts -}}
|
||||
*Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }}
|
||||
@@ -102,9 +106,7 @@ function CreateAlertChannels({
|
||||
message: 'Success',
|
||||
description: t('channel_creation_done'),
|
||||
});
|
||||
setTimeout(() => {
|
||||
history.replace(ROUTES.SETTINGS);
|
||||
}, 2000);
|
||||
history.replace(ROUTES.ALL_CHANNELS);
|
||||
} else {
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
@@ -165,9 +167,7 @@ function CreateAlertChannels({
|
||||
message: 'Success',
|
||||
description: t('channel_creation_done'),
|
||||
});
|
||||
setTimeout(() => {
|
||||
history.replace(ROUTES.SETTINGS);
|
||||
}, 2000);
|
||||
history.replace(ROUTES.ALL_CHANNELS);
|
||||
} else {
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
@@ -222,9 +222,7 @@ function CreateAlertChannels({
|
||||
message: 'Success',
|
||||
description: t('channel_creation_done'),
|
||||
});
|
||||
setTimeout(() => {
|
||||
history.replace(ROUTES.SETTINGS);
|
||||
}, 2000);
|
||||
history.replace(ROUTES.ALL_CHANNELS);
|
||||
} else {
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
@@ -241,26 +239,71 @@ function CreateAlertChannels({
|
||||
setSavingState(false);
|
||||
}, [t, notifications, preparePagerRequest]);
|
||||
|
||||
const prepareMsTeamsRequest = useCallback(
|
||||
() => ({
|
||||
webhook_url: selectedConfig?.webhook_url || '',
|
||||
name: selectedConfig?.name || '',
|
||||
send_resolved: true,
|
||||
text: selectedConfig?.text || '',
|
||||
title: selectedConfig?.title || '',
|
||||
}),
|
||||
[selectedConfig],
|
||||
);
|
||||
|
||||
const onMsTeamsHandler = useCallback(async () => {
|
||||
setSavingState(true);
|
||||
|
||||
try {
|
||||
const response = await createMsTeamsApi(prepareMsTeamsRequest());
|
||||
|
||||
if (response.statusCode === 200) {
|
||||
notifications.success({
|
||||
message: 'Success',
|
||||
description: t('channel_creation_done'),
|
||||
});
|
||||
history.replace(ROUTES.ALL_CHANNELS);
|
||||
} else {
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
description: response.error || t('channel_creation_failed'),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
description: t('channel_creation_failed'),
|
||||
});
|
||||
}
|
||||
setSavingState(false);
|
||||
}, [prepareMsTeamsRequest, t, notifications]);
|
||||
|
||||
const onSaveHandler = useCallback(
|
||||
async (value: ChannelType) => {
|
||||
switch (value) {
|
||||
case SlackType:
|
||||
onSlackHandler();
|
||||
break;
|
||||
case WebhookType:
|
||||
onWebhookHandler();
|
||||
break;
|
||||
case PagerType:
|
||||
onPagerHandler();
|
||||
break;
|
||||
default:
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
description: t('selected_channel_invalid'),
|
||||
});
|
||||
const functionMapper = {
|
||||
[SlackType]: onSlackHandler,
|
||||
[WebhookType]: onWebhookHandler,
|
||||
[PagerType]: onPagerHandler,
|
||||
[MsTeamsType]: onMsTeamsHandler,
|
||||
};
|
||||
const functionToCall = functionMapper[value];
|
||||
|
||||
if (functionToCall) {
|
||||
functionToCall();
|
||||
} else {
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
description: t('selected_channel_invalid'),
|
||||
});
|
||||
}
|
||||
},
|
||||
[onSlackHandler, t, onPagerHandler, onWebhookHandler, notifications],
|
||||
[
|
||||
onSlackHandler,
|
||||
onWebhookHandler,
|
||||
onPagerHandler,
|
||||
onMsTeamsHandler,
|
||||
notifications,
|
||||
t,
|
||||
],
|
||||
);
|
||||
|
||||
const performChannelTest = useCallback(
|
||||
@@ -282,6 +325,10 @@ function CreateAlertChannels({
|
||||
request = preparePagerRequest();
|
||||
if (request) response = await testPagerApi(request);
|
||||
break;
|
||||
case MsTeamsType:
|
||||
request = prepareMsTeamsRequest();
|
||||
response = await testMsTeamsApi(request);
|
||||
break;
|
||||
default:
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
@@ -315,6 +362,7 @@ function CreateAlertChannels({
|
||||
t,
|
||||
preparePagerRequest,
|
||||
prepareSlackRequest,
|
||||
prepareMsTeamsRequest,
|
||||
notifications,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -40,6 +40,7 @@ export const alertDefaults: AlertDef = {
|
||||
},
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
panelType: PANEL_TYPES.TIME_SERIES,
|
||||
unit: undefined,
|
||||
},
|
||||
op: defaultCompareOp,
|
||||
matchType: defaultMatchType,
|
||||
@@ -69,6 +70,7 @@ export const logAlertDefaults: AlertDef = {
|
||||
},
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
panelType: PANEL_TYPES.TIME_SERIES,
|
||||
unit: undefined,
|
||||
},
|
||||
op: defaultCompareOp,
|
||||
matchType: '4',
|
||||
@@ -99,6 +101,7 @@ export const traceAlertDefaults: AlertDef = {
|
||||
},
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
panelType: PANEL_TYPES.TIME_SERIES,
|
||||
unit: undefined,
|
||||
},
|
||||
op: defaultCompareOp,
|
||||
matchType: '4',
|
||||
@@ -129,6 +132,7 @@ export const exceptionAlertDefaults: AlertDef = {
|
||||
},
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
panelType: PANEL_TYPES.TIME_SERIES,
|
||||
unit: undefined,
|
||||
},
|
||||
op: defaultCompareOp,
|
||||
matchType: '4',
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { Form } from 'antd';
|
||||
import editMsTeamsApi from 'api/channels/editMsTeams';
|
||||
import editPagerApi from 'api/channels/editPager';
|
||||
import editSlackApi from 'api/channels/editSlack';
|
||||
import editWebhookApi from 'api/channels/editWebhook';
|
||||
import testMsTeamsApi from 'api/channels/testMsTeams';
|
||||
import testPagerApi from 'api/channels/testPager';
|
||||
import testSlackApi from 'api/channels/testSlack';
|
||||
import testWebhookApi from 'api/channels/testWebhook';
|
||||
import ROUTES from 'constants/routes';
|
||||
import {
|
||||
ChannelType,
|
||||
MsTeamsChannel,
|
||||
MsTeamsType,
|
||||
PagerChannel,
|
||||
PagerType,
|
||||
SlackChannel,
|
||||
@@ -31,7 +35,7 @@ function EditAlertChannels({
|
||||
|
||||
const [formInstance] = Form.useForm();
|
||||
const [selectedConfig, setSelectedConfig] = useState<
|
||||
Partial<SlackChannel & WebhookChannel & PagerChannel>
|
||||
Partial<SlackChannel & WebhookChannel & PagerChannel & MsTeamsChannel>
|
||||
>({
|
||||
...initialValue,
|
||||
});
|
||||
@@ -81,9 +85,7 @@ function EditAlertChannels({
|
||||
description: t('channel_edit_done'),
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
history.replace(ROUTES.SETTINGS);
|
||||
}, 2000);
|
||||
history.replace(ROUTES.ALL_CHANNELS);
|
||||
} else {
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
@@ -136,9 +138,7 @@ function EditAlertChannels({
|
||||
description: t('channel_edit_done'),
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
history.replace(ROUTES.SETTINGS);
|
||||
}, 2000);
|
||||
history.replace(ROUTES.ALL_CHANNELS);
|
||||
} else {
|
||||
showError(response.error || t('channel_edit_failed'));
|
||||
}
|
||||
@@ -183,9 +183,7 @@ function EditAlertChannels({
|
||||
description: t('channel_edit_done'),
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
history.replace(ROUTES.SETTINGS);
|
||||
}, 2000);
|
||||
history.replace(ROUTES.ALL_CHANNELS);
|
||||
} else {
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
@@ -195,6 +193,48 @@ function EditAlertChannels({
|
||||
setSavingState(false);
|
||||
}, [preparePagerRequest, notifications, selectedConfig, t]);
|
||||
|
||||
const prepareMsTeamsRequest = useCallback(
|
||||
() => ({
|
||||
webhook_url: selectedConfig?.webhook_url || '',
|
||||
name: selectedConfig?.name || '',
|
||||
send_resolved: true,
|
||||
text: selectedConfig?.text || '',
|
||||
title: selectedConfig?.title || '',
|
||||
id,
|
||||
}),
|
||||
[id, selectedConfig],
|
||||
);
|
||||
|
||||
const onMsTeamsEditHandler = useCallback(async () => {
|
||||
setSavingState(true);
|
||||
|
||||
if (selectedConfig?.webhook_url === '') {
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
description: t('webhook_url_required'),
|
||||
});
|
||||
setSavingState(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await editMsTeamsApi(prepareMsTeamsRequest());
|
||||
|
||||
if (response.statusCode === 200) {
|
||||
notifications.success({
|
||||
message: 'Success',
|
||||
description: t('channel_edit_done'),
|
||||
});
|
||||
|
||||
history.replace(ROUTES.ALL_CHANNELS);
|
||||
} else {
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
description: response.error || t('channel_edit_failed'),
|
||||
});
|
||||
}
|
||||
setSavingState(false);
|
||||
}, [prepareMsTeamsRequest, t, notifications, selectedConfig]);
|
||||
|
||||
const onSaveHandler = useCallback(
|
||||
(value: ChannelType) => {
|
||||
if (value === SlackType) {
|
||||
@@ -203,9 +243,16 @@ function EditAlertChannels({
|
||||
onWebhookEditHandler();
|
||||
} else if (value === PagerType) {
|
||||
onPagerEditHandler();
|
||||
} else if (value === MsTeamsType) {
|
||||
onMsTeamsEditHandler();
|
||||
}
|
||||
},
|
||||
[onSlackEditHandler, onWebhookEditHandler, onPagerEditHandler],
|
||||
[
|
||||
onSlackEditHandler,
|
||||
onWebhookEditHandler,
|
||||
onPagerEditHandler,
|
||||
onMsTeamsEditHandler,
|
||||
],
|
||||
);
|
||||
|
||||
const performChannelTest = useCallback(
|
||||
@@ -227,6 +274,10 @@ function EditAlertChannels({
|
||||
request = preparePagerRequest();
|
||||
if (request) response = await testPagerApi(request);
|
||||
break;
|
||||
case MsTeamsType:
|
||||
request = prepareMsTeamsRequest();
|
||||
if (request) response = await testMsTeamsApi(request);
|
||||
break;
|
||||
default:
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
@@ -260,6 +311,7 @@ function EditAlertChannels({
|
||||
prepareWebhookRequest,
|
||||
preparePagerRequest,
|
||||
prepareSlackRequest,
|
||||
prepareMsTeamsRequest,
|
||||
notifications,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import { Form, Input } from 'antd';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { MsTeamsChannel } from '../../CreateAlertChannels/config';
|
||||
|
||||
function MsTeams({ setSelectedConfig }: MsTeamsProps): JSX.Element {
|
||||
const { t } = useTranslation('channels');
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item name="webhook_url" label={t('field_webhook_url')}>
|
||||
<Input
|
||||
onChange={(event): void => {
|
||||
setSelectedConfig((value) => ({
|
||||
...value,
|
||||
webhook_url: event.target.value,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="title" label={t('field_slack_title')}>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
// value={`[{{ .Status | toUpper }}{{ if eq .Status \"firing\" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}\n{{- if gt (len .CommonLabels) (len .GroupLabels) -}}\n{{\" \"}}(\n{{- with .CommonLabels.Remove .GroupLabels.Names }}\n {{- range $index, $label := .SortedPairs -}}\n {{ if $index }}, {{ end }}\n {{- $label.Name }}=\"{{ $label.Value -}}\"\n {{- end }}\n{{- end -}}\n)\n{{- end }}`}
|
||||
onChange={(event): void =>
|
||||
setSelectedConfig((value) => ({
|
||||
...value,
|
||||
title: event.target.value,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="text" label={t('field_slack_description')}>
|
||||
<Input.TextArea
|
||||
onChange={(event): void =>
|
||||
setSelectedConfig((value) => ({
|
||||
...value,
|
||||
text: event.target.value,
|
||||
}))
|
||||
}
|
||||
placeholder={t('placeholder_slack_description')}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
interface MsTeamsProps {
|
||||
setSelectedConfig: React.Dispatch<
|
||||
React.SetStateAction<Partial<MsTeamsChannel>>
|
||||
>;
|
||||
}
|
||||
|
||||
export default MsTeams;
|
||||
@@ -1,8 +1,11 @@
|
||||
import { Form, FormInstance, Input, Select, Typography } from 'antd';
|
||||
import { Store } from 'antd/lib/form/interface';
|
||||
import UpgradePrompt from 'components/Upgrade/UpgradePrompt';
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import ROUTES from 'constants/routes';
|
||||
import {
|
||||
ChannelType,
|
||||
MsTeamsType,
|
||||
PagerChannel,
|
||||
PagerType,
|
||||
SlackChannel,
|
||||
@@ -10,18 +13,18 @@ import {
|
||||
WebhookChannel,
|
||||
WebhookType,
|
||||
} from 'container/CreateAlertChannels/config';
|
||||
import useFeatureFlags from 'hooks/useFeatureFlag';
|
||||
import { isFeatureKeys } from 'hooks/useFeatureFlag/utils';
|
||||
import history from 'lib/history';
|
||||
import { Dispatch, ReactElement, SetStateAction } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import MsTeamsSettings from './Settings/MsTeams';
|
||||
import PagerSettings from './Settings/Pager';
|
||||
import SlackSettings from './Settings/Slack';
|
||||
import WebhookSettings from './Settings/Webhook';
|
||||
import { Button } from './styles';
|
||||
|
||||
const { Option } = Select;
|
||||
const { Title } = Typography;
|
||||
|
||||
function FormAlertChannels({
|
||||
formInstance,
|
||||
type,
|
||||
@@ -36,8 +39,27 @@ function FormAlertChannels({
|
||||
editing = false,
|
||||
}: FormAlertChannelsProps): JSX.Element {
|
||||
const { t } = useTranslation('channels');
|
||||
const isUserOnEEPlan = useFeatureFlags(FeatureKeys.ENTERPRISE_PLAN);
|
||||
|
||||
const feature = `ALERT_CHANNEL_${type.toUpperCase()}`;
|
||||
|
||||
const hasFeature = useFeatureFlags(
|
||||
isFeatureKeys(feature) ? feature : FeatureKeys.ALERT_CHANNEL_SLACK,
|
||||
);
|
||||
|
||||
const isOssFeature = useFeatureFlags(FeatureKeys.OSS);
|
||||
|
||||
const renderSettings = (): ReactElement | null => {
|
||||
if (
|
||||
// for ee plan
|
||||
!isOssFeature?.active &&
|
||||
(!hasFeature || !hasFeature.active) &&
|
||||
type === 'msteams'
|
||||
) {
|
||||
// channel type is not available for users plan
|
||||
return <UpgradePrompt />;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SlackType:
|
||||
return <SlackSettings setSelectedConfig={setSelectedConfig} />;
|
||||
@@ -45,14 +67,16 @@ function FormAlertChannels({
|
||||
return <WebhookSettings setSelectedConfig={setSelectedConfig} />;
|
||||
case PagerType:
|
||||
return <PagerSettings setSelectedConfig={setSelectedConfig} />;
|
||||
|
||||
case MsTeamsType:
|
||||
return <MsTeamsSettings setSelectedConfig={setSelectedConfig} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title level={3}>{title}</Title>
|
||||
<Typography.Title level={3}>{title}</Typography.Title>
|
||||
|
||||
<Form initialValues={initialValue} layout="vertical" form={formInstance}>
|
||||
<Form.Item label={t('field_channel_name')} labelAlign="left" name="name">
|
||||
@@ -69,15 +93,22 @@ function FormAlertChannels({
|
||||
|
||||
<Form.Item label={t('field_channel_type')} labelAlign="left" name="type">
|
||||
<Select disabled={editing} onChange={onTypeChangeHandler} value={type}>
|
||||
<Option value="slack" key="slack">
|
||||
<Select.Option value="slack" key="slack">
|
||||
Slack
|
||||
</Option>
|
||||
<Option value="webhook" key="webhook">
|
||||
</Select.Option>
|
||||
<Select.Option value="webhook" key="webhook">
|
||||
Webhook
|
||||
</Option>
|
||||
<Option value="pagerduty" key="pagerduty">
|
||||
</Select.Option>
|
||||
<Select.Option value="pagerduty" key="pagerduty">
|
||||
Pagerduty
|
||||
</Option>
|
||||
</Select.Option>
|
||||
{!isOssFeature?.active && (
|
||||
<Select.Option value="msteams" key="msteams">
|
||||
<div>
|
||||
Microsoft Teams {!isUserOnEEPlan && '(Supported in Paid Plans Only)'}{' '}
|
||||
</div>
|
||||
</Select.Option>
|
||||
)}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
@@ -85,7 +116,7 @@ function FormAlertChannels({
|
||||
|
||||
<Form.Item>
|
||||
<Button
|
||||
disabled={savingState}
|
||||
disabled={savingState || !hasFeature}
|
||||
loading={savingState}
|
||||
type="primary"
|
||||
onClick={(): void => onSaveHandler(type)}
|
||||
@@ -93,7 +124,7 @@ function FormAlertChannels({
|
||||
{t('button_save_channel')}
|
||||
</Button>
|
||||
<Button
|
||||
disabled={testingState}
|
||||
disabled={testingState || !hasFeature}
|
||||
loading={testingState}
|
||||
onClick={(): void => onTestHandler(type)}
|
||||
>
|
||||
|
||||
98
frontend/src/container/FormAlertRules/ChartPreview/config.ts
Normal file
98
frontend/src/container/FormAlertRules/ChartPreview/config.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import {
|
||||
DataFormats,
|
||||
DataRateFormats,
|
||||
MiscellaneousFormats,
|
||||
ThroughputFormats,
|
||||
TimeFormats,
|
||||
} from 'container/NewWidget/RightContainer/types';
|
||||
|
||||
export const dataFormatConfig: Record<DataFormats, number> = {
|
||||
[DataFormats.BytesIEC]: 1,
|
||||
[DataFormats.BytesSI]: 1,
|
||||
[DataFormats.BitsIEC]: 1 / 8,
|
||||
[DataFormats.BitsSI]: 1 / 8,
|
||||
[DataFormats.KibiBytes]: 1024,
|
||||
[DataFormats.KiloBytes]: 1000,
|
||||
[DataFormats.MebiBytes]: 1024 ** 2,
|
||||
[DataFormats.MegaBytes]: 1000 ** 2,
|
||||
[DataFormats.GibiBytes]: 1024 ** 3,
|
||||
[DataFormats.GigaBytes]: 1000 ** 3,
|
||||
[DataFormats.TebiBytes]: 1024 ** 4,
|
||||
[DataFormats.TeraBytes]: 1000 ** 4,
|
||||
[DataFormats.PebiBytes]: 1024 ** 5,
|
||||
[DataFormats.PetaBytes]: 1000 ** 5,
|
||||
};
|
||||
|
||||
export const throughputConfig: Record<ThroughputFormats, number> = {
|
||||
[ThroughputFormats.CountsPerSec]: 1,
|
||||
[ThroughputFormats.OpsPerSec]: 1,
|
||||
[ThroughputFormats.RequestsPerSec]: 1,
|
||||
[ThroughputFormats.ReadsPerSec]: 1,
|
||||
[ThroughputFormats.WritesPerSec]: 1,
|
||||
[ThroughputFormats.IOOpsPerSec]: 1,
|
||||
[ThroughputFormats.CountsPerMin]: 1 / 60,
|
||||
[ThroughputFormats.OpsPerMin]: 1 / 60,
|
||||
[ThroughputFormats.ReadsPerMin]: 1 / 60,
|
||||
[ThroughputFormats.WritesPerMin]: 1 / 60,
|
||||
};
|
||||
|
||||
export const timeUnitsConfig: Record<TimeFormats, number> = {
|
||||
[TimeFormats.Hertz]: 1,
|
||||
[TimeFormats.Nanoseconds]: 1e-9,
|
||||
[TimeFormats.Microseconds]: 1e-6,
|
||||
[TimeFormats.Milliseconds]: 1e-3,
|
||||
[TimeFormats.Seconds]: 1,
|
||||
[TimeFormats.Minutes]: 60,
|
||||
[TimeFormats.Hours]: 3600,
|
||||
[TimeFormats.Days]: 86400,
|
||||
[TimeFormats.DurationMs]: 1e-3,
|
||||
[TimeFormats.DurationS]: 1,
|
||||
[TimeFormats.DurationHms]: 3600,
|
||||
[TimeFormats.DurationDhms]: 86400,
|
||||
[TimeFormats.Timeticks]: 1e-3,
|
||||
[TimeFormats.ClockMs]: 1e-3,
|
||||
[TimeFormats.ClockS]: 1,
|
||||
};
|
||||
|
||||
export const dataRateUnitsConfig: Record<DataRateFormats, number> = {
|
||||
[DataRateFormats.PacketsPerSec]: 1,
|
||||
[DataRateFormats.BytesPerSecIEC]: dataFormatConfig[DataFormats.BytesIEC],
|
||||
[DataRateFormats.BytesPerSecSI]: dataFormatConfig[DataFormats.BytesSI],
|
||||
[DataRateFormats.BitsPerSecIEC]: dataFormatConfig[DataFormats.BitsIEC],
|
||||
[DataRateFormats.BitsPerSecSI]: dataFormatConfig[DataFormats.BitsSI],
|
||||
[DataRateFormats.KibiBytesPerSec]: dataFormatConfig[DataFormats.KibiBytes],
|
||||
[DataRateFormats.KibiBitsPerSec]: dataFormatConfig[DataFormats.KibiBytes] * 8,
|
||||
[DataRateFormats.KiloBytesPerSec]: dataFormatConfig[DataFormats.KiloBytes],
|
||||
[DataRateFormats.KiloBitsPerSec]: dataFormatConfig[DataFormats.KiloBytes] * 8,
|
||||
[DataRateFormats.MebiBytesPerSec]: dataFormatConfig[DataFormats.MebiBytes],
|
||||
[DataRateFormats.MebiBitsPerSec]: dataFormatConfig[DataFormats.MebiBytes] * 8,
|
||||
[DataRateFormats.MegaBytesPerSec]: dataFormatConfig[DataFormats.MegaBytes],
|
||||
[DataRateFormats.MegaBitsPerSec]: dataFormatConfig[DataFormats.MegaBytes] * 8,
|
||||
[DataRateFormats.GibiBytesPerSec]: dataFormatConfig[DataFormats.GibiBytes],
|
||||
[DataRateFormats.GibiBitsPerSec]: dataFormatConfig[DataFormats.GibiBytes] * 8,
|
||||
[DataRateFormats.GigaBytesPerSec]: dataFormatConfig[DataFormats.GigaBytes],
|
||||
[DataRateFormats.GigaBitsPerSec]: dataFormatConfig[DataFormats.GigaBytes] * 8,
|
||||
[DataRateFormats.TebiBytesPerSec]: dataFormatConfig[DataFormats.TebiBytes],
|
||||
[DataRateFormats.TebiBitsPerSec]: dataFormatConfig[DataFormats.TebiBytes] * 8,
|
||||
[DataRateFormats.TeraBytesPerSec]: dataFormatConfig[DataFormats.TeraBytes],
|
||||
[DataRateFormats.TeraBitsPerSec]: dataFormatConfig[DataFormats.TeraBytes] * 8,
|
||||
[DataRateFormats.PebiBytesPerSec]: dataFormatConfig[DataFormats.PebiBytes],
|
||||
[DataRateFormats.PebiBitsPerSec]: dataFormatConfig[DataFormats.PebiBytes] * 8,
|
||||
[DataRateFormats.PetaBytesPerSec]: dataFormatConfig[DataFormats.PetaBytes],
|
||||
[DataRateFormats.PetaBitsPerSec]: dataFormatConfig[DataFormats.PetaBytes] * 8,
|
||||
};
|
||||
|
||||
export const miscUnitsConfig: Record<MiscellaneousFormats, number> = {
|
||||
[MiscellaneousFormats.None]: 1,
|
||||
[MiscellaneousFormats.String]: 1,
|
||||
[MiscellaneousFormats.Short]: 1,
|
||||
[MiscellaneousFormats.Percent]: 0.01,
|
||||
[MiscellaneousFormats.PercentUnit]: 1,
|
||||
[MiscellaneousFormats.Humidity]: 1,
|
||||
[MiscellaneousFormats.Decibel]: 1,
|
||||
[MiscellaneousFormats.Hexadecimal0x]: 1,
|
||||
[MiscellaneousFormats.Hexadecimal]: 1,
|
||||
[MiscellaneousFormats.ScientificNotation]: 1,
|
||||
[MiscellaneousFormats.LocaleFormat]: 1,
|
||||
[MiscellaneousFormats.Pixels]: 1,
|
||||
};
|
||||
@@ -9,10 +9,12 @@ import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||
import getChartData from 'lib/getChartData';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AlertDef } from 'types/api/alerts/def';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
|
||||
import { ChartContainer, FailedMessageContainer } from './styles';
|
||||
import { covertIntoDataFormats } from './utils';
|
||||
|
||||
export interface ChartPreviewProps {
|
||||
name: string;
|
||||
@@ -21,7 +23,7 @@ export interface ChartPreviewProps {
|
||||
selectedTime?: timePreferenceType;
|
||||
selectedInterval?: Time;
|
||||
headline?: JSX.Element;
|
||||
threshold?: number | undefined;
|
||||
alertDef?: AlertDef;
|
||||
userQueryKey?: string;
|
||||
}
|
||||
|
||||
@@ -32,18 +34,28 @@ function ChartPreview({
|
||||
selectedTime = 'GLOBAL_TIME',
|
||||
selectedInterval = '5min',
|
||||
headline,
|
||||
threshold,
|
||||
userQueryKey,
|
||||
alertDef,
|
||||
}: ChartPreviewProps): JSX.Element | null {
|
||||
const { t } = useTranslation('alerts');
|
||||
const threshold = alertDef?.condition.target || 0;
|
||||
|
||||
const thresholdValue = covertIntoDataFormats({
|
||||
value: threshold,
|
||||
sourceUnit: alertDef?.condition.targetUnit,
|
||||
targetUnit: query?.unit,
|
||||
});
|
||||
|
||||
const staticLine: StaticLineProps | undefined =
|
||||
threshold !== undefined
|
||||
? {
|
||||
yMin: threshold,
|
||||
yMax: threshold,
|
||||
yMin: thresholdValue,
|
||||
yMax: thresholdValue,
|
||||
borderColor: '#f14',
|
||||
borderWidth: 1,
|
||||
lineText: `${t('preview_chart_threshold_label')} (y=${threshold})`,
|
||||
lineText: `${t('preview_chart_threshold_label')} (y=${thresholdValue} ${
|
||||
query?.unit || ''
|
||||
})`,
|
||||
textColor: '#f14',
|
||||
}
|
||||
: undefined;
|
||||
@@ -121,6 +133,7 @@ function ChartPreview({
|
||||
staticLine={staticLine}
|
||||
panelData={queryResponse.data?.payload.data.newResult.data.result || []}
|
||||
query={query || initialQueriesMap.metrics}
|
||||
yAxisUnit={query?.unit}
|
||||
/>
|
||||
)}
|
||||
</ChartContainer>
|
||||
@@ -132,8 +145,8 @@ ChartPreview.defaultProps = {
|
||||
selectedTime: 'GLOBAL_TIME',
|
||||
selectedInterval: '5min',
|
||||
headline: undefined,
|
||||
threshold: undefined,
|
||||
userQueryKey: '',
|
||||
alertDef: undefined,
|
||||
};
|
||||
|
||||
export default ChartPreview;
|
||||
|
||||
105
frontend/src/container/FormAlertRules/ChartPreview/utils.test.ts
Normal file
105
frontend/src/container/FormAlertRules/ChartPreview/utils.test.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { DataFormats } from 'container/NewWidget/RightContainer/types';
|
||||
|
||||
import { covertIntoDataFormats } from './utils';
|
||||
|
||||
describe('Convert One Unit to another unit', () => {
|
||||
it('should convert from BitsIEC to BytesIEC', () => {
|
||||
const result = covertIntoDataFormats({
|
||||
value: 8,
|
||||
sourceUnit: DataFormats.BitsIEC,
|
||||
targetUnit: DataFormats.BytesIEC,
|
||||
});
|
||||
expect(result).toBe(1);
|
||||
});
|
||||
|
||||
// for KibiBytes to MebiBytes conversion
|
||||
it('should convert from KibiBytes to MebiBytes', () => {
|
||||
const result = covertIntoDataFormats({
|
||||
value: 1024,
|
||||
sourceUnit: DataFormats.KibiBytes,
|
||||
targetUnit: DataFormats.MebiBytes,
|
||||
});
|
||||
expect(result).toBe(1);
|
||||
});
|
||||
|
||||
// for MegaBytes to GigaBytes conversion (SI units)
|
||||
it('should convert from MegaBytes to GigaBytes (SI)', () => {
|
||||
const result = covertIntoDataFormats({
|
||||
value: 1000,
|
||||
sourceUnit: DataFormats.MegaBytes,
|
||||
targetUnit: DataFormats.GigaBytes,
|
||||
});
|
||||
expect(result).toBe(1);
|
||||
});
|
||||
|
||||
// for identity conversion
|
||||
it('should handle identity conversion', () => {
|
||||
const result = covertIntoDataFormats({
|
||||
value: 100,
|
||||
sourceUnit: DataFormats.KibiBytes,
|
||||
targetUnit: DataFormats.KibiBytes,
|
||||
});
|
||||
expect(result).toBe(100);
|
||||
});
|
||||
|
||||
// BytesIEC to BitsIEC conversion
|
||||
it('should convert from BytesIEC to BitsIEC', () => {
|
||||
const result = covertIntoDataFormats({
|
||||
value: 1,
|
||||
sourceUnit: DataFormats.BytesIEC,
|
||||
targetUnit: DataFormats.BitsIEC,
|
||||
});
|
||||
expect(result).toBe(8);
|
||||
});
|
||||
|
||||
// for GibiBytes to TebiBytes conversion
|
||||
it('should convert from GibiBytes to TebiBytes', () => {
|
||||
const result = covertIntoDataFormats({
|
||||
value: 1024,
|
||||
sourceUnit: DataFormats.GibiBytes,
|
||||
targetUnit: DataFormats.TebiBytes,
|
||||
});
|
||||
expect(result).toBe(1);
|
||||
});
|
||||
|
||||
// for GigaBytes to TeraBytes conversion (SI units)
|
||||
it('should convert from GigaBytes to TeraBytes (SI)', () => {
|
||||
const result = covertIntoDataFormats({
|
||||
value: 1000,
|
||||
sourceUnit: DataFormats.GigaBytes,
|
||||
targetUnit: DataFormats.TeraBytes,
|
||||
});
|
||||
expect(result).toBe(1);
|
||||
});
|
||||
|
||||
// for GigaBytes to GibiBytes conversion (cross conversion)
|
||||
it('should convert from GigaBytes to GibiBytes', () => {
|
||||
const result = covertIntoDataFormats({
|
||||
value: 1,
|
||||
sourceUnit: DataFormats.GigaBytes,
|
||||
targetUnit: DataFormats.GibiBytes,
|
||||
});
|
||||
// 1 GB = 0.93132257461548 GiB approximately
|
||||
expect(result).toBeCloseTo(0.93132257461548);
|
||||
});
|
||||
|
||||
// for a large number conversion
|
||||
it('should handle large number conversion from PebiBytes to BitsIEC', () => {
|
||||
const result = covertIntoDataFormats({
|
||||
value: 1,
|
||||
sourceUnit: DataFormats.PebiBytes,
|
||||
targetUnit: DataFormats.BitsIEC,
|
||||
});
|
||||
expect(result).toBe(1 * 1024 ** 5 * 8); // 1 PebiByte = 2^50 Bytes = 2^53 Bits
|
||||
});
|
||||
|
||||
// Negative value conversion
|
||||
it('should handle negative values', () => {
|
||||
const result = covertIntoDataFormats({
|
||||
value: -1,
|
||||
sourceUnit: DataFormats.KibiBytes,
|
||||
targetUnit: DataFormats.BytesIEC,
|
||||
});
|
||||
expect(result).toBe(-1024);
|
||||
});
|
||||
});
|
||||
54
frontend/src/container/FormAlertRules/ChartPreview/utils.ts
Normal file
54
frontend/src/container/FormAlertRules/ChartPreview/utils.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
BooleanFormats,
|
||||
DataFormats,
|
||||
DataRateFormats,
|
||||
MiscellaneousFormats,
|
||||
ThroughputFormats,
|
||||
TimeFormats,
|
||||
} from 'container/NewWidget/RightContainer/types';
|
||||
|
||||
import {
|
||||
dataFormatConfig,
|
||||
dataRateUnitsConfig,
|
||||
miscUnitsConfig,
|
||||
throughputConfig,
|
||||
timeUnitsConfig,
|
||||
} from './config';
|
||||
|
||||
export function covertIntoDataFormats({
|
||||
value,
|
||||
sourceUnit,
|
||||
targetUnit,
|
||||
}: IUnit): number {
|
||||
if (Object.values(BooleanFormats).includes(sourceUnit as BooleanFormats)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const sourceMultiplier =
|
||||
dataFormatConfig[sourceUnit as DataFormats] ||
|
||||
timeUnitsConfig[sourceUnit as TimeFormats] ||
|
||||
dataRateUnitsConfig[sourceUnit as DataRateFormats] ||
|
||||
miscUnitsConfig[sourceUnit as MiscellaneousFormats] ||
|
||||
throughputConfig[sourceUnit as ThroughputFormats];
|
||||
|
||||
const targetDivider =
|
||||
dataFormatConfig[targetUnit as DataFormats] ||
|
||||
timeUnitsConfig[targetUnit as TimeFormats] ||
|
||||
dataRateUnitsConfig[targetUnit as DataRateFormats] ||
|
||||
miscUnitsConfig[targetUnit as MiscellaneousFormats] ||
|
||||
throughputConfig[sourceUnit as ThroughputFormats];
|
||||
|
||||
const intermediateValue = value * sourceMultiplier;
|
||||
|
||||
const roundedValue = Math.round(intermediateValue * 1000000) / 1000000;
|
||||
|
||||
const result = roundedValue / targetDivider;
|
||||
|
||||
return Number.isNaN(result) ? 0 : result;
|
||||
}
|
||||
|
||||
interface IUnit {
|
||||
value: number;
|
||||
sourceUnit?: string;
|
||||
targetUnit?: string;
|
||||
}
|
||||
@@ -1,4 +1,17 @@
|
||||
import { Form, InputNumber, InputNumberProps, Select, Typography } from 'antd';
|
||||
import {
|
||||
Form,
|
||||
InputNumber,
|
||||
InputNumberProps,
|
||||
Select,
|
||||
SelectProps,
|
||||
Space,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import {
|
||||
getCategoryByOptionId,
|
||||
getCategorySelectOptionByName,
|
||||
} from 'container/NewWidget/RightContainer/alertFomatCategories';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
AlertDef,
|
||||
@@ -10,9 +23,6 @@ import { EQueryType } from 'types/common/dashboard';
|
||||
|
||||
import { FormContainer, InlineSelect, StepHeading } from './styles';
|
||||
|
||||
const { Option } = Select;
|
||||
const FormItem = Form.Item;
|
||||
|
||||
function RuleOptions({
|
||||
alertDef,
|
||||
setAlertDef,
|
||||
@@ -20,6 +30,7 @@ function RuleOptions({
|
||||
}: RuleOptionsProps): JSX.Element {
|
||||
// init namespace for translations
|
||||
const { t } = useTranslation('alerts');
|
||||
const { currentQuery } = useQueryBuilder();
|
||||
|
||||
const handleMatchOptChange = (value: string | unknown): void => {
|
||||
const m = (value as string) || alertDef.condition?.matchType;
|
||||
@@ -49,10 +60,10 @@ function RuleOptions({
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Option value="1">{t('option_above')}</Option>
|
||||
<Option value="2">{t('option_below')}</Option>
|
||||
<Option value="3">{t('option_equal')}</Option>
|
||||
<Option value="4">{t('option_notequal')}</Option>
|
||||
<Select.Option value="1">{t('option_above')}</Select.Option>
|
||||
<Select.Option value="2">{t('option_below')}</Select.Option>
|
||||
<Select.Option value="3">{t('option_equal')}</Select.Option>
|
||||
<Select.Option value="4">{t('option_notequal')}</Select.Option>
|
||||
</InlineSelect>
|
||||
);
|
||||
|
||||
@@ -63,10 +74,10 @@ function RuleOptions({
|
||||
value={alertDef.condition?.matchType}
|
||||
onChange={(value: string | unknown): void => handleMatchOptChange(value)}
|
||||
>
|
||||
<Option value="1">{t('option_atleastonce')}</Option>
|
||||
<Option value="2">{t('option_allthetimes')}</Option>
|
||||
<Option value="3">{t('option_onaverage')}</Option>
|
||||
<Option value="4">{t('option_intotal')}</Option>
|
||||
<Select.Option value="1">{t('option_atleastonce')}</Select.Option>
|
||||
<Select.Option value="2">{t('option_allthetimes')}</Select.Option>
|
||||
<Select.Option value="3">{t('option_onaverage')}</Select.Option>
|
||||
<Select.Option value="4">{t('option_intotal')}</Select.Option>
|
||||
</InlineSelect>
|
||||
);
|
||||
|
||||
@@ -77,7 +88,7 @@ function RuleOptions({
|
||||
value={alertDef.condition?.matchType}
|
||||
onChange={(value: string | unknown): void => handleMatchOptChange(value)}
|
||||
>
|
||||
<Option value="1">{t('option_atleastonce')}</Option>
|
||||
<Select.Option value="1">{t('option_atleastonce')}</Select.Option>
|
||||
</InlineSelect>
|
||||
);
|
||||
|
||||
@@ -94,31 +105,30 @@ function RuleOptions({
|
||||
});
|
||||
}}
|
||||
>
|
||||
{' '}
|
||||
<Option value="5m0s">{t('option_5min')}</Option>
|
||||
<Option value="10m0s">{t('option_10min')}</Option>
|
||||
<Option value="15m0s">{t('option_15min')}</Option>
|
||||
<Option value="1h0m0s">{t('option_60min')}</Option>
|
||||
<Option value="4h0m0s">{t('option_4hours')}</Option>
|
||||
<Option value="24h0m0s">{t('option_24hours')}</Option>
|
||||
<Select.Option value="5m0s">{t('option_5min')}</Select.Option>
|
||||
<Select.Option value="10m0s">{t('option_10min')}</Select.Option>
|
||||
<Select.Option value="15m0s">{t('option_15min')}</Select.Option>
|
||||
<Select.Option value="1h0m0s">{t('option_60min')}</Select.Option>
|
||||
<Select.Option value="4h0m0s">{t('option_4hours')}</Select.Option>
|
||||
<Select.Option value="24h0m0s">{t('option_24hours')}</Select.Option>
|
||||
</InlineSelect>
|
||||
);
|
||||
|
||||
const renderThresholdRuleOpts = (): JSX.Element => (
|
||||
<FormItem>
|
||||
<Form.Item>
|
||||
<Typography.Text>
|
||||
{t('text_condition1')} {renderCompareOps()} {t('text_condition2')}{' '}
|
||||
{renderThresholdMatchOpts()} {t('text_condition3')} {renderEvalWindows()}
|
||||
</Typography.Text>
|
||||
</FormItem>
|
||||
</Form.Item>
|
||||
);
|
||||
const renderPromRuleOptions = (): JSX.Element => (
|
||||
<FormItem>
|
||||
<Form.Item>
|
||||
<Typography.Text>
|
||||
{t('text_condition1')} {renderCompareOps()} {t('text_condition2')}{' '}
|
||||
{renderPromMatchOpts()}
|
||||
</Typography.Text>
|
||||
</FormItem>
|
||||
</Form.Item>
|
||||
);
|
||||
|
||||
const onChange: InputNumberProps['onChange'] = (value): void => {
|
||||
@@ -133,6 +143,22 @@ function RuleOptions({
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeAlertUnit: SelectProps['onChange'] = (value) => {
|
||||
setAlertDef({
|
||||
...alertDef,
|
||||
condition: {
|
||||
...alertDef.condition,
|
||||
targetUnit: value as string,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const selectedCategory = getCategoryByOptionId(currentQuery?.unit || '');
|
||||
|
||||
const categorySelectOptions = getCategorySelectOptionByName(
|
||||
selectedCategory?.name,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<StepHeading>{t('alert_form_step2')}</StepHeading>
|
||||
@@ -140,14 +166,29 @@ function RuleOptions({
|
||||
{queryCategory === EQueryType.PROM
|
||||
? renderPromRuleOptions()
|
||||
: renderThresholdRuleOpts()}
|
||||
<Form.Item name={['condition', 'target']}>
|
||||
<InputNumber
|
||||
addonBefore={t('field_threshold')}
|
||||
value={alertDef?.condition?.target}
|
||||
onChange={onChange}
|
||||
type="number"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Space align="start">
|
||||
<Form.Item noStyle name={['condition', 'target']}>
|
||||
<InputNumber
|
||||
addonBefore={t('field_threshold')}
|
||||
value={alertDef?.condition?.target}
|
||||
onChange={onChange}
|
||||
type="number"
|
||||
onWheel={(e): void => e.currentTarget.blur()}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Select
|
||||
allowClear
|
||||
showSearch
|
||||
options={categorySelectOptions}
|
||||
placeholder={t('field_unit')}
|
||||
value={alertDef.condition.targetUnit}
|
||||
onChange={onChangeAlertUnit}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Space>
|
||||
</FormContainer>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -7,6 +7,7 @@ import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import ROUTES from 'constants/routes';
|
||||
import QueryTypeTag from 'container/NewWidget/LeftContainer/QueryTypeTag';
|
||||
import PlotTag from 'container/NewWidget/LeftContainer/WidgetGraph/PlotTag';
|
||||
import { BuilderUnitsFilter } from 'container/QueryBuilder/filters';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
||||
import { updateStepInterval } from 'hooks/queryBuilder/useStepInterval';
|
||||
@@ -39,6 +40,7 @@ import {
|
||||
ButtonContainer,
|
||||
MainFormContainer,
|
||||
PanelContainer,
|
||||
StepContainer,
|
||||
StyledLeftContainer,
|
||||
} from './styles';
|
||||
import UserGuide from './UserGuide';
|
||||
@@ -224,6 +226,7 @@ function FormAlertRules({
|
||||
chQueries: mapQueryDataToApi(currentQuery.clickhouse_sql, 'name').data,
|
||||
queryType: currentQuery.queryType,
|
||||
panelType: initQuery.panelType,
|
||||
unit: currentQuery.unit,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -360,9 +363,9 @@ function FormAlertRules({
|
||||
/>
|
||||
}
|
||||
name=""
|
||||
threshold={alertDef.condition?.target}
|
||||
query={stagedQuery}
|
||||
selectedInterval={toChartInterval(alertDef.evalWindow)}
|
||||
alertDef={alertDef}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -375,8 +378,8 @@ function FormAlertRules({
|
||||
/>
|
||||
}
|
||||
name="Chart Preview"
|
||||
threshold={alertDef.condition?.target}
|
||||
query={stagedQuery}
|
||||
alertDef={alertDef}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -389,8 +392,8 @@ function FormAlertRules({
|
||||
/>
|
||||
}
|
||||
name="Chart Preview"
|
||||
threshold={alertDef.condition?.target}
|
||||
query={stagedQuery}
|
||||
alertDef={alertDef}
|
||||
selectedInterval={toChartInterval(alertDef.evalWindow)}
|
||||
/>
|
||||
);
|
||||
@@ -404,9 +407,21 @@ function FormAlertRules({
|
||||
currentQuery.queryType === EQueryType.QUERY_BUILDER &&
|
||||
alertType !== AlertTypes.METRICS_BASED_ALERT;
|
||||
|
||||
const onUnitChangeHandler = (): void => {
|
||||
// reset target unit
|
||||
setAlertDef((def) => ({
|
||||
...def,
|
||||
condition: {
|
||||
...def.condition,
|
||||
targetUnit: undefined,
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{Element}
|
||||
|
||||
<PanelContainer>
|
||||
<StyledLeftContainer flex="5 1 600px" md={18}>
|
||||
<MainFormContainer
|
||||
@@ -419,6 +434,11 @@ function FormAlertRules({
|
||||
{currentQuery.queryType === EQueryType.PROM && renderPromChartPreview()}
|
||||
{currentQuery.queryType === EQueryType.CLICKHOUSE &&
|
||||
renderChQueryChartPreview()}
|
||||
|
||||
<StepContainer>
|
||||
<BuilderUnitsFilter onChange={onUnitChangeHandler} />
|
||||
</StepContainer>
|
||||
|
||||
<QuerySection
|
||||
queryCategory={currentQuery.queryType}
|
||||
setQueryCategory={onQueryCategoryChange}
|
||||
|
||||
@@ -89,3 +89,7 @@ export const FormItemMedium = styled(Item)`
|
||||
export const ChannelSelectTip = styled(Typography.Text)`
|
||||
color: hsla(0, 0%, 100%, 0.3);
|
||||
`;
|
||||
|
||||
export const StepContainer = styled.div`
|
||||
margin-top: 2rem;
|
||||
`;
|
||||
|
||||
@@ -53,9 +53,8 @@ function WidgetGraphComponent({
|
||||
setLayout,
|
||||
onDragSelect,
|
||||
onClickHandler,
|
||||
allowClone = true,
|
||||
allowDelete = true,
|
||||
allowEdit = true,
|
||||
threshold,
|
||||
headerMenuList,
|
||||
}: WidgetGraphComponentProps): JSX.Element {
|
||||
const [deleteModal, setDeleteModal] = useState(false);
|
||||
const [modal, setModal] = useState<boolean>(false);
|
||||
@@ -281,9 +280,8 @@ function WidgetGraphComponent({
|
||||
onClone={onCloneHandler}
|
||||
queryResponse={queryResponse}
|
||||
errorMessage={errorMessage}
|
||||
allowClone={allowClone}
|
||||
allowDelete={allowDelete}
|
||||
allowEdit={allowEdit}
|
||||
threshold={threshold}
|
||||
headerMenuList={headerMenuList}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -297,7 +295,7 @@ function WidgetGraphComponent({
|
||||
yAxisUnit={yAxisUnit}
|
||||
onClickHandler={onClickHandler}
|
||||
onDragSelect={onDragSelect}
|
||||
panelData={[]}
|
||||
panelData={queryResponse.data?.payload?.data.newResult.data.result || []}
|
||||
query={widget.query}
|
||||
ref={lineChartRef}
|
||||
/>
|
||||
@@ -313,9 +311,6 @@ WidgetGraphComponent.defaultProps = {
|
||||
setLayout: undefined,
|
||||
onDragSelect: undefined,
|
||||
onClickHandler: undefined,
|
||||
allowDelete: true,
|
||||
allowClone: true,
|
||||
allowEdit: true,
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (
|
||||
|
||||
@@ -15,6 +15,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { getSelectedDashboardVariable } from 'utils/dashboard/selectedDashboard';
|
||||
|
||||
import EmptyWidget from '../EmptyWidget';
|
||||
import { MenuItemKeys } from '../WidgetHeader/contants';
|
||||
import { GridCardGraphProps } from './types';
|
||||
import WidgetGraphComponent from './WidgetGraphComponent';
|
||||
|
||||
@@ -26,10 +27,9 @@ function GridCardGraph({
|
||||
setLayout,
|
||||
onDragSelect,
|
||||
onClickHandler,
|
||||
allowDelete,
|
||||
allowClone,
|
||||
allowEdit,
|
||||
headerMenuList = [MenuItemKeys.View],
|
||||
isQueryEnabled,
|
||||
threshold,
|
||||
}: GridCardGraphProps): JSX.Element {
|
||||
const { isAddWidget } = useSelector<AppState, DashboardReducer>(
|
||||
(state) => state.dashboards,
|
||||
@@ -71,11 +71,12 @@ function GridCardGraph({
|
||||
{
|
||||
queryKey: [
|
||||
`GetMetricsQueryRange-${widget?.timePreferance}-${globalSelectedInterval}-${widget?.id}`,
|
||||
widget,
|
||||
maxTime,
|
||||
minTime,
|
||||
globalSelectedInterval,
|
||||
variables,
|
||||
widget?.query,
|
||||
widget?.panelTypes,
|
||||
],
|
||||
keepPreviousData: true,
|
||||
enabled: isGraphVisible && !isEmptyWidget && isQueryEnabled && !isAddWidget,
|
||||
@@ -102,11 +103,11 @@ function GridCardGraph({
|
||||
|
||||
const isEmptyLayout = widget?.id === 'empty' || isEmpty(widget);
|
||||
|
||||
if (queryResponse.isRefetching) {
|
||||
if (queryResponse.isRefetching || queryResponse.isLoading) {
|
||||
return <Spinner height="20vh" tip="Loading..." />;
|
||||
}
|
||||
|
||||
if (queryResponse.isError && !isEmptyLayout) {
|
||||
if ((queryResponse.isError && !isEmptyLayout) || !isQueryEnabled) {
|
||||
return (
|
||||
<span ref={graphRef}>
|
||||
{!isEmpty(widget) && prevChartDataSetRef && (
|
||||
@@ -121,38 +122,32 @@ function GridCardGraph({
|
||||
yAxisUnit={yAxisUnit}
|
||||
layout={layout}
|
||||
setLayout={setLayout}
|
||||
allowClone={allowClone}
|
||||
allowDelete={allowDelete}
|
||||
allowEdit={allowEdit}
|
||||
threshold={threshold}
|
||||
headerMenuList={headerMenuList}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
if (queryResponse.status === 'loading' || queryResponse.status === 'idle') {
|
||||
if (!isEmpty(widget) && prevChartDataSetRef?.labels) {
|
||||
return (
|
||||
<span ref={graphRef}>
|
||||
{!isEmpty(widget) && prevChartDataSetRef?.labels ? (
|
||||
<WidgetGraphComponent
|
||||
enableModel={false}
|
||||
enableWidgetHeader
|
||||
widget={widget}
|
||||
queryResponse={queryResponse}
|
||||
errorMessage={errorMessage}
|
||||
data={prevChartDataSetRef}
|
||||
name={name}
|
||||
yAxisUnit={yAxisUnit}
|
||||
layout={layout}
|
||||
setLayout={setLayout}
|
||||
allowClone={allowClone}
|
||||
allowDelete={allowDelete}
|
||||
allowEdit={allowEdit}
|
||||
onClickHandler={onClickHandler}
|
||||
/>
|
||||
) : (
|
||||
<Spinner height="20vh" tip="Loading..." />
|
||||
)}
|
||||
<WidgetGraphComponent
|
||||
enableModel={false}
|
||||
enableWidgetHeader
|
||||
widget={widget}
|
||||
queryResponse={queryResponse}
|
||||
errorMessage={errorMessage}
|
||||
data={prevChartDataSetRef}
|
||||
name={name}
|
||||
yAxisUnit={yAxisUnit}
|
||||
layout={layout}
|
||||
setLayout={setLayout}
|
||||
threshold={threshold}
|
||||
headerMenuList={headerMenuList}
|
||||
onClickHandler={onClickHandler}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -170,9 +165,8 @@ function GridCardGraph({
|
||||
name={name}
|
||||
yAxisUnit={yAxisUnit}
|
||||
onDragSelect={onDragSelect}
|
||||
allowClone={allowClone}
|
||||
allowDelete={allowDelete}
|
||||
allowEdit={allowEdit}
|
||||
threshold={threshold}
|
||||
headerMenuList={headerMenuList}
|
||||
onClickHandler={onClickHandler}
|
||||
/>
|
||||
)}
|
||||
@@ -185,10 +179,9 @@ function GridCardGraph({
|
||||
GridCardGraph.defaultProps = {
|
||||
onDragSelect: undefined,
|
||||
onClickHandler: undefined,
|
||||
allowDelete: true,
|
||||
allowClone: true,
|
||||
allowEdit: true,
|
||||
isQueryEnabled: true,
|
||||
threshold: undefined,
|
||||
headerMenuList: [MenuItemKeys.View],
|
||||
};
|
||||
|
||||
export default memo(GridCardGraph);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ChartData } from 'chart.js';
|
||||
import { GraphOnClickHandler, ToggleGraphProps } from 'components/Graph/types';
|
||||
import { Dispatch, MutableRefObject, SetStateAction } from 'react';
|
||||
import { Dispatch, MutableRefObject, ReactNode, SetStateAction } from 'react';
|
||||
import { Layout } from 'react-grid-layout';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { DeleteWidgetProps } from 'store/actions/dashboard/deleteWidget';
|
||||
@@ -10,6 +10,7 @@ import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
|
||||
import { LayoutProps } from '..';
|
||||
import { MenuItemKeys } from '../WidgetHeader/contants';
|
||||
import { LegendEntryProps } from './FullView/types';
|
||||
|
||||
export interface GraphVisibilityLegendEntryProps {
|
||||
@@ -38,25 +39,21 @@ export interface WidgetGraphComponentProps extends DispatchProps {
|
||||
setLayout?: Dispatch<SetStateAction<LayoutProps[]>>;
|
||||
onDragSelect?: (start: number, end: number) => void;
|
||||
onClickHandler?: GraphOnClickHandler;
|
||||
allowDelete?: boolean;
|
||||
allowClone?: boolean;
|
||||
allowEdit?: boolean;
|
||||
threshold?: ReactNode;
|
||||
headerMenuList: MenuItemKeys[];
|
||||
}
|
||||
|
||||
export interface GridCardGraphProps {
|
||||
widget: Widgets;
|
||||
name: string;
|
||||
yAxisUnit: string | undefined;
|
||||
// eslint-disable-next-line react/require-default-props
|
||||
layout?: Layout[];
|
||||
// eslint-disable-next-line react/require-default-props
|
||||
setLayout?: Dispatch<SetStateAction<LayoutProps[]>>;
|
||||
onDragSelect?: (start: number, end: number) => void;
|
||||
onClickHandler?: GraphOnClickHandler;
|
||||
allowDelete?: boolean;
|
||||
allowClone?: boolean;
|
||||
allowEdit?: boolean;
|
||||
isQueryEnabled?: boolean;
|
||||
threshold?: ReactNode;
|
||||
headerMenuList?: WidgetGraphComponentProps['headerMenuList'];
|
||||
isQueryEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface GetGraphVisibilityStateOnLegendClickProps {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
|
||||
import {
|
||||
DisplayThresholdContainer,
|
||||
TypographHeading,
|
||||
Typography,
|
||||
} from './styles';
|
||||
import { DisplayThresholdProps } from './types';
|
||||
|
||||
function DisplayThreshold({ threshold }: DisplayThresholdProps): JSX.Element {
|
||||
return (
|
||||
<DisplayThresholdContainer>
|
||||
<TypographHeading>Threshold </TypographHeading>
|
||||
<Typography>{threshold || <InfoCircleOutlined />}</Typography>
|
||||
</DisplayThresholdContainer>
|
||||
);
|
||||
}
|
||||
|
||||
export default DisplayThreshold;
|
||||
@@ -3,6 +3,7 @@ export enum MenuItemKeys {
|
||||
Edit = 'edit',
|
||||
Delete = 'delete',
|
||||
Clone = 'clone',
|
||||
CreateAlerts = 'createAlerts',
|
||||
}
|
||||
|
||||
export const MENUITEM_KEYS_VS_LABELS = {
|
||||
@@ -10,4 +11,5 @@ export const MENUITEM_KEYS_VS_LABELS = {
|
||||
[MenuItemKeys.Edit]: 'Edit',
|
||||
[MenuItemKeys.Delete]: 'Delete',
|
||||
[MenuItemKeys.Clone]: 'Clone',
|
||||
[MenuItemKeys.CreateAlerts]: 'Create Alerts',
|
||||
};
|
||||
|
||||
@@ -7,12 +7,12 @@ import {
|
||||
FullscreenOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Dropdown, MenuProps, Tooltip, Typography } from 'antd';
|
||||
import { MenuItemType } from 'antd/es/menu/hooks/useItems';
|
||||
import Spinner from 'components/Spinner';
|
||||
import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
|
||||
import ROUTES from 'constants/routes';
|
||||
import useComponentPermission from 'hooks/useComponentPermission';
|
||||
import history from 'lib/history';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { ReactNode, useCallback, useMemo, useState } from 'react';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
@@ -32,12 +32,14 @@ import {
|
||||
ArrowContainer,
|
||||
HeaderContainer,
|
||||
HeaderContentContainer,
|
||||
ThesholdContainer,
|
||||
WidgetHeaderContainer,
|
||||
} from './styles';
|
||||
import { KeyMethodMappingProps, MenuItem, TWidgetOptions } from './types';
|
||||
import { MenuItem } from './types';
|
||||
import { generateMenuList, isTWidgetOptions } from './utils';
|
||||
|
||||
interface IWidgetHeaderProps {
|
||||
title: string;
|
||||
title: ReactNode;
|
||||
widget: Widgets;
|
||||
onView: VoidFunction;
|
||||
onDelete?: VoidFunction;
|
||||
@@ -47,10 +49,10 @@ interface IWidgetHeaderProps {
|
||||
SuccessResponse<MetricRangePayloadProps> | ErrorResponse
|
||||
>;
|
||||
errorMessage: string | undefined;
|
||||
allowDelete?: boolean;
|
||||
allowClone?: boolean;
|
||||
allowEdit?: boolean;
|
||||
threshold?: ReactNode;
|
||||
headerMenuList?: MenuItemKeys[];
|
||||
}
|
||||
|
||||
function WidgetHeader({
|
||||
title,
|
||||
widget,
|
||||
@@ -60,9 +62,8 @@ function WidgetHeader({
|
||||
parentHover,
|
||||
queryResponse,
|
||||
errorMessage,
|
||||
allowClone = true,
|
||||
allowDelete = true,
|
||||
allowEdit = true,
|
||||
threshold,
|
||||
headerMenuList,
|
||||
}: IWidgetHeaderProps): JSX.Element {
|
||||
const [localHover, setLocalHover] = useState(false);
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||
@@ -78,32 +79,30 @@ function WidgetHeader({
|
||||
);
|
||||
}, [widget.id, widget.panelTypes, widget.query]);
|
||||
|
||||
const keyMethodMapping: KeyMethodMappingProps<TWidgetOptions> = useMemo(
|
||||
const onCreateAlertsHandler = useCallback(() => {
|
||||
history.push(
|
||||
`${ROUTES.ALERTS_NEW}?${
|
||||
queryParamNamesMap.compositeQuery
|
||||
}=${encodeURIComponent(JSON.stringify(widget.query))}`,
|
||||
);
|
||||
}, [widget]);
|
||||
|
||||
const keyMethodMapping = useMemo(
|
||||
() => ({
|
||||
view: {
|
||||
key: MenuItemKeys.View,
|
||||
method: onView,
|
||||
},
|
||||
edit: {
|
||||
key: MenuItemKeys.Edit,
|
||||
method: onEditHandler,
|
||||
},
|
||||
delete: {
|
||||
key: MenuItemKeys.Delete,
|
||||
method: onDelete,
|
||||
},
|
||||
clone: {
|
||||
key: MenuItemKeys.Clone,
|
||||
method: onClone,
|
||||
},
|
||||
[MenuItemKeys.View]: onView,
|
||||
[MenuItemKeys.Edit]: onEditHandler,
|
||||
[MenuItemKeys.Delete]: onDelete,
|
||||
[MenuItemKeys.Clone]: onClone,
|
||||
[MenuItemKeys.CreateAlerts]: onCreateAlertsHandler,
|
||||
}),
|
||||
[onDelete, onEditHandler, onView, onClone],
|
||||
[onDelete, onEditHandler, onView, onClone, onCreateAlertsHandler],
|
||||
);
|
||||
|
||||
const onMenuItemSelectHandler: MenuProps['onClick'] = useCallback(
|
||||
({ key }: { key: string }): void => {
|
||||
if (isTWidgetOptions(key)) {
|
||||
const functionToCall = keyMethodMapping[key]?.method;
|
||||
const functionToCall = keyMethodMapping[key];
|
||||
|
||||
if (functionToCall) {
|
||||
functionToCall();
|
||||
setIsOpen(false);
|
||||
@@ -125,46 +124,43 @@ function WidgetHeader({
|
||||
key: MenuItemKeys.View,
|
||||
icon: <FullscreenOutlined />,
|
||||
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.View],
|
||||
isVisible: true,
|
||||
isVisible: headerMenuList?.includes(MenuItemKeys.View) || false,
|
||||
disabled: queryResponse.isLoading,
|
||||
},
|
||||
{
|
||||
key: MenuItemKeys.Edit,
|
||||
icon: <EditFilled />,
|
||||
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Edit],
|
||||
isVisible: allowEdit,
|
||||
isVisible: headerMenuList?.includes(MenuItemKeys.Edit) || false,
|
||||
disabled: !editWidget,
|
||||
},
|
||||
{
|
||||
key: MenuItemKeys.Clone,
|
||||
icon: <CopyOutlined />,
|
||||
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Clone],
|
||||
isVisible: allowClone,
|
||||
isVisible: headerMenuList?.includes(MenuItemKeys.Clone) || false,
|
||||
disabled: !editWidget,
|
||||
},
|
||||
{
|
||||
key: MenuItemKeys.Delete,
|
||||
icon: <DeleteOutlined />,
|
||||
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Delete],
|
||||
isVisible: allowDelete,
|
||||
isVisible: headerMenuList?.includes(MenuItemKeys.Delete) || false,
|
||||
disabled: !deleteWidget,
|
||||
danger: true,
|
||||
},
|
||||
{
|
||||
key: MenuItemKeys.CreateAlerts,
|
||||
icon: <DeleteOutlined />,
|
||||
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.CreateAlerts],
|
||||
isVisible: headerMenuList?.includes(MenuItemKeys.CreateAlerts) || false,
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
[
|
||||
allowEdit,
|
||||
allowClone,
|
||||
allowDelete,
|
||||
queryResponse.isLoading,
|
||||
deleteWidget,
|
||||
editWidget,
|
||||
],
|
||||
[queryResponse.isLoading, headerMenuList, editWidget, deleteWidget],
|
||||
);
|
||||
|
||||
const menuList: MenuItemType[] = useMemo(
|
||||
(): MenuItemType[] => generateMenuList(actions, keyMethodMapping),
|
||||
[actions, keyMethodMapping],
|
||||
);
|
||||
const updatedMenuList = useMemo(() => generateMenuList(actions), [actions]);
|
||||
|
||||
const onClickHandler = useCallback(() => {
|
||||
setIsOpen((open) => !open);
|
||||
@@ -172,14 +168,14 @@ function WidgetHeader({
|
||||
|
||||
const menu = useMemo(
|
||||
() => ({
|
||||
items: menuList,
|
||||
items: updatedMenuList,
|
||||
onClick: onMenuItemSelectHandler,
|
||||
}),
|
||||
[menuList, onMenuItemSelectHandler],
|
||||
[updatedMenuList, onMenuItemSelectHandler],
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<WidgetHeaderContainer>
|
||||
<Dropdown
|
||||
destroyPopupOnHide
|
||||
open={isOpen}
|
||||
@@ -204,6 +200,7 @@ function WidgetHeader({
|
||||
</HeaderContentContainer>
|
||||
</HeaderContainer>
|
||||
</Dropdown>
|
||||
<ThesholdContainer>{threshold}</ThesholdContainer>
|
||||
{queryResponse.isFetching && !queryResponse.isError && (
|
||||
<Spinner height="5vh" style={spinnerStyles} />
|
||||
)}
|
||||
@@ -212,16 +209,15 @@ function WidgetHeader({
|
||||
<ExclamationCircleOutlined style={tooltipStyles} />
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</WidgetHeaderContainer>
|
||||
);
|
||||
}
|
||||
|
||||
WidgetHeader.defaultProps = {
|
||||
onDelete: undefined,
|
||||
onClone: undefined,
|
||||
allowDelete: true,
|
||||
allowClone: true,
|
||||
allowEdit: true,
|
||||
threshold: undefined,
|
||||
headerMenuList: [MenuItemKeys.View],
|
||||
};
|
||||
|
||||
export default WidgetHeader;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { grey } from '@ant-design/colors';
|
||||
import { Typography as TypographyComponent } from 'antd';
|
||||
import { themeColors } from 'constants/theme';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const HeaderContainer = styled.div<{ hover: boolean }>`
|
||||
@@ -24,3 +26,34 @@ export const ArrowContainer = styled.span<{ hover: boolean }>`
|
||||
position: absolute;
|
||||
right: -1rem;
|
||||
`;
|
||||
|
||||
export const ThesholdContainer = styled.span`
|
||||
margin-top: -0.3rem;
|
||||
`;
|
||||
|
||||
export const DisplayThresholdContainer = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: auto;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
export const WidgetHeaderContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
export const Typography = styled(TypographyComponent)`
|
||||
&&& {
|
||||
color: ${themeColors.white};
|
||||
width: auto;
|
||||
margin-left: 0.2rem;
|
||||
}
|
||||
`;
|
||||
|
||||
export const TypographHeading = styled(TypographyComponent)`
|
||||
&&& {
|
||||
color: ${grey[2]};
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ReactNode } from 'react';
|
||||
import { MenuItemKeys } from './contants';
|
||||
|
||||
export interface MenuItem {
|
||||
key: TWidgetOptions;
|
||||
key: MenuItemKeys;
|
||||
icon: ReactNode;
|
||||
label: string;
|
||||
isVisible: boolean;
|
||||
@@ -11,15 +11,6 @@ export interface MenuItem {
|
||||
danger?: boolean;
|
||||
}
|
||||
|
||||
export type TWidgetOptions =
|
||||
| MenuItemKeys.View
|
||||
| MenuItemKeys.Edit
|
||||
| MenuItemKeys.Delete
|
||||
| MenuItemKeys.Clone;
|
||||
|
||||
export type KeyMethodMappingProps<T extends TWidgetOptions> = {
|
||||
[K in T]: {
|
||||
key: TWidgetOptions;
|
||||
method?: VoidFunction;
|
||||
};
|
||||
};
|
||||
export interface DisplayThresholdProps {
|
||||
threshold: ReactNode;
|
||||
}
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
import { MenuItemType } from 'antd/es/menu/hooks/useItems';
|
||||
|
||||
import { MenuItemKeys } from './contants';
|
||||
import { KeyMethodMappingProps, MenuItem, TWidgetOptions } from './types';
|
||||
import { MenuItem } from './types';
|
||||
|
||||
export const generateMenuList = (
|
||||
actions: MenuItem[],
|
||||
keyMethodMapping: KeyMethodMappingProps<TWidgetOptions>,
|
||||
): MenuItemType[] =>
|
||||
export const generateMenuList = (actions: MenuItem[]): MenuItemType[] =>
|
||||
actions
|
||||
.filter((action: MenuItem) => action.isVisible)
|
||||
.map(({ key, icon: Icon, label, disabled, ...rest }) => ({
|
||||
key: keyMethodMapping[key].key,
|
||||
key,
|
||||
icon: Icon,
|
||||
label,
|
||||
disabled,
|
||||
...rest,
|
||||
}));
|
||||
|
||||
export const isTWidgetOptions = (value: string): value is TWidgetOptions =>
|
||||
export const isTWidgetOptions = (value: string): value is MenuItemKeys =>
|
||||
value === MenuItemKeys.View ||
|
||||
value === MenuItemKeys.Edit ||
|
||||
value === MenuItemKeys.Delete ||
|
||||
value === MenuItemKeys.Clone;
|
||||
value === MenuItemKeys.Clone ||
|
||||
value === MenuItemKeys.CreateAlerts;
|
||||
|
||||
8
frontend/src/container/GridGraphLayout/config.ts
Normal file
8
frontend/src/container/GridGraphLayout/config.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { MenuItemKeys } from 'container/GridGraphLayout/WidgetHeader/contants';
|
||||
|
||||
export const headerMenuList = [
|
||||
MenuItemKeys.View,
|
||||
MenuItemKeys.Clone,
|
||||
MenuItemKeys.Delete,
|
||||
MenuItemKeys.Edit,
|
||||
];
|
||||
@@ -29,6 +29,7 @@ import { Dashboard, Widgets } from 'types/api/dashboard/getAll';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
import DashboardReducer from 'types/reducer/dashboards';
|
||||
|
||||
import { headerMenuList } from './config';
|
||||
import Graph from './Graph';
|
||||
import GraphLayoutContainer from './GraphLayout';
|
||||
import { UpdateDashboard } from './utils';
|
||||
@@ -49,6 +50,7 @@ export const getPreLayouts = (
|
||||
yAxisUnit={widget?.yAxisUnit}
|
||||
layout={layout}
|
||||
setLayout={setLayout}
|
||||
headerMenuList={headerMenuList}
|
||||
/>
|
||||
);
|
||||
},
|
||||
@@ -233,6 +235,7 @@ function GridGraph(props: Props): JSX.Element {
|
||||
layout={layout}
|
||||
setLayout={setLayout}
|
||||
onDragSelect={onDragSelect}
|
||||
headerMenuList={headerMenuList}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
@@ -38,6 +38,7 @@ export const UpdateDashboard = async (
|
||||
query: widgetData?.query || initialQueriesMap.metrics,
|
||||
timePreferance: widgetData?.timePreferance || 'GLOBAL_TIME',
|
||||
title: widgetData ? copyTitle : '',
|
||||
yAxisUnit: widgetData?.yAxisUnit,
|
||||
},
|
||||
],
|
||||
layout,
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
} from 'components/Graph/types';
|
||||
import { GridTableComponentProps } from 'container/GridTableComponent/types';
|
||||
import { GridValueComponentProps } from 'container/GridValueComponent/types';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { QueryDataV3 } from 'types/api/widgets/getQuery';
|
||||
|
||||
@@ -14,7 +15,7 @@ import { PANEL_TYPES } from '../../constants/queryBuilder';
|
||||
export type GridPanelSwitchProps = {
|
||||
panelType: PANEL_TYPES;
|
||||
data: ChartData;
|
||||
title?: string;
|
||||
title?: Widgets['title'];
|
||||
opacity?: string;
|
||||
isStacked?: boolean;
|
||||
onClickHandler?: GraphOnClickHandler;
|
||||
|
||||
12
frontend/src/container/GridPanelSwitch/utils.ts
Normal file
12
frontend/src/container/GridPanelSwitch/utils.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
export const generateGridTitle = (title: ReactNode): string => {
|
||||
if (React.isValidElement(title)) {
|
||||
return Array.isArray(title.props.children)
|
||||
? title.props.children
|
||||
.map((child: ReactNode) => (typeof child === 'string' ? child : ''))
|
||||
.join(' ')
|
||||
: title.props.children;
|
||||
}
|
||||
return title?.toString() || '';
|
||||
};
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Typography } from 'antd';
|
||||
import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
|
||||
import ValueGraph from 'components/ValueGraph';
|
||||
import { memo } from 'react';
|
||||
import { generateGridTitle } from 'container/GridPanelSwitch/utils';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import { TitleContainer, ValueContainer } from './styles';
|
||||
@@ -15,6 +16,7 @@ function GridValueComponent({
|
||||
const value = (((data.datasets[0] || []).data || [])[0] || 0) as number;
|
||||
|
||||
const location = useLocation();
|
||||
const gridTitle = useMemo(() => generateGridTitle(title), [title]);
|
||||
|
||||
const isDashboardPage = location.pathname.split('/').length === 3;
|
||||
|
||||
@@ -29,7 +31,7 @@ function GridValueComponent({
|
||||
return (
|
||||
<>
|
||||
<TitleContainer isDashboardPage={isDashboardPage}>
|
||||
<Typography>{title}</Typography>
|
||||
<Typography>{gridTitle}</Typography>
|
||||
</TitleContainer>
|
||||
<ValueContainer isDashboardPage={isDashboardPage}>
|
||||
<ValueGraph
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { ChartData } from 'chart.js';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export type GridValueComponentProps = {
|
||||
data: ChartData;
|
||||
title?: string;
|
||||
title?: ReactNode;
|
||||
yAxisUnit?: string;
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
CaretUpFilled,
|
||||
LogoutOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Button, Divider, Dropdown, MenuProps, Space, Typography } from 'antd';
|
||||
import { Button, Divider, MenuProps, Space, Typography } from 'antd';
|
||||
import { Logout } from 'api/utils';
|
||||
import ROUTES from 'constants/routes';
|
||||
import Config from 'container/ConfigDropdown';
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
LogoutContainer,
|
||||
NavLinkWrapper,
|
||||
ToggleButton,
|
||||
UserDropdown,
|
||||
} from './styles';
|
||||
|
||||
function HeaderContainer(): JSX.Element {
|
||||
@@ -133,7 +134,7 @@ function HeaderContainer(): JSX.Element {
|
||||
unCheckedChildren="🌞"
|
||||
/>
|
||||
|
||||
<Dropdown
|
||||
<UserDropdown
|
||||
onOpenChange={onToggleHandler(setIsUserDropDownOpen)}
|
||||
trigger={['click']}
|
||||
menu={menu}
|
||||
@@ -145,7 +146,7 @@ function HeaderContainer(): JSX.Element {
|
||||
{!isUserDropDownOpen ? <CaretDownFilled /> : <CaretUpFilled />}
|
||||
</IconContainer>
|
||||
</Space>
|
||||
</Dropdown>
|
||||
</UserDropdown>
|
||||
</Space>
|
||||
</Container>
|
||||
</Header>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Avatar, Layout, Switch, Typography } from 'antd';
|
||||
import { Avatar, Dropdown, Layout, Switch, Typography } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Header = styled(Layout.Header)`
|
||||
@@ -82,3 +82,7 @@ export const NavLinkWrapper = styled.div`
|
||||
export const AvatarWrapper = styled(Avatar)`
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
`;
|
||||
|
||||
export const UserDropdown = styled(Dropdown)`
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import { Modal } from 'antd';
|
||||
import { useCallback } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators, Dispatch } from 'redux';
|
||||
import { ThunkDispatch } from 'redux-thunk';
|
||||
@@ -9,11 +10,11 @@ import AppActions from 'types/actions';
|
||||
import { Data } from '../index';
|
||||
import { TableLinkText } from './styles';
|
||||
|
||||
const { confirm } = Modal;
|
||||
|
||||
function DeleteButton({ deleteDashboard, id }: DeleteButtonProps): JSX.Element {
|
||||
const openConfirmationDialog = (): void => {
|
||||
confirm({
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
|
||||
const openConfirmationDialog = useCallback((): void => {
|
||||
modal.confirm({
|
||||
title: 'Do you really want to delete this dashboard?',
|
||||
icon: <ExclamationCircleOutlined style={{ color: '#e42b35' }} />,
|
||||
onOk() {
|
||||
@@ -25,12 +26,16 @@ function DeleteButton({ deleteDashboard, id }: DeleteButtonProps): JSX.Element {
|
||||
okButtonProps: { danger: true },
|
||||
centered: true,
|
||||
});
|
||||
};
|
||||
}, [id, modal, deleteDashboard]);
|
||||
|
||||
return (
|
||||
<TableLinkText type="danger" onClick={openConfirmationDialog}>
|
||||
Delete
|
||||
</TableLinkText>
|
||||
<>
|
||||
<TableLinkText type="danger" onClick={openConfirmationDialog}>
|
||||
Delete
|
||||
</TableLinkText>
|
||||
|
||||
{contextHolder}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@ function LogsContextList({
|
||||
PANEL_TYPES.LIST,
|
||||
{
|
||||
keepPreviousData: true,
|
||||
enabled: !!requestData,
|
||||
onSuccess: handleSuccess,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { initialFilters } from 'constants/queryBuilder';
|
||||
import { getPaginationQueryData } from 'lib/newQueryBuilder/getPaginationQueryData';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
import {
|
||||
@@ -28,7 +29,7 @@ export const getRequestData = ({
|
||||
if (!query) return null;
|
||||
|
||||
const paginateData = getPaginationQueryData({
|
||||
currentStagedQueryData: stagedQueryData,
|
||||
filters: stagedQueryData?.filters || initialFilters,
|
||||
listItemId: log ? log.id : null,
|
||||
orderByTimestamp,
|
||||
page,
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
import { Tabs } from 'antd';
|
||||
import { themeColors } from 'constants/theme';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const TabsStyled = styled(Tabs)`
|
||||
& .ant-tabs-nav {
|
||||
background-color: ${themeColors.lightBlack};
|
||||
}
|
||||
`;
|
||||
|
||||
export const ActionsWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { TabsProps } from 'antd';
|
||||
import { Tabs, TabsProps } from 'antd';
|
||||
import TabLabel from 'components/TabLabel';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import {
|
||||
initialAutocompleteData,
|
||||
initialFilters,
|
||||
initialQueriesMap,
|
||||
initialQueryBuilderFormValues,
|
||||
PANEL_TYPES,
|
||||
} from 'constants/queryBuilder';
|
||||
import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
|
||||
@@ -36,11 +38,12 @@ import {
|
||||
IBuilderQuery,
|
||||
OrderByPayload,
|
||||
Query,
|
||||
TagFilter,
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource, StringOperators } from 'types/common/queryBuilder';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { ActionsWrapper, TabsStyled } from './LogsExplorerViews.styled';
|
||||
import { ActionsWrapper } from './LogsExplorerViews.styled';
|
||||
|
||||
function LogsExplorerViews(): JSX.Element {
|
||||
const { notifications } = useNotifications();
|
||||
@@ -75,19 +78,19 @@ function LogsExplorerViews(): JSX.Element {
|
||||
|
||||
const handleAxisError = useAxiosError();
|
||||
|
||||
const currentStagedQueryData = useMemo(() => {
|
||||
if (!stagedQuery || stagedQuery.builder.queryData.length !== 1) return null;
|
||||
const listQuery = useMemo(() => {
|
||||
if (!stagedQuery || stagedQuery.builder.queryData.length < 1) return null;
|
||||
|
||||
return stagedQuery.builder.queryData[0];
|
||||
return stagedQuery.builder.queryData.find((item) => !item.disabled) || null;
|
||||
}, [stagedQuery]);
|
||||
|
||||
const orderByTimestamp: OrderByPayload | null = useMemo(() => {
|
||||
const timestampOrderBy = currentStagedQueryData?.orderBy.find(
|
||||
const timestampOrderBy = listQuery?.orderBy.find(
|
||||
(item) => item.columnName === 'timestamp',
|
||||
);
|
||||
|
||||
return timestampOrderBy || null;
|
||||
}, [currentStagedQueryData]);
|
||||
}, [listQuery]);
|
||||
|
||||
const isMultipleQueries = useMemo(
|
||||
() =>
|
||||
@@ -106,17 +109,17 @@ function LogsExplorerViews(): JSX.Element {
|
||||
}, [currentQuery]);
|
||||
|
||||
const isLimit: boolean = useMemo(() => {
|
||||
if (!currentStagedQueryData) return false;
|
||||
if (!currentStagedQueryData.limit) return false;
|
||||
if (!listQuery) return false;
|
||||
if (!listQuery.limit) return false;
|
||||
|
||||
return logs.length >= currentStagedQueryData.limit;
|
||||
}, [logs.length, currentStagedQueryData]);
|
||||
return logs.length >= listQuery.limit;
|
||||
}, [logs.length, listQuery]);
|
||||
|
||||
const listChartQuery = useMemo(() => {
|
||||
if (!stagedQuery || !currentStagedQueryData) return null;
|
||||
if (!stagedQuery || !listQuery) return null;
|
||||
|
||||
const modifiedQueryData: IBuilderQuery = {
|
||||
...currentStagedQueryData,
|
||||
...listQuery,
|
||||
aggregateOperator: StringOperators.COUNT,
|
||||
};
|
||||
|
||||
@@ -132,7 +135,7 @@ function LogsExplorerViews(): JSX.Element {
|
||||
};
|
||||
|
||||
return modifiedQuery;
|
||||
}, [stagedQuery, currentStagedQueryData]);
|
||||
}, [stagedQuery, listQuery]);
|
||||
|
||||
const exportDefaultQuery = useMemo(
|
||||
() =>
|
||||
@@ -147,6 +150,9 @@ function LogsExplorerViews(): JSX.Element {
|
||||
const listChartData = useGetExplorerQueryRange(
|
||||
listChartQuery,
|
||||
PANEL_TYPES.TIME_SERIES,
|
||||
{
|
||||
enabled: !!listChartQuery && panelType === PANEL_TYPES.LIST,
|
||||
},
|
||||
);
|
||||
|
||||
const { data, isFetching, isError } = useGetExplorerQueryRange(
|
||||
@@ -154,7 +160,7 @@ function LogsExplorerViews(): JSX.Element {
|
||||
panelType,
|
||||
{
|
||||
keepPreviousData: true,
|
||||
enabled: !isLimit,
|
||||
enabled: !isLimit && !!requestData,
|
||||
},
|
||||
{
|
||||
...(timeRange &&
|
||||
@@ -205,52 +211,66 @@ function LogsExplorerViews(): JSX.Element {
|
||||
const getRequestData = useCallback(
|
||||
(
|
||||
query: Query | null,
|
||||
params: { page: number; log: ILog | null; pageSize: number },
|
||||
params: {
|
||||
page: number;
|
||||
log: ILog | null;
|
||||
pageSize: number;
|
||||
filters: TagFilter;
|
||||
},
|
||||
): Query | null => {
|
||||
if (!query) return null;
|
||||
|
||||
const paginateData = getPaginationQueryData({
|
||||
currentStagedQueryData,
|
||||
filters: params.filters,
|
||||
listItemId: params.log ? params.log.id : null,
|
||||
orderByTimestamp,
|
||||
page: params.page,
|
||||
pageSize: params.pageSize,
|
||||
});
|
||||
|
||||
const queryData: IBuilderQuery[] =
|
||||
query.builder.queryData.length > 1
|
||||
? query.builder.queryData
|
||||
: [
|
||||
{
|
||||
...(listQuery || initialQueryBuilderFormValues),
|
||||
...paginateData,
|
||||
},
|
||||
];
|
||||
|
||||
const data: Query = {
|
||||
...query,
|
||||
builder: {
|
||||
...query.builder,
|
||||
queryData: query.builder.queryData.map((item) => ({
|
||||
...item,
|
||||
...paginateData,
|
||||
pageSize: params.pageSize,
|
||||
})),
|
||||
queryData,
|
||||
},
|
||||
};
|
||||
|
||||
return data;
|
||||
},
|
||||
[currentStagedQueryData, orderByTimestamp],
|
||||
[orderByTimestamp, listQuery],
|
||||
);
|
||||
|
||||
const handleEndReached = useCallback(
|
||||
(index: number) => {
|
||||
if (!listQuery) return;
|
||||
|
||||
if (isLimit) return;
|
||||
if (logs.length < pageSize) return;
|
||||
|
||||
const { limit, filters } = listQuery;
|
||||
|
||||
const lastLog = logs[index];
|
||||
|
||||
const limit = currentStagedQueryData?.limit;
|
||||
|
||||
const nextLogsLenth = logs.length + pageSize;
|
||||
const nextLogsLength = logs.length + pageSize;
|
||||
|
||||
const nextPageSize =
|
||||
limit && nextLogsLenth >= limit ? limit - logs.length : pageSize;
|
||||
limit && nextLogsLength >= limit ? limit - logs.length : pageSize;
|
||||
|
||||
if (!stagedQuery) return;
|
||||
|
||||
const newRequestData = getRequestData(stagedQuery, {
|
||||
filters,
|
||||
page: page + 1,
|
||||
log: orderByTimestamp ? lastLog : null,
|
||||
pageSize: nextPageSize,
|
||||
@@ -263,7 +283,7 @@ function LogsExplorerViews(): JSX.Element {
|
||||
[
|
||||
isLimit,
|
||||
logs,
|
||||
currentStagedQueryData?.limit,
|
||||
listQuery,
|
||||
pageSize,
|
||||
stagedQuery,
|
||||
getRequestData,
|
||||
@@ -367,11 +387,13 @@ function LogsExplorerViews(): JSX.Element {
|
||||
currentMinTimeRef.current !== minTime
|
||||
) {
|
||||
const newRequestData = getRequestData(stagedQuery, {
|
||||
filters: listQuery?.filters || initialFilters,
|
||||
page: 1,
|
||||
log: null,
|
||||
pageSize:
|
||||
timeRange?.pageSize && activeLogId ? timeRange?.pageSize : pageSize,
|
||||
});
|
||||
|
||||
setLogs([]);
|
||||
setPage(1);
|
||||
setRequestData(newRequestData);
|
||||
@@ -385,11 +407,13 @@ function LogsExplorerViews(): JSX.Element {
|
||||
stagedQuery,
|
||||
requestData,
|
||||
getRequestData,
|
||||
listQuery,
|
||||
pageSize,
|
||||
minTime,
|
||||
timeRange,
|
||||
activeLogId,
|
||||
onTimeRangeChange,
|
||||
panelType,
|
||||
]);
|
||||
|
||||
const tabsItems: TabsProps['items'] = useMemo(
|
||||
@@ -407,7 +431,7 @@ function LogsExplorerViews(): JSX.Element {
|
||||
children: (
|
||||
<LogsExplorerList
|
||||
isLoading={isFetching}
|
||||
currentStagedQueryData={currentStagedQueryData}
|
||||
currentStagedQueryData={listQuery}
|
||||
logs={logs}
|
||||
onEndReached={handleEndReached}
|
||||
/>
|
||||
@@ -435,7 +459,7 @@ function LogsExplorerViews(): JSX.Element {
|
||||
isMultipleQueries,
|
||||
isGroupByExist,
|
||||
isFetching,
|
||||
currentStagedQueryData,
|
||||
listQuery,
|
||||
logs,
|
||||
handleEndReached,
|
||||
data,
|
||||
@@ -463,10 +487,14 @@ function LogsExplorerViews(): JSX.Element {
|
||||
(queryData) => queryData.groupBy.length > 0,
|
||||
);
|
||||
|
||||
return isGroupByExist
|
||||
? data.payload.data.result
|
||||
: [data.payload.data.result[0]];
|
||||
}, [stagedQuery, data, panelType, listChartData]);
|
||||
const firstPayloadQuery = data.payload.data.result.find(
|
||||
(item) => item.queryName === listQuery?.queryName,
|
||||
);
|
||||
|
||||
const firstPayloadQueryArray = firstPayloadQuery ? [firstPayloadQuery] : [];
|
||||
|
||||
return isGroupByExist ? data.payload.data.result : firstPayloadQueryArray;
|
||||
}, [stagedQuery, panelType, data, listChartData, listQuery]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -480,7 +508,7 @@ function LogsExplorerViews(): JSX.Element {
|
||||
/>
|
||||
</ActionsWrapper>
|
||||
)}
|
||||
<TabsStyled
|
||||
<Tabs
|
||||
items={tabsItems}
|
||||
defaultActiveKey={panelType || PANEL_TYPES.LIST}
|
||||
activeKey={panelType || PANEL_TYPES.LIST}
|
||||
|
||||
@@ -5,6 +5,7 @@ import RawLogView from 'components/Logs/RawLogView';
|
||||
import LogsTableView from 'components/Logs/TableView';
|
||||
import Spinner from 'components/Spinner';
|
||||
import { contentStyle } from 'container/Trace/Search/config';
|
||||
import { useActiveLog } from 'hooks/logs/useActiveLog';
|
||||
import useFontFaceObserver from 'hooks/useFontObserver';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
@@ -26,6 +27,8 @@ type LogsTableProps = {
|
||||
function LogsTable(props: LogsTableProps): JSX.Element {
|
||||
const { viewMode, linesPerRow } = props;
|
||||
|
||||
const { onSetActiveLog } = useActiveLog();
|
||||
|
||||
useFontFaceObserver(
|
||||
[
|
||||
{
|
||||
@@ -72,7 +75,12 @@ function LogsTable(props: LogsTableProps): JSX.Element {
|
||||
const renderContent = useMemo(() => {
|
||||
if (viewMode === 'table') {
|
||||
return (
|
||||
<LogsTableView logs={logs} fields={selected} linesPerRow={linesPerRow} />
|
||||
<LogsTableView
|
||||
onClickExpand={onSetActiveLog}
|
||||
logs={logs}
|
||||
fields={selected}
|
||||
linesPerRow={linesPerRow}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -85,7 +93,7 @@ function LogsTable(props: LogsTableProps): JSX.Element {
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}, [getItemContent, linesPerRow, logs, selected, viewMode]);
|
||||
}, [getItemContent, linesPerRow, logs, onSetActiveLog, selected, viewMode]);
|
||||
|
||||
if (isLoading) {
|
||||
return <Spinner height={20} tip="Getting Logs" />;
|
||||
|
||||
@@ -18,7 +18,13 @@ import {
|
||||
QUERYNAME_AND_EXPRESSION,
|
||||
WidgetKeys,
|
||||
} from '../constant';
|
||||
import { LatencyProps, OperationPerSecProps } from '../Tabs/types';
|
||||
import {
|
||||
ApDexMetricsQueryBuilderQueriesProps,
|
||||
ApDexProps,
|
||||
LatencyProps,
|
||||
OperationPerSecProps,
|
||||
} from '../Tabs/types';
|
||||
import { convertMilSecToNanoSec, getNearestHighestBucketValue } from '../utils';
|
||||
import {
|
||||
getQueryBuilderQueries,
|
||||
getQueryBuilderQuerieswithFormula,
|
||||
@@ -85,6 +91,365 @@ export const latency = ({
|
||||
});
|
||||
};
|
||||
|
||||
export const apDexTracesQueryBuilderQueries = ({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
topLevelOperationsRoute,
|
||||
threashold,
|
||||
}: ApDexProps): QueryBuilderData => {
|
||||
const autoCompleteDataA: BaseAutocompleteData = {
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
key: '',
|
||||
type: null,
|
||||
};
|
||||
|
||||
const autoCompleteDataB: BaseAutocompleteData = {
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
key: '',
|
||||
type: null,
|
||||
};
|
||||
|
||||
const autoCompleteDataC: BaseAutocompleteData = {
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
key: '',
|
||||
type: null,
|
||||
};
|
||||
|
||||
const filterItemA: TagFilterItem[] = [
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.ServiceName,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: true,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS['='],
|
||||
value: servicename,
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.Name,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: true,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
value: [...topLevelOperationsRoute],
|
||||
},
|
||||
...tagFilterItems,
|
||||
];
|
||||
|
||||
const filterItemB: TagFilterItem[] = [
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.HasError,
|
||||
dataType: DataType.BOOL,
|
||||
isColumn: true,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS['='],
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.DurationNano,
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS['<='],
|
||||
value: convertMilSecToNanoSec(threashold),
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.ServiceName,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: true,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS['='],
|
||||
value: servicename,
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.Name,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: true,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
value: [...topLevelOperationsRoute],
|
||||
},
|
||||
];
|
||||
|
||||
const filterItemC: TagFilterItem[] = [
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.DurationNano,
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS['<='],
|
||||
value: convertMilSecToNanoSec(threashold * 4),
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.HasError,
|
||||
dataType: DataType.BOOL,
|
||||
isColumn: true,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS['='],
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.ServiceName,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: true,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS['='],
|
||||
value: servicename,
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.Name,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: true,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
value: [...topLevelOperationsRoute],
|
||||
},
|
||||
];
|
||||
|
||||
const autocompleteData = [
|
||||
autoCompleteDataA,
|
||||
autoCompleteDataB,
|
||||
autoCompleteDataC,
|
||||
];
|
||||
const additionalItems = [filterItemA, filterItemB, filterItemC];
|
||||
const legends = [GraphTitle.APDEX];
|
||||
const disabled = Array(3).fill(true);
|
||||
const expressions = [FORMULA.APDEX_TRACES];
|
||||
const legendFormulas = [GraphTitle.APDEX];
|
||||
const aggregateOperators = [
|
||||
MetricAggregateOperator.COUNT,
|
||||
MetricAggregateOperator.COUNT,
|
||||
MetricAggregateOperator.COUNT,
|
||||
];
|
||||
const dataSource = DataSource.TRACES;
|
||||
|
||||
return getQueryBuilderQuerieswithFormula({
|
||||
autocompleteData,
|
||||
additionalItems,
|
||||
legends,
|
||||
disabled,
|
||||
expressions,
|
||||
legendFormulas,
|
||||
aggregateOperators,
|
||||
dataSource,
|
||||
});
|
||||
};
|
||||
|
||||
export const apDexMetricsQueryBuilderQueries = ({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
topLevelOperationsRoute,
|
||||
threashold,
|
||||
delta,
|
||||
metricsBuckets,
|
||||
}: ApDexMetricsQueryBuilderQueriesProps): QueryBuilderData => {
|
||||
const autoCompleteDataA: BaseAutocompleteData = {
|
||||
key: WidgetKeys.SignozLatencyCount,
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
type: null,
|
||||
};
|
||||
|
||||
const autoCompleteDataB: BaseAutocompleteData = {
|
||||
key: WidgetKeys.Signoz_latency_bucket,
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
type: null,
|
||||
};
|
||||
|
||||
const autoCompleteDataC: BaseAutocompleteData = {
|
||||
key: WidgetKeys.Signoz_latency_bucket,
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
type: null,
|
||||
};
|
||||
|
||||
const filterItemA: TagFilterItem[] = [
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.Service_name,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS['='],
|
||||
value: servicename,
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.Operation,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
value: [...topLevelOperationsRoute],
|
||||
},
|
||||
...tagFilterItems,
|
||||
];
|
||||
|
||||
const filterItemB: TagFilterItem[] = [
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.StatusCode,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS['='],
|
||||
value: 'STATUS_CODE_UNSET',
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.Le,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS['='],
|
||||
value: getNearestHighestBucketValue(threashold * 1000, metricsBuckets),
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.Service_name,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS['='],
|
||||
value: servicename,
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.Operation,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
value: [...topLevelOperationsRoute],
|
||||
},
|
||||
...tagFilterItems,
|
||||
];
|
||||
|
||||
const filterItemC: TagFilterItem[] = [
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.Le,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS['='],
|
||||
value: getNearestHighestBucketValue(threashold * 1000 * 4, metricsBuckets),
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.StatusCode,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS['='],
|
||||
value: 'STATUS_CODE_UNSET',
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.Service_name,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS['='],
|
||||
value: servicename,
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.Operation,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
value: [...topLevelOperationsRoute],
|
||||
},
|
||||
...tagFilterItems,
|
||||
];
|
||||
|
||||
const autocompleteData = [
|
||||
autoCompleteDataA,
|
||||
autoCompleteDataB,
|
||||
autoCompleteDataC,
|
||||
];
|
||||
|
||||
const additionalItems = [filterItemA, filterItemB, filterItemC];
|
||||
const legends = [GraphTitle.APDEX];
|
||||
const disabled = Array(3).fill(true);
|
||||
const expressions = delta
|
||||
? [FORMULA.APDEX_DELTA_SPAN_METRICS]
|
||||
: [FORMULA.APDEX_CUMULATIVE_SPAN_METRICS];
|
||||
const legendFormulas = [GraphTitle.APDEX];
|
||||
const aggregateOperators = [
|
||||
MetricAggregateOperator.SUM_RATE,
|
||||
MetricAggregateOperator.SUM_RATE,
|
||||
MetricAggregateOperator.SUM_RATE,
|
||||
];
|
||||
const dataSource = DataSource.METRICS;
|
||||
|
||||
return getQueryBuilderQuerieswithFormula({
|
||||
autocompleteData,
|
||||
additionalItems,
|
||||
legends,
|
||||
disabled,
|
||||
expressions,
|
||||
legendFormulas,
|
||||
aggregateOperators,
|
||||
dataSource,
|
||||
});
|
||||
};
|
||||
|
||||
export const operationPerSec = ({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
|
||||
@@ -117,9 +117,6 @@ function DBCall(): JSX.Element {
|
||||
'database_call_rps',
|
||||
);
|
||||
}}
|
||||
allowClone={false}
|
||||
allowDelete={false}
|
||||
allowEdit={false}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
@@ -153,9 +150,6 @@ function DBCall(): JSX.Element {
|
||||
'database_call_avg_duration',
|
||||
);
|
||||
}}
|
||||
allowClone={false}
|
||||
allowDelete={false}
|
||||
allowEdit={false}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
|
||||
@@ -156,9 +156,6 @@ function External(): JSX.Element {
|
||||
'external_call_error_percentage',
|
||||
);
|
||||
}}
|
||||
allowClone={false}
|
||||
allowDelete={false}
|
||||
allowEdit={false}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
@@ -194,9 +191,6 @@ function External(): JSX.Element {
|
||||
'external_call_duration',
|
||||
);
|
||||
}}
|
||||
allowClone={false}
|
||||
allowDelete={false}
|
||||
allowEdit={false}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
@@ -233,9 +227,6 @@ function External(): JSX.Element {
|
||||
'external_call_rps_by_address',
|
||||
);
|
||||
}}
|
||||
allowClone={false}
|
||||
allowDelete={false}
|
||||
allowEdit={false}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
@@ -271,9 +262,6 @@ function External(): JSX.Element {
|
||||
'external_call_duration_by_address',
|
||||
);
|
||||
}}
|
||||
allowClone={false}
|
||||
allowDelete={false}
|
||||
allowEdit={false}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
|
||||
@@ -32,7 +32,14 @@ import {
|
||||
errorPercentage,
|
||||
operationPerSec,
|
||||
} from '../MetricsPageQueries/OverviewQueries';
|
||||
import { Card, Col, Row } from '../styles';
|
||||
import {
|
||||
Card,
|
||||
Col,
|
||||
ColApDexContainer,
|
||||
ColErrorContainer,
|
||||
Row,
|
||||
} from '../styles';
|
||||
import ApDex from './Overview/ApDex';
|
||||
import ServiceOverview from './Overview/ServiceOverview';
|
||||
import TopLevelOperation from './Overview/TopLevelOperations';
|
||||
import TopOperation from './Overview/TopOperation';
|
||||
@@ -160,7 +167,7 @@ function Application(): JSX.Element {
|
||||
[dispatch],
|
||||
);
|
||||
|
||||
const onErrorTrackHandler = (timestamp: number): void => {
|
||||
const onErrorTrackHandler = (timestamp: number): (() => void) => (): void => {
|
||||
const currentTime = timestamp;
|
||||
const tPlusOne = timestamp + 60 * 1000;
|
||||
|
||||
@@ -189,8 +196,8 @@ function Application(): JSX.Element {
|
||||
handleGraphClick={handleGraphClick}
|
||||
selectedTimeStamp={selectedTimeStamp}
|
||||
selectedTraceTags={selectedTraceTags}
|
||||
tagFilterItems={tagFilterItems}
|
||||
topLevelOperationsRoute={topLevelOperationsRoute}
|
||||
topLevelOperationsLoading={topLevelOperationsLoading}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
@@ -222,28 +229,48 @@ function Application(): JSX.Element {
|
||||
</Row>
|
||||
<Row gutter={24}>
|
||||
<Col span={12}>
|
||||
<Button
|
||||
type="default"
|
||||
size="small"
|
||||
id="Error_button"
|
||||
onClick={(): void => {
|
||||
onErrorTrackHandler(selectedTimeStamp);
|
||||
}}
|
||||
>
|
||||
View Traces
|
||||
</Button>
|
||||
<ColApDexContainer>
|
||||
<Button
|
||||
type="default"
|
||||
size="small"
|
||||
id="ApDex_button"
|
||||
onClick={onViewTracePopupClick({
|
||||
servicename,
|
||||
selectedTraceTags,
|
||||
timestamp: selectedTimeStamp,
|
||||
})}
|
||||
>
|
||||
View Traces
|
||||
</Button>
|
||||
<ApDex
|
||||
handleGraphClick={handleGraphClick}
|
||||
onDragSelect={onDragSelect}
|
||||
topLevelOperationsRoute={topLevelOperationsRoute}
|
||||
tagFilterItems={tagFilterItems}
|
||||
/>
|
||||
</ColApDexContainer>
|
||||
<ColErrorContainer>
|
||||
<Button
|
||||
type="default"
|
||||
size="small"
|
||||
id="Error_button"
|
||||
onClick={onErrorTrackHandler(selectedTimeStamp)}
|
||||
>
|
||||
View Traces
|
||||
</Button>
|
||||
|
||||
<TopLevelOperation
|
||||
handleGraphClick={handleGraphClick}
|
||||
onDragSelect={onDragSelect}
|
||||
topLevelOperationsError={topLevelOperationsError}
|
||||
topLevelOperationsLoading={topLevelOperationsLoading}
|
||||
topLevelOperationsIsError={topLevelOperationsIsError}
|
||||
name="error_percentage_%"
|
||||
widget={errorPercentageWidget}
|
||||
yAxisUnit="%"
|
||||
opName="Error"
|
||||
/>
|
||||
<TopLevelOperation
|
||||
handleGraphClick={handleGraphClick}
|
||||
onDragSelect={onDragSelect}
|
||||
topLevelOperationsError={topLevelOperationsError}
|
||||
topLevelOperationsLoading={topLevelOperationsLoading}
|
||||
topLevelOperationsIsError={topLevelOperationsIsError}
|
||||
name="error_percentage_%"
|
||||
widget={errorPercentageWidget}
|
||||
yAxisUnit="%"
|
||||
opName="Error"
|
||||
/>
|
||||
</ColErrorContainer>
|
||||
</Col>
|
||||
|
||||
<Col span={12}>
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
import { Space, Typography } from 'antd';
|
||||
import TextToolTip from 'components/TextToolTip';
|
||||
import {
|
||||
apDexToolTipText,
|
||||
apDexToolTipUrl,
|
||||
apDexToolTipUrlText,
|
||||
} from 'constants/apDex';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import Graph from 'container/GridGraphLayout/Graph';
|
||||
import DisplayThreshold from 'container/GridGraphLayout/WidgetHeader/DisplayThreshold';
|
||||
import { GraphTitle } from 'container/MetricsApplication/constant';
|
||||
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
|
||||
import { apDexMetricsQueryBuilderQueries } from 'container/MetricsApplication/MetricsPageQueries/OverviewQueries';
|
||||
import { ReactNode, useMemo } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { IServiceName } from '../../types';
|
||||
import { ApDexMetricsProps } from './types';
|
||||
|
||||
function ApDexMetrics({
|
||||
delta,
|
||||
metricsBuckets,
|
||||
thresholdValue,
|
||||
onDragSelect,
|
||||
tagFilterItems,
|
||||
topLevelOperationsRoute,
|
||||
handleGraphClick,
|
||||
}: ApDexMetricsProps): JSX.Element {
|
||||
const { servicename } = useParams<IServiceName>();
|
||||
|
||||
const apDexMetricsWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder({
|
||||
query: {
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: apDexMetricsQueryBuilderQueries({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
topLevelOperationsRoute,
|
||||
threashold: thresholdValue || 0,
|
||||
delta: delta || false,
|
||||
metricsBuckets: metricsBuckets || [],
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
title: (
|
||||
<Space>
|
||||
<Typography>{GraphTitle.APDEX}</Typography>
|
||||
<TextToolTip
|
||||
text={apDexToolTipText}
|
||||
url={apDexToolTipUrl}
|
||||
useFilledIcon={false}
|
||||
urlText={apDexToolTipUrlText}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||
}),
|
||||
[
|
||||
delta,
|
||||
metricsBuckets,
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
thresholdValue,
|
||||
topLevelOperationsRoute,
|
||||
],
|
||||
);
|
||||
|
||||
const threshold: ReactNode = useMemo(() => {
|
||||
if (thresholdValue) return <DisplayThreshold threshold={thresholdValue} />;
|
||||
return null;
|
||||
}, [thresholdValue]);
|
||||
|
||||
const isQueryEnabled =
|
||||
topLevelOperationsRoute.length > 0 &&
|
||||
metricsBuckets &&
|
||||
metricsBuckets?.length > 0 &&
|
||||
delta !== undefined;
|
||||
|
||||
return (
|
||||
<Graph
|
||||
name="apdex"
|
||||
widget={apDexMetricsWidget}
|
||||
onDragSelect={onDragSelect}
|
||||
onClickHandler={handleGraphClick('ApDex')}
|
||||
yAxisUnit=""
|
||||
threshold={threshold}
|
||||
isQueryEnabled={isQueryEnabled}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
ApDexMetrics.defaultProps = {
|
||||
delta: undefined,
|
||||
le: undefined,
|
||||
};
|
||||
|
||||
export default ApDexMetrics;
|
||||
@@ -0,0 +1,36 @@
|
||||
import Spinner from 'components/Spinner';
|
||||
import { useGetMetricMeta } from 'hooks/apDex/useGetMetricMeta';
|
||||
import useErrorNotification from 'hooks/useErrorNotification';
|
||||
|
||||
import ApDexMetrics from './ApDexMetrics';
|
||||
import { metricMeta } from './constants';
|
||||
import { ApDexDataSwitcherProps } from './types';
|
||||
|
||||
function ApDexMetricsApplication({
|
||||
handleGraphClick,
|
||||
onDragSelect,
|
||||
tagFilterItems,
|
||||
topLevelOperationsRoute,
|
||||
thresholdValue,
|
||||
}: ApDexDataSwitcherProps): JSX.Element {
|
||||
const { data, isLoading, error } = useGetMetricMeta(metricMeta);
|
||||
useErrorNotification(error);
|
||||
|
||||
if (isLoading) {
|
||||
return <Spinner height="40vh" tip="Loading..." />;
|
||||
}
|
||||
|
||||
return (
|
||||
<ApDexMetrics
|
||||
handleGraphClick={handleGraphClick}
|
||||
delta={data?.data.delta}
|
||||
metricsBuckets={data?.data.le}
|
||||
onDragSelect={onDragSelect}
|
||||
topLevelOperationsRoute={topLevelOperationsRoute}
|
||||
tagFilterItems={tagFilterItems}
|
||||
thresholdValue={thresholdValue}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default ApDexMetricsApplication;
|
||||
@@ -0,0 +1,62 @@
|
||||
// This component is not been used in the application as we support only metrics for ApDex as of now.
|
||||
// This component is been kept for future reference.
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import Graph from 'container/GridGraphLayout/Graph';
|
||||
import { GraphTitle } from 'container/MetricsApplication/constant';
|
||||
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
|
||||
import { apDexTracesQueryBuilderQueries } from 'container/MetricsApplication/MetricsPageQueries/OverviewQueries';
|
||||
import { useMemo } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { IServiceName } from '../../types';
|
||||
import { ApDexDataSwitcherProps } from './types';
|
||||
|
||||
function ApDexTraces({
|
||||
handleGraphClick,
|
||||
onDragSelect,
|
||||
topLevelOperationsRoute,
|
||||
tagFilterItems,
|
||||
thresholdValue,
|
||||
}: ApDexDataSwitcherProps): JSX.Element {
|
||||
const { servicename } = useParams<IServiceName>();
|
||||
|
||||
const apDexTracesWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder({
|
||||
query: {
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: apDexTracesQueryBuilderQueries({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
topLevelOperationsRoute,
|
||||
threashold: thresholdValue || 0,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
title: GraphTitle.APDEX,
|
||||
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||
}),
|
||||
[servicename, tagFilterItems, thresholdValue, topLevelOperationsRoute],
|
||||
);
|
||||
|
||||
const isQueryEnabled =
|
||||
topLevelOperationsRoute.length > 0 && thresholdValue !== undefined;
|
||||
|
||||
return (
|
||||
<Graph
|
||||
name="apdex"
|
||||
widget={apDexTracesWidget}
|
||||
onDragSelect={onDragSelect}
|
||||
onClickHandler={handleGraphClick('ApDex')}
|
||||
yAxisUnit=""
|
||||
threshold={thresholdValue}
|
||||
isQueryEnabled={isQueryEnabled}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default ApDexTraces;
|
||||
@@ -0,0 +1 @@
|
||||
export const metricMeta = 'signoz_latency_bucket';
|
||||
@@ -0,0 +1,47 @@
|
||||
import Spinner from 'components/Spinner';
|
||||
import { Card, GraphContainer } from 'container/MetricsApplication/styles';
|
||||
import { useGetApDexSettings } from 'hooks/apDex/useGetApDexSettings';
|
||||
import useErrorNotification from 'hooks/useErrorNotification';
|
||||
import { memo } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { IServiceName } from '../../types';
|
||||
import ApDexMetricsApplication from './ApDexMetricsApplication';
|
||||
import { ApDexApplicationProps } from './types';
|
||||
|
||||
function ApDexApplication({
|
||||
handleGraphClick,
|
||||
onDragSelect,
|
||||
topLevelOperationsRoute,
|
||||
tagFilterItems,
|
||||
}: ApDexApplicationProps): JSX.Element {
|
||||
const { servicename } = useParams<IServiceName>();
|
||||
const { data, isLoading, error, isRefetching } = useGetApDexSettings(
|
||||
servicename,
|
||||
);
|
||||
useErrorNotification(error);
|
||||
|
||||
if (isLoading || isRefetching) {
|
||||
return (
|
||||
<Card>
|
||||
<Spinner height="40vh" tip="Loading..." />
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<GraphContainer>
|
||||
<ApDexMetricsApplication
|
||||
handleGraphClick={handleGraphClick}
|
||||
onDragSelect={onDragSelect}
|
||||
topLevelOperationsRoute={topLevelOperationsRoute}
|
||||
tagFilterItems={tagFilterItems}
|
||||
thresholdValue={data?.data[0].threshold}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(ApDexApplication);
|
||||
@@ -0,0 +1,19 @@
|
||||
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import { ClickHandlerType } from '../../Overview';
|
||||
|
||||
export interface ApDexApplicationProps {
|
||||
handleGraphClick: (type: string) => ClickHandlerType;
|
||||
onDragSelect: (start: number, end: number) => void;
|
||||
topLevelOperationsRoute: string[];
|
||||
tagFilterItems: TagFilterItem[];
|
||||
}
|
||||
|
||||
export interface ApDexDataSwitcherProps extends ApDexApplicationProps {
|
||||
thresholdValue?: number;
|
||||
}
|
||||
|
||||
export interface ApDexMetricsProps extends ApDexDataSwitcherProps {
|
||||
delta?: boolean;
|
||||
metricsBuckets?: number[];
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import Spinner from 'components/Spinner';
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import Graph from 'container/GridGraphLayout/Graph/';
|
||||
@@ -6,30 +7,41 @@ import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsAppli
|
||||
import { latency } from 'container/MetricsApplication/MetricsPageQueries/OverviewQueries';
|
||||
import { Card, GraphContainer } from 'container/MetricsApplication/styles';
|
||||
import useFeatureFlag from 'hooks/useFeatureFlag';
|
||||
import useResourceAttribute from 'hooks/useResourceAttribute';
|
||||
import { resourceAttributesToTagFilterItems } from 'hooks/useResourceAttribute/utils';
|
||||
import { useMemo } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { ClickHandlerType } from '../Overview';
|
||||
import { Button } from '../styles';
|
||||
import { IServiceName } from '../types';
|
||||
import { onViewTracePopupClick } from '../util';
|
||||
import { handleNonInQueryRange, onViewTracePopupClick } from '../util';
|
||||
|
||||
function ServiceOverview({
|
||||
onDragSelect,
|
||||
handleGraphClick,
|
||||
selectedTraceTags,
|
||||
selectedTimeStamp,
|
||||
tagFilterItems,
|
||||
topLevelOperationsRoute,
|
||||
topLevelOperationsLoading,
|
||||
}: ServiceOverviewProps): JSX.Element {
|
||||
const { servicename } = useParams<IServiceName>();
|
||||
|
||||
const isSpanMetricEnable = useFeatureFlag(FeatureKeys.USE_SPAN_METRICS)
|
||||
?.active;
|
||||
|
||||
const { queries } = useResourceAttribute();
|
||||
|
||||
const tagFilterItems = useMemo(
|
||||
() =>
|
||||
handleNonInQueryRange(
|
||||
resourceAttributesToTagFilterItems(queries, !isSpanMetricEnable),
|
||||
) || [],
|
||||
[isSpanMetricEnable, queries],
|
||||
);
|
||||
|
||||
const latencyWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder({
|
||||
@@ -48,11 +60,19 @@ function ServiceOverview({
|
||||
title: GraphTitle.LATENCY,
|
||||
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||
}),
|
||||
[servicename, tagFilterItems, isSpanMetricEnable, topLevelOperationsRoute],
|
||||
[servicename, isSpanMetricEnable, topLevelOperationsRoute, tagFilterItems],
|
||||
);
|
||||
|
||||
const isQueryEnabled = topLevelOperationsRoute.length > 0;
|
||||
|
||||
if (topLevelOperationsLoading) {
|
||||
return (
|
||||
<Card>
|
||||
<Spinner height="40vh" tip="Loading..." />
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
@@ -75,9 +95,6 @@ function ServiceOverview({
|
||||
widget={latencyWidget}
|
||||
yAxisUnit="ns"
|
||||
onClickHandler={handleGraphClick('Service')}
|
||||
allowClone={false}
|
||||
allowDelete={false}
|
||||
allowEdit={false}
|
||||
isQueryEnabled={isQueryEnabled}
|
||||
/>
|
||||
</GraphContainer>
|
||||
@@ -91,8 +108,8 @@ interface ServiceOverviewProps {
|
||||
selectedTraceTags: string;
|
||||
onDragSelect: (start: number, end: number) => void;
|
||||
handleGraphClick: (type: string) => ClickHandlerType;
|
||||
tagFilterItems: TagFilterItem[];
|
||||
topLevelOperationsRoute: string[];
|
||||
topLevelOperationsLoading: boolean;
|
||||
}
|
||||
|
||||
export default ServiceOverview;
|
||||
|
||||
@@ -39,9 +39,6 @@ function TopLevelOperation({
|
||||
onClickHandler={handleGraphClick(opName)}
|
||||
yAxisUnit={yAxisUnit}
|
||||
onDragSelect={onDragSelect}
|
||||
allowClone={false}
|
||||
allowDelete={false}
|
||||
allowEdit={false}
|
||||
/>
|
||||
)}
|
||||
</GraphContainer>
|
||||
|
||||
@@ -19,6 +19,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { IServiceName } from '../types';
|
||||
import { title } from './config';
|
||||
import ColumnWithLink from './TableRenderer/ColumnWithLink';
|
||||
import { getTableColumnRenderer } from './TableRenderer/TableColumnRenderer';
|
||||
|
||||
@@ -108,6 +109,7 @@ function TopOperationMetrics(): JSX.Element {
|
||||
|
||||
return (
|
||||
<QueryTable
|
||||
title={title}
|
||||
query={updatedQuery}
|
||||
queryTableData={queryTableData}
|
||||
loading={isLoading}
|
||||
|
||||
@@ -56,7 +56,19 @@ export interface LatencyProps {
|
||||
topLevelOperationsRoute: string[];
|
||||
}
|
||||
|
||||
export interface ApDexProps {
|
||||
servicename: IServiceName['servicename'];
|
||||
tagFilterItems: TagFilterItem[];
|
||||
topLevelOperationsRoute: string[];
|
||||
threashold: number;
|
||||
}
|
||||
|
||||
export interface TableRendererProps {
|
||||
columnName: string;
|
||||
renderFunction: (record: RowData) => ReactNode;
|
||||
}
|
||||
|
||||
export interface ApDexMetricsQueryBuilderQueriesProps extends ApDexProps {
|
||||
delta: boolean;
|
||||
metricsBuckets: number[];
|
||||
}
|
||||
|
||||
@@ -14,9 +14,13 @@ export const OPERATION_LEGENDS = ['Operations'];
|
||||
export enum FORMULA {
|
||||
ERROR_PERCENTAGE = 'A*100/B',
|
||||
DATABASE_CALLS_AVG_DURATION = 'A/B',
|
||||
APDEX_TRACES = '((B + C)/2)/A',
|
||||
APDEX_DELTA_SPAN_METRICS = '(B + C/2)/A',
|
||||
APDEX_CUMULATIVE_SPAN_METRICS = '((B + C)/2)/A',
|
||||
}
|
||||
|
||||
export enum GraphTitle {
|
||||
APDEX = 'Apdex',
|
||||
LATENCY = 'Latency',
|
||||
RATE_PER_OPS = 'Rate (ops/s)',
|
||||
ERROR_PERCENTAGE = 'Error Percentage',
|
||||
@@ -41,6 +45,7 @@ export enum DataType {
|
||||
STRING = 'string',
|
||||
FLOAT64 = 'float64',
|
||||
INT64 = 'int64',
|
||||
BOOL = 'bool',
|
||||
}
|
||||
|
||||
export enum MetricsType {
|
||||
@@ -49,7 +54,9 @@ export enum MetricsType {
|
||||
}
|
||||
|
||||
export enum WidgetKeys {
|
||||
Le = 'le',
|
||||
Name = 'name',
|
||||
HasError = 'hasError',
|
||||
Address = 'address',
|
||||
DurationNano = 'durationNano',
|
||||
StatusCode = 'status_code',
|
||||
|
||||
@@ -29,6 +29,14 @@ export const Col = styled(ColComponent)`
|
||||
}
|
||||
`;
|
||||
|
||||
export const ColApDexContainer = styled(ColComponent)`
|
||||
padding: 0 !important;
|
||||
`;
|
||||
|
||||
export const ColErrorContainer = styled(ColComponent)`
|
||||
padding: 2rem 0 !important;
|
||||
`;
|
||||
|
||||
export const GraphContainer = styled.div`
|
||||
height: 40vh;
|
||||
`;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
@@ -5,7 +6,7 @@ import { IServiceName } from './Tabs/types';
|
||||
|
||||
export interface GetWidgetQueryBuilderProps {
|
||||
query: Widgets['query'];
|
||||
title?: string;
|
||||
title?: ReactNode;
|
||||
panelTypes: Widgets['panelTypes'];
|
||||
}
|
||||
|
||||
|
||||
@@ -24,3 +24,14 @@ export const navigateToTrace = ({
|
||||
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"],"operation":["${operation}"]}&filterToFetchData=["duration","status","serviceName","operation"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&&isFilterExclude={"serviceName":false,"operation":false}&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"],"operation":["${operation}"]}&spanAggregateCurrentPage=1`,
|
||||
);
|
||||
};
|
||||
|
||||
export const getNearestHighestBucketValue = (
|
||||
value: number,
|
||||
buckets: number[],
|
||||
): string => {
|
||||
const nearestBucket = buckets.find((bucket) => bucket >= value);
|
||||
return nearestBucket?.toString() || '+Inf';
|
||||
};
|
||||
|
||||
export const convertMilSecToNanoSec = (value: number): number =>
|
||||
value * 1000000000;
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import { DefaultOptionType } from 'antd/es/select';
|
||||
|
||||
import {
|
||||
BooleanFormats,
|
||||
Category,
|
||||
CategoryNames,
|
||||
DataFormats,
|
||||
DataRateFormats,
|
||||
MiscellaneousFormats,
|
||||
ThroughputFormats,
|
||||
TimeFormats,
|
||||
} from './types';
|
||||
|
||||
export const alertsCategory = [
|
||||
{
|
||||
name: CategoryNames.Time,
|
||||
formats: [
|
||||
{ name: 'nanoseconds (ns)', id: TimeFormats.Nanoseconds },
|
||||
{ name: 'microseconds (µs)', id: TimeFormats.Microseconds },
|
||||
{ name: 'milliseconds (ms)', id: TimeFormats.Milliseconds },
|
||||
{ name: 'seconds (s)', id: TimeFormats.Seconds },
|
||||
{ name: 'minutes (m)', id: TimeFormats.Minutes },
|
||||
{ name: 'hours (h)', id: TimeFormats.Hours },
|
||||
{ name: 'days (d)', id: TimeFormats.Days },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: CategoryNames.Data,
|
||||
formats: [
|
||||
{ name: 'bytes(IEC)', id: DataFormats.BytesIEC },
|
||||
{ name: 'bytes(SI)', id: DataFormats.BytesSI },
|
||||
{ name: 'bits(IEC)', id: DataFormats.BitsIEC },
|
||||
{ name: 'bits(SI)', id: DataFormats.BitsSI },
|
||||
{ name: 'kibibytes', id: DataFormats.KibiBytes },
|
||||
{ name: 'kilobytes', id: DataFormats.KiloBytes },
|
||||
{ name: 'mebibytes', id: DataFormats.MebiBytes },
|
||||
{ name: 'megabytes', id: DataFormats.MegaBytes },
|
||||
{ name: 'gibibytes', id: DataFormats.GibiBytes },
|
||||
{ name: 'gigabytes', id: DataFormats.GigaBytes },
|
||||
{ name: 'tebibytes', id: DataFormats.TebiBytes },
|
||||
{ name: 'terabytes', id: DataFormats.TeraBytes },
|
||||
{ name: 'pebibytes', id: DataFormats.PebiBytes },
|
||||
{ name: 'petabytes', id: DataFormats.PetaBytes },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: CategoryNames.DataRate,
|
||||
formats: [
|
||||
{ name: 'bytes/sec(IEC)', id: DataRateFormats.BytesPerSecIEC },
|
||||
{ name: 'bytes/sec(SI)', id: DataRateFormats.BytesPerSecSI },
|
||||
{ name: 'bits/sec(IEC)', id: DataRateFormats.BitsPerSecIEC },
|
||||
{ name: 'bits/sec(SI)', id: DataRateFormats.BitsPerSecSI },
|
||||
{ name: 'kibibytes/sec', id: DataRateFormats.KibiBytesPerSec },
|
||||
{ name: 'kibibits/sec', id: DataRateFormats.KibiBitsPerSec },
|
||||
{ name: 'kilobytes/sec', id: DataRateFormats.KiloBytesPerSec },
|
||||
{ name: 'kilobits/sec', id: DataRateFormats.KiloBitsPerSec },
|
||||
{ name: 'mebibytes/sec', id: DataRateFormats.MebiBytesPerSec },
|
||||
{ name: 'mebibits/sec', id: DataRateFormats.MebiBitsPerSec },
|
||||
{ name: 'megabytes/sec', id: DataRateFormats.MegaBytesPerSec },
|
||||
{ name: 'megabits/sec', id: DataRateFormats.MegaBitsPerSec },
|
||||
{ name: 'gibibytes/sec', id: DataRateFormats.GibiBytesPerSec },
|
||||
{ name: 'gibibits/sec', id: DataRateFormats.GibiBitsPerSec },
|
||||
{ name: 'gigabytes/sec', id: DataRateFormats.GigaBytesPerSec },
|
||||
{ name: 'gigabits/sec', id: DataRateFormats.GigaBitsPerSec },
|
||||
{ name: 'tebibytes/sec', id: DataRateFormats.TebiBytesPerSec },
|
||||
{ name: 'tebibits/sec', id: DataRateFormats.TebiBitsPerSec },
|
||||
{ name: 'terabytes/sec', id: DataRateFormats.TeraBytesPerSec },
|
||||
{ name: 'terabits/sec', id: DataRateFormats.TeraBitsPerSec },
|
||||
{ name: 'pebibytes/sec', id: DataRateFormats.PebiBytesPerSec },
|
||||
{ name: 'pebibits/sec', id: DataRateFormats.PebiBitsPerSec },
|
||||
{ name: 'petabytes/sec', id: DataRateFormats.PetaBytesPerSec },
|
||||
{ name: 'petabits/sec', id: DataRateFormats.PetaBitsPerSec },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: CategoryNames.Miscellaneous,
|
||||
formats: [
|
||||
{ name: 'Percent (0.0-1.0)', id: MiscellaneousFormats.PercentUnit },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: CategoryNames.Boolean,
|
||||
formats: [
|
||||
{ name: 'True / False', id: BooleanFormats.TRUE_FALSE },
|
||||
{ name: 'Yes / No', id: BooleanFormats.YES_NO },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: CategoryNames.Throughput,
|
||||
formats: [
|
||||
{ name: 'counts/sec (cps)', id: ThroughputFormats.CountsPerSec },
|
||||
{ name: 'ops/sec (ops)', id: ThroughputFormats.OpsPerSec },
|
||||
{ name: 'requests/sec (reqps)', id: ThroughputFormats.RequestsPerSec },
|
||||
{ name: 'reads/sec (rps)', id: ThroughputFormats.ReadsPerSec },
|
||||
{ name: 'writes/sec (wps)', id: ThroughputFormats.WritesPerSec },
|
||||
{ name: 'I/O operations/sec (iops)', id: ThroughputFormats.IOOpsPerSec },
|
||||
{ name: 'counts/min (cpm)', id: ThroughputFormats.CountsPerMin },
|
||||
{ name: 'ops/min (opm)', id: ThroughputFormats.OpsPerMin },
|
||||
{ name: 'reads/min (rpm)', id: ThroughputFormats.ReadsPerMin },
|
||||
{ name: 'writes/min (wpm)', id: ThroughputFormats.WritesPerMin },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const getCategorySelectOptionByName = (
|
||||
name?: CategoryNames | string,
|
||||
): DefaultOptionType[] =>
|
||||
alertsCategory
|
||||
.find((category) => category.name === name)
|
||||
?.formats.map((format) => ({
|
||||
label: format.name,
|
||||
value: format.id,
|
||||
})) || [];
|
||||
|
||||
export const getCategoryByOptionId = (id: string): Category | undefined =>
|
||||
alertsCategory.find((category) =>
|
||||
category.formats.some((format) => format.id === id),
|
||||
);
|
||||
|
||||
export const isCategoryName = (name: string): name is CategoryNames =>
|
||||
alertsCategory.some((category) => category.name === name);
|
||||
@@ -1,383 +1,436 @@
|
||||
import { flattenDeep } from 'lodash-es';
|
||||
|
||||
export const dataTypeCategories = [
|
||||
{
|
||||
name: 'Time',
|
||||
formats: [
|
||||
{ name: 'Hertz (1/s)', id: 'hertz' },
|
||||
{ name: 'nanoseconds (ns)', id: 'ns' },
|
||||
{ name: 'microseconds (µs)', id: 'µs' },
|
||||
{ name: 'milliseconds (ms)', id: 'ms' },
|
||||
{ name: 'seconds (s)', id: 's' },
|
||||
{ name: 'minutes (m)', id: 'm' },
|
||||
{ name: 'hours (h)', id: 'h' },
|
||||
{ name: 'days (d)', id: 'd' },
|
||||
{ name: 'duration (ms)', id: 'dtdurationms' },
|
||||
{ name: 'duration (s)', id: 'dtdurations' },
|
||||
{ name: 'duration (hh:mm:ss)', id: 'dthms' },
|
||||
{ name: 'duration (d hh:mm:ss)', id: 'dtdhms' },
|
||||
{ name: 'Timeticks (s/100)', id: 'timeticks' },
|
||||
{ name: 'clock (ms)', id: 'clockms' },
|
||||
{ name: 'clock (s)', id: 'clocks' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Throughput',
|
||||
formats: [
|
||||
{ name: 'counts/sec (cps)', id: 'cps' },
|
||||
{ name: 'ops/sec (ops)', id: 'ops' },
|
||||
{ name: 'requests/sec (rps)', id: 'reqps' },
|
||||
{ name: 'reads/sec (rps)', id: 'rps' },
|
||||
{ name: 'writes/sec (wps)', id: 'wps' },
|
||||
{ name: 'I/O ops/sec (iops)', id: 'iops' },
|
||||
{ name: 'counts/min (cpm)', id: 'cpm' },
|
||||
{ name: 'ops/min (opm)', id: 'opm' },
|
||||
{ name: 'reads/min (rpm)', id: 'rpm' },
|
||||
{ name: 'writes/min (wpm)', id: 'wpm' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Data',
|
||||
formats: [
|
||||
{ name: 'bytes(IEC)', id: 'bytes' },
|
||||
{ name: 'bytes(SI)', id: 'decbytes' },
|
||||
{ name: 'bits(IEC)', id: 'bits' },
|
||||
{ name: 'bits(SI)', id: 'decbits' },
|
||||
{ name: 'kibibytes', id: 'kbytes' },
|
||||
{ name: 'kilobytes', id: 'deckbytes' },
|
||||
{ name: 'mebibytes', id: 'mbytes' },
|
||||
{ name: 'megabytes', id: 'decmbytes' },
|
||||
{ name: 'gibibytes', id: 'gbytes' },
|
||||
{ name: 'gigabytes', id: 'decgbytes' },
|
||||
{ name: 'tebibytes', id: 'tbytes' },
|
||||
{ name: 'terabytes', id: 'dectbytes' },
|
||||
{ name: 'pebibytes', id: 'pbytes' },
|
||||
{ name: 'petabytes', id: 'decpbytes' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Data rate',
|
||||
formats: [
|
||||
{ name: 'packets/sec', id: 'pps' },
|
||||
{ name: 'bytes/sec(IEC)', id: 'binBps' },
|
||||
{ name: 'bytes/sec(SI)', id: 'Bps' },
|
||||
{ name: 'bits/sec(IEC)', id: 'binbps' },
|
||||
{ name: 'bits/sec(SI)', id: 'bps' },
|
||||
{ name: 'kibibytes/sec', id: 'KiBs' },
|
||||
{ name: 'kibibits/sec', id: 'Kibits' },
|
||||
{ name: 'kilobytes/sec', id: 'KBs' },
|
||||
{ name: 'kilobits/sec', id: 'Kbits' },
|
||||
{ name: 'mebibytes/sec', id: 'MiBs' },
|
||||
{ name: 'mebibits/sec', id: 'Mibits' },
|
||||
{ name: 'megabytes/sec', id: 'MBs' },
|
||||
{ name: 'megabits/sec', id: 'Mbits' },
|
||||
{ name: 'gibibytes/sec', id: 'GiBs' },
|
||||
{ name: 'gibibits/sec', id: 'Gibits' },
|
||||
{ name: 'gigabytes/sec', id: 'GBs' },
|
||||
{ name: 'gigabits/sec', id: 'Gbits' },
|
||||
{ name: 'tebibytes/sec', id: 'TiBs' },
|
||||
{ name: 'tebibits/sec', id: 'Tibits' },
|
||||
{ name: 'terabytes/sec', id: 'TBs' },
|
||||
{ name: 'terabits/sec', id: 'Tbits' },
|
||||
{ name: 'pebibytes/sec', id: 'PiBs' },
|
||||
{ name: 'pebibits/sec', id: 'Pibits' },
|
||||
{ name: 'petabytes/sec', id: 'PBs' },
|
||||
{ name: 'petabits/sec', id: 'Pbits' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Hash rate',
|
||||
formats: [
|
||||
{ name: 'hashes/sec', id: 'Hs' },
|
||||
{ name: 'kilohashes/sec', id: 'KHs' },
|
||||
{ name: 'megahashes/sec', id: 'MHs' },
|
||||
{ name: 'gigahashes/sec', id: 'GHs' },
|
||||
{ name: 'terahashes/sec', id: 'THs' },
|
||||
{ name: 'petahashes/sec', id: 'PHs' },
|
||||
{ name: 'exahashes/sec', id: 'EHs' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Misc',
|
||||
formats: [
|
||||
{ name: 'none', id: 'none' },
|
||||
{ name: 'String', id: 'string' },
|
||||
{ name: 'short', id: 'short' },
|
||||
{ name: 'Percent (0-100)', id: 'percent' },
|
||||
{ name: 'Percent (0.0-1.0)', id: 'percentunit' },
|
||||
{ name: 'Humidity (%H)', id: 'humidity' },
|
||||
{ name: 'Decibel', id: 'dB' },
|
||||
{ name: 'Hexadecimal (0x)', id: 'hex0x' },
|
||||
{ name: 'Hexadecimal', id: 'hex' },
|
||||
{ name: 'Scientific notation', id: 'sci' },
|
||||
{ name: 'Locale format', id: 'locale' },
|
||||
{ name: 'Pixels', id: 'pixel' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Acceleration',
|
||||
formats: [
|
||||
{ name: 'Meters/sec²', id: 'accMS2' },
|
||||
{ name: 'Feet/sec²', id: 'accFS2' },
|
||||
{ name: 'G unit', id: 'accG' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Angle',
|
||||
formats: [
|
||||
{ name: 'Degrees (°)', id: 'degree' },
|
||||
{ name: 'Radians', id: 'radian' },
|
||||
{ name: 'Gradian', id: 'grad' },
|
||||
{ name: 'Arc Minutes', id: 'arcmin' },
|
||||
{ name: 'Arc Seconds', id: 'arcsec' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Area',
|
||||
formats: [
|
||||
{ name: 'Square Meters (m²)', id: 'areaM2' },
|
||||
{ name: 'Square Feet (ft²)', id: 'areaF2' },
|
||||
{ name: 'Square Miles (mi²)', id: 'areaMI2' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Computation',
|
||||
formats: [
|
||||
{ name: 'FLOP/s', id: 'flops' },
|
||||
{ name: 'MFLOP/s', id: 'mflops' },
|
||||
{ name: 'GFLOP/s', id: 'gflops' },
|
||||
{ name: 'TFLOP/s', id: 'tflops' },
|
||||
{ name: 'PFLOP/s', id: 'pflops' },
|
||||
{ name: 'EFLOP/s', id: 'eflops' },
|
||||
{ name: 'ZFLOP/s', id: 'zflops' },
|
||||
{ name: 'YFLOP/s', id: 'yflops' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Concentration',
|
||||
formats: [
|
||||
{ name: 'parts-per-million (ppm)', id: 'ppm' },
|
||||
{ name: 'parts-per-billion (ppb)', id: 'conppb' },
|
||||
{ name: 'nanogram per cubic meter (ng/m³)', id: 'conngm3' },
|
||||
{ name: 'nanogram per normal cubic meter (ng/Nm³)', id: 'conngNm3' },
|
||||
{ name: 'microgram per cubic meter (μg/m³)', id: 'conμgm3' },
|
||||
{ name: 'microgram per normal cubic meter (μg/Nm³)', id: 'conμgNm3' },
|
||||
{ name: 'milligram per cubic meter (mg/m³)', id: 'conmgm3' },
|
||||
{ name: 'milligram per normal cubic meter (mg/Nm³)', id: 'conmgNm3' },
|
||||
{ name: 'gram per cubic meter (g/m³)', id: 'congm3' },
|
||||
{ name: 'gram per normal cubic meter (g/Nm³)', id: 'congNm3' },
|
||||
{ name: 'milligrams per decilitre (mg/dL)', id: 'conmgdL' },
|
||||
{ name: 'millimoles per litre (mmol/L)', id: 'conmmolL' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Currency',
|
||||
formats: [
|
||||
{ name: 'Dollars ($)', id: 'currencyUSD' },
|
||||
{ name: 'Pounds (£)', id: 'currencyGBP' },
|
||||
{ name: 'Euro (€)', id: 'currencyEUR' },
|
||||
{ name: 'Yen (¥)', id: 'currencyJPY' },
|
||||
{ name: 'Rubles (₽)', id: 'currencyRUB' },
|
||||
{ name: 'Hryvnias (₴)', id: 'currencyUAH' },
|
||||
{ name: 'Real (R$)', id: 'currencyBRL' },
|
||||
{ name: 'Danish Krone (kr)', id: 'currencyDKK' },
|
||||
{ name: 'Icelandic Króna (kr)', id: 'currencyISK' },
|
||||
{ name: 'Norwegian Krone (kr)', id: 'currencyNOK' },
|
||||
{ name: 'Swedish Krona (kr)', id: 'currencySEK' },
|
||||
{ name: 'Czech koruna (czk)', id: 'currencyCZK' },
|
||||
{ name: 'Swiss franc (CHF)', id: 'currencyCHF' },
|
||||
{ name: 'Polish Złoty (PLN)', id: 'currencyPLN' },
|
||||
{ name: 'Bitcoin (฿)', id: 'currencyBTC' },
|
||||
{ name: 'Milli Bitcoin (฿)', id: 'currencymBTC' },
|
||||
{ name: 'Micro Bitcoin (฿)', id: 'currencyμBTC' },
|
||||
{ name: 'South African Rand (R)', id: 'currencyZAR' },
|
||||
{ name: 'Indian Rupee (₹)', id: 'currencyINR' },
|
||||
{ name: 'South Korean Won (₩)', id: 'currencyKRW' },
|
||||
{ name: 'Indonesian Rupiah (Rp)', id: 'currencyIDR' },
|
||||
{ name: 'Philippine Peso (PHP)', id: 'currencyPHP' },
|
||||
{ name: 'Vietnamese Dong (VND)', id: 'currencyVND' },
|
||||
],
|
||||
},
|
||||
import {
|
||||
AccelerationFormats,
|
||||
AngularFormats,
|
||||
AreaFormats,
|
||||
BooleanFormats,
|
||||
CategoryNames,
|
||||
ConcentrationFormats,
|
||||
CurrencyFormats,
|
||||
DataFormats,
|
||||
DataRateFormats,
|
||||
DataTypeCategories,
|
||||
DatetimeFormats,
|
||||
FlopsFormats,
|
||||
FlowFormats,
|
||||
ForceFormats,
|
||||
HashRateFormats,
|
||||
LengthFormats,
|
||||
MassFormats,
|
||||
MiscellaneousFormats,
|
||||
PowerElectricalFormats,
|
||||
PressureFormats,
|
||||
RadiationFormats,
|
||||
RotationSpeedFormats,
|
||||
TemperatureFormats,
|
||||
ThroughputFormats,
|
||||
TimeFormats,
|
||||
VelocityFormats,
|
||||
VolumeFormats,
|
||||
} from './types';
|
||||
|
||||
export const dataTypeCategories: DataTypeCategories = [
|
||||
{
|
||||
name: 'Date & time',
|
||||
name: CategoryNames.Time,
|
||||
formats: [
|
||||
{ name: 'Datetime ISO', id: 'dateTimeAsIso' },
|
||||
{ name: 'Hertz (1/s)', id: TimeFormats.Hertz },
|
||||
{ name: 'nanoseconds (ns)', id: TimeFormats.Nanoseconds },
|
||||
{ name: 'microseconds (µs)', id: TimeFormats.Microseconds },
|
||||
{ name: 'milliseconds (ms)', id: TimeFormats.Milliseconds },
|
||||
{ name: 'seconds (s)', id: TimeFormats.Seconds },
|
||||
{ name: 'minutes (m)', id: TimeFormats.Minutes },
|
||||
{ name: 'hours (h)', id: TimeFormats.Hours },
|
||||
{ name: 'days (d)', id: TimeFormats.Days },
|
||||
{ name: 'duration in ms (dtdurationms)', id: TimeFormats.DurationMs },
|
||||
{ name: 'duration in s (dtdurations)', id: TimeFormats.DurationS },
|
||||
{ name: 'duration in h:m:s (dthms)', id: TimeFormats.DurationHms },
|
||||
{ name: 'duration in d:h:m:s (dtdhms)', id: TimeFormats.DurationDhms },
|
||||
{ name: 'timeticks (timeticks)', id: TimeFormats.Timeticks },
|
||||
{ name: 'clock in ms (clockms)', id: TimeFormats.ClockMs },
|
||||
{ name: 'clock in s (clocks)', id: TimeFormats.ClockS },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: CategoryNames.Throughput,
|
||||
formats: [
|
||||
{ name: 'counts/sec (cps)', id: ThroughputFormats.CountsPerSec },
|
||||
{ name: 'ops/sec (ops)', id: ThroughputFormats.OpsPerSec },
|
||||
{ name: 'requests/sec (reqps)', id: ThroughputFormats.RequestsPerSec },
|
||||
{ name: 'reads/sec (rps)', id: ThroughputFormats.ReadsPerSec },
|
||||
{ name: 'writes/sec (wps)', id: ThroughputFormats.WritesPerSec },
|
||||
{ name: 'I/O operations/sec (iops)', id: ThroughputFormats.IOOpsPerSec },
|
||||
{ name: 'counts/min (cpm)', id: ThroughputFormats.CountsPerMin },
|
||||
{ name: 'ops/min (opm)', id: ThroughputFormats.OpsPerMin },
|
||||
{ name: 'reads/min (rpm)', id: ThroughputFormats.ReadsPerMin },
|
||||
{ name: 'writes/min (wpm)', id: ThroughputFormats.WritesPerMin },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: CategoryNames.Data,
|
||||
formats: [
|
||||
{ name: 'bytes(IEC)', id: DataFormats.BytesIEC },
|
||||
{ name: 'bytes(SI)', id: DataFormats.BytesSI },
|
||||
{ name: 'bits(IEC)', id: DataFormats.BitsIEC },
|
||||
{ name: 'bits(SI)', id: DataFormats.BitsSI },
|
||||
{ name: 'kibibytes', id: DataFormats.KibiBytes },
|
||||
{ name: 'kilobytes', id: DataFormats.KiloBytes },
|
||||
{ name: 'mebibytes', id: DataFormats.MebiBytes },
|
||||
{ name: 'megabytes', id: DataFormats.MegaBytes },
|
||||
{ name: 'gibibytes', id: DataFormats.GibiBytes },
|
||||
{ name: 'gigabytes', id: DataFormats.GigaBytes },
|
||||
{ name: 'tebibytes', id: DataFormats.TebiBytes },
|
||||
{ name: 'terabytes', id: DataFormats.TeraBytes },
|
||||
{ name: 'pebibytes', id: DataFormats.PebiBytes },
|
||||
{ name: 'petabytes', id: DataFormats.PetaBytes },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: CategoryNames.DataRate,
|
||||
formats: [
|
||||
{ name: 'packets/sec', id: DataRateFormats.PacketsPerSec },
|
||||
{ name: 'bytes/sec(IEC)', id: DataRateFormats.BytesPerSecIEC },
|
||||
{ name: 'bytes/sec(SI)', id: DataRateFormats.BytesPerSecSI },
|
||||
{ name: 'bits/sec(IEC)', id: DataRateFormats.BitsPerSecIEC },
|
||||
{ name: 'bits/sec(SI)', id: DataRateFormats.BitsPerSecSI },
|
||||
{ name: 'kibibytes/sec', id: DataRateFormats.KibiBytesPerSec },
|
||||
{ name: 'kibibits/sec', id: DataRateFormats.KibiBitsPerSec },
|
||||
{ name: 'kilobytes/sec', id: DataRateFormats.KiloBytesPerSec },
|
||||
{ name: 'kilobits/sec', id: DataRateFormats.KiloBitsPerSec },
|
||||
{ name: 'mebibytes/sec', id: DataRateFormats.MebiBytesPerSec },
|
||||
{ name: 'mebibits/sec', id: DataRateFormats.MebiBitsPerSec },
|
||||
{ name: 'megabytes/sec', id: DataRateFormats.MegaBytesPerSec },
|
||||
{ name: 'megabits/sec', id: DataRateFormats.MegaBitsPerSec },
|
||||
{ name: 'gibibytes/sec', id: DataRateFormats.GibiBytesPerSec },
|
||||
{ name: 'gibibits/sec', id: DataRateFormats.GibiBitsPerSec },
|
||||
{ name: 'gigabytes/sec', id: DataRateFormats.GigaBytesPerSec },
|
||||
{ name: 'gigabits/sec', id: DataRateFormats.GigaBitsPerSec },
|
||||
{ name: 'tebibytes/sec', id: DataRateFormats.TebiBytesPerSec },
|
||||
{ name: 'tebibits/sec', id: DataRateFormats.TebiBitsPerSec },
|
||||
{ name: 'terabytes/sec', id: DataRateFormats.TeraBytesPerSec },
|
||||
{ name: 'terabits/sec', id: DataRateFormats.TeraBitsPerSec },
|
||||
{ name: 'pebibytes/sec', id: DataRateFormats.PebiBytesPerSec },
|
||||
{ name: 'pebibits/sec', id: DataRateFormats.PebiBitsPerSec },
|
||||
{ name: 'petabytes/sec', id: DataRateFormats.PetaBytesPerSec },
|
||||
{ name: 'petabits/sec', id: DataRateFormats.PetaBitsPerSec },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: CategoryNames.HashRate,
|
||||
formats: [
|
||||
{ name: 'hashes/sec', id: HashRateFormats.HashesPerSec },
|
||||
{ name: 'kilohashes/sec', id: HashRateFormats.KiloHashesPerSec },
|
||||
{ name: 'megahashes/sec', id: HashRateFormats.MegaHashesPerSec },
|
||||
{ name: 'gigahashes/sec', id: HashRateFormats.GigaHashesPerSec },
|
||||
{ name: 'terahashes/sec', id: HashRateFormats.TeraHashesPerSec },
|
||||
{ name: 'petahashes/sec', id: HashRateFormats.PetaHashesPerSec },
|
||||
{ name: 'exahashes/sec', id: HashRateFormats.ExaHashesPerSec },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: CategoryNames.Miscellaneous,
|
||||
formats: [
|
||||
{ name: 'none', id: MiscellaneousFormats.None },
|
||||
{ name: 'String', id: MiscellaneousFormats.String },
|
||||
{ name: 'short', id: MiscellaneousFormats.Short },
|
||||
{ name: 'Percent (0-100)', id: MiscellaneousFormats.Percent },
|
||||
{ name: 'Percent (0.0-1.0)', id: MiscellaneousFormats.PercentUnit },
|
||||
{ name: 'Humidity (%H)', id: MiscellaneousFormats.Humidity },
|
||||
{ name: 'Decibel', id: MiscellaneousFormats.Decibel },
|
||||
{ name: 'Hexadecimal (0x)', id: MiscellaneousFormats.Hexadecimal0x },
|
||||
{ name: 'Hexadecimal', id: MiscellaneousFormats.Hexadecimal },
|
||||
{ name: 'Scientific notation', id: MiscellaneousFormats.ScientificNotation },
|
||||
{ name: 'Locale format', id: MiscellaneousFormats.LocaleFormat },
|
||||
{ name: 'Pixels', id: MiscellaneousFormats.Pixels },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: CategoryNames.Acceleration,
|
||||
formats: [
|
||||
{ name: 'Meters/sec²', id: AccelerationFormats.MetersPerSecondSquared },
|
||||
{ name: 'Feet/sec²', id: AccelerationFormats.FeetPerSecondSquared },
|
||||
{ name: 'G unit', id: AccelerationFormats.GUnit },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: CategoryNames.Angle,
|
||||
formats: [
|
||||
{ name: 'Degrees (°)', id: AngularFormats.Degree },
|
||||
{ name: 'Radians', id: AngularFormats.Radian },
|
||||
{ name: 'Gradian', id: AngularFormats.Gradian },
|
||||
{ name: 'Arc Minutes', id: AngularFormats.ArcMinute },
|
||||
{ name: 'Arc Seconds', id: AngularFormats.ArcSecond },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: CategoryNames.Area,
|
||||
formats: [
|
||||
{ name: 'Square Meters (m²)', id: AreaFormats.SquareMeters },
|
||||
{ name: 'Square Feet (ft²)', id: AreaFormats.SquareFeet },
|
||||
{ name: 'Square Miles (mi²)', id: AreaFormats.SquareMiles },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: CategoryNames.Computation,
|
||||
formats: [
|
||||
{ name: 'FLOP/s', id: FlopsFormats.FLOPs },
|
||||
{ name: 'MFLOP/s', id: FlopsFormats.MFLOPs },
|
||||
{ name: 'GFLOP/s', id: FlopsFormats.GFLOPs },
|
||||
{ name: 'TFLOP/s', id: FlopsFormats.TFLOPs },
|
||||
{ name: 'PFLOP/s', id: FlopsFormats.PFLOPs },
|
||||
{ name: 'EFLOP/s', id: FlopsFormats.EFLOPs },
|
||||
{ name: 'ZFLOP/s', id: FlopsFormats.ZFLOPs },
|
||||
{ name: 'YFLOP/s', id: FlopsFormats.YFLOPs },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: CategoryNames.Concentration,
|
||||
formats: [
|
||||
{ name: 'parts-per-million (ppm)', id: ConcentrationFormats.PPM },
|
||||
{ name: 'parts-per-billion (ppb)', id: ConcentrationFormats.PPB },
|
||||
{ name: 'nanogram per cubic meter (ng/m³)', id: ConcentrationFormats.NgM3 },
|
||||
{
|
||||
name: 'nanogram per normal cubic meter (ng/Nm³)',
|
||||
id: ConcentrationFormats.NgNM3,
|
||||
},
|
||||
{ name: 'microgram per cubic meter (μg/m³)', id: ConcentrationFormats.UgM3 },
|
||||
{
|
||||
name: 'microgram per normal cubic meter (μg/Nm³)',
|
||||
id: ConcentrationFormats.UgNM3,
|
||||
},
|
||||
{ name: 'milligram per cubic meter (mg/m³)', id: ConcentrationFormats.MgM3 },
|
||||
{
|
||||
name: 'milligram per normal cubic meter (mg/Nm³)',
|
||||
id: ConcentrationFormats.MgNM3,
|
||||
},
|
||||
{ name: 'gram per cubic meter (g/m³)', id: ConcentrationFormats.GM3 },
|
||||
{
|
||||
name: 'gram per normal cubic meter (g/Nm³)',
|
||||
id: ConcentrationFormats.GNM3,
|
||||
},
|
||||
{ name: 'milligrams per decilitre (mg/dL)', id: ConcentrationFormats.MgDL },
|
||||
{ name: 'millimoles per litre (mmol/L)', id: ConcentrationFormats.MmolL },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: CategoryNames.Currency,
|
||||
formats: [
|
||||
{ name: 'Dollars ($)', id: CurrencyFormats.USD },
|
||||
{ name: 'Pounds (£)', id: CurrencyFormats.GBP },
|
||||
{ name: 'Euro (€)', id: CurrencyFormats.EUR },
|
||||
{ name: 'Yen (¥)', id: CurrencyFormats.JPY },
|
||||
{ name: 'Rubles (₽)', id: CurrencyFormats.RUB },
|
||||
{ name: 'Hryvnias (₴)', id: CurrencyFormats.UAH },
|
||||
{ name: 'Real (R$)', id: CurrencyFormats.BRL },
|
||||
{ name: 'Danish Krone (kr)', id: CurrencyFormats.DKK },
|
||||
{ name: 'Icelandic Króna (kr)', id: CurrencyFormats.ISK },
|
||||
{ name: 'Norwegian Krone (kr)', id: CurrencyFormats.NOK },
|
||||
{ name: 'Swedish Krona (kr)', id: CurrencyFormats.SEK },
|
||||
{ name: 'Czech koruna (czk)', id: CurrencyFormats.CZK },
|
||||
{ name: 'Swiss franc (CHF)', id: CurrencyFormats.CHF },
|
||||
{ name: 'Polish Złoty (PLN)', id: CurrencyFormats.PLN },
|
||||
{ name: 'Bitcoin (฿)', id: CurrencyFormats.BTC },
|
||||
{ name: 'Milli Bitcoin (฿)', id: CurrencyFormats.MBTC },
|
||||
{ name: 'Micro Bitcoin (฿)', id: CurrencyFormats.UBTC },
|
||||
{ name: 'South African Rand (R)', id: CurrencyFormats.ZAR },
|
||||
{ name: 'Indian Rupee (₹)', id: CurrencyFormats.INR },
|
||||
{ name: 'South Korean Won (₩)', id: CurrencyFormats.KRW },
|
||||
{ name: 'Indonesian Rupiah (Rp)', id: CurrencyFormats.IDR },
|
||||
{ name: 'Philippine Peso (PHP)', id: CurrencyFormats.PHP },
|
||||
{ name: 'Vietnamese Dong (VND)', id: CurrencyFormats.VND },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: CategoryNames.Datetime,
|
||||
formats: [
|
||||
{ name: 'Datetime ISO', id: DatetimeFormats.ISO },
|
||||
{
|
||||
name: 'Datetime ISO (No date if today)',
|
||||
id: 'dateTimeAsIsoNoDateIfToday',
|
||||
id: DatetimeFormats.ISONoDateIfToday,
|
||||
},
|
||||
{ name: 'Datetime US', id: 'dateTimeAsUS' },
|
||||
{ name: 'Datetime US (No date if today)', id: 'dateTimeAsUSNoDateIfToday' },
|
||||
{ name: 'Datetime local', id: 'dateTimeAsLocal' },
|
||||
{ name: 'Datetime US', id: DatetimeFormats.US },
|
||||
{
|
||||
name: 'Datetime US (No date if today)',
|
||||
id: DatetimeFormats.USNoDateIfToday,
|
||||
},
|
||||
{ name: 'Datetime local', id: DatetimeFormats.Local },
|
||||
{
|
||||
name: 'Datetime local (No date if today)',
|
||||
id: 'dateTimeAsLocalNoDateIfToday',
|
||||
id: DatetimeFormats.LocalNoDateIfToday,
|
||||
},
|
||||
{ name: 'Datetime default', id: 'dateTimeAsSystem' },
|
||||
{ name: 'From Now', id: 'dateTimeFromNow' },
|
||||
{ name: 'Datetime default', id: DatetimeFormats.System },
|
||||
{ name: 'From Now', id: DatetimeFormats.FromNow },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Energy',
|
||||
name: CategoryNames.Energy,
|
||||
formats: [
|
||||
{ name: 'Watt (W)', id: 'watt' },
|
||||
{ name: 'Kilowatt (kW)', id: 'kwatt' },
|
||||
{ name: 'Megawatt (MW)', id: 'megwatt' },
|
||||
{ name: 'Gigawatt (GW)', id: 'gwatt' },
|
||||
{ name: 'Milliwatt (mW)', id: 'mwatt' },
|
||||
{ name: 'Watt per square meter (W/m²)', id: 'Wm2' },
|
||||
{ name: 'Volt-Ampere (VA)', id: 'voltamp' },
|
||||
{ name: 'Kilovolt-Ampere (kVA)', id: 'kvoltamp' },
|
||||
{ name: 'Volt-Ampere reactive (VAr)', id: 'voltampreact' },
|
||||
{ name: 'Kilovolt-Ampere reactive (kVAr)', id: 'kvoltampreact' },
|
||||
{ name: 'Watt-hour (Wh)', id: 'watth' },
|
||||
{ name: 'Watt-hour per Kilogram (Wh/kg)', id: 'watthperkg' },
|
||||
{ name: 'Kilowatt-hour (kWh)', id: 'kwatth' },
|
||||
{ name: 'Kilowatt-min (kWm)', id: 'kwattm' },
|
||||
{ name: 'Ampere-hour (Ah)', id: 'amph' },
|
||||
{ name: 'Kiloampere-hour (kAh)', id: 'kamph' },
|
||||
{ name: 'Milliampere-hour (mAh)', id: 'mamph' },
|
||||
{ name: 'Joule (J)', id: 'joule' },
|
||||
{ name: 'Electron volt (eV)', id: 'ev' },
|
||||
{ name: 'Ampere (A)', id: 'amp' },
|
||||
{ name: 'Kiloampere (kA)', id: 'kamp' },
|
||||
{ name: 'Milliampere (mA)', id: 'mamp' },
|
||||
{ name: 'Volt (V)', id: 'volt' },
|
||||
{ name: 'Kilovolt (kV)', id: 'kvolt' },
|
||||
{ name: 'Millivolt (mV)', id: 'mvolt' },
|
||||
{ name: 'Decibel-milliwatt (dBm)', id: 'dBm' },
|
||||
{ name: 'Ohm (Ω)', id: 'ohm' },
|
||||
{ name: 'Kiloohm (kΩ)', id: 'kohm' },
|
||||
{ name: 'Megaohm (MΩ)', id: 'Mohm' },
|
||||
{ name: 'Farad (F)', id: 'farad' },
|
||||
{ name: 'Microfarad (µF)', id: 'µfarad' },
|
||||
{ name: 'Nanofarad (nF)', id: 'nfarad' },
|
||||
{ name: 'Picofarad (pF)', id: 'pfarad' },
|
||||
{ name: 'Femtofarad (fF)', id: 'ffarad' },
|
||||
{ name: 'Henry (H)', id: 'henry' },
|
||||
{ name: 'Millihenry (mH)', id: 'mhenry' },
|
||||
{ name: 'Microhenry (µH)', id: 'µhenry' },
|
||||
{ name: 'Lumens (Lm)', id: 'lumens' },
|
||||
{ name: 'Watt (W)', id: PowerElectricalFormats.WATT },
|
||||
{ name: 'Kilowatt (kW)', id: PowerElectricalFormats.KWATT },
|
||||
{ name: 'Megawatt (MW)', id: PowerElectricalFormats.MEGWATT },
|
||||
{ name: 'Gigawatt (GW)', id: PowerElectricalFormats.GWATT },
|
||||
{ name: 'Milliwatt (mW)', id: PowerElectricalFormats.MWATT },
|
||||
{ name: 'Watt per square meter (W/m²)', id: PowerElectricalFormats.WM2 },
|
||||
{ name: 'Volt-Ampere (VA)', id: PowerElectricalFormats.VOLTAMP },
|
||||
{ name: 'Kilovolt-Ampere (kVA)', id: PowerElectricalFormats.KVOLTAMP },
|
||||
{
|
||||
name: 'Volt-Ampere reactive (VAr)',
|
||||
id: PowerElectricalFormats.VOLTAMPREACT,
|
||||
},
|
||||
{
|
||||
name: 'Kilovolt-Ampere reactive (kVAr)',
|
||||
id: PowerElectricalFormats.KVOLTAMPREACT,
|
||||
},
|
||||
{ name: 'Watt-hour (Wh)', id: PowerElectricalFormats.WATTH },
|
||||
{
|
||||
name: 'Watt-hour per Kilogram (Wh/kg)',
|
||||
id: PowerElectricalFormats.WATTHPERKG,
|
||||
},
|
||||
{ name: 'Kilowatt-hour (kWh)', id: PowerElectricalFormats.KWATTH },
|
||||
{ name: 'Kilowatt-min (kWm)', id: PowerElectricalFormats.KWATTM },
|
||||
{ name: 'Ampere-hour (Ah)', id: PowerElectricalFormats.AMPH },
|
||||
{ name: 'Kiloampere-hour (kAh)', id: PowerElectricalFormats.KAMPH },
|
||||
{ name: 'Milliampere-hour (mAh)', id: PowerElectricalFormats.MAMPH },
|
||||
{ name: 'Joule (J)', id: PowerElectricalFormats.JOULE },
|
||||
{ name: 'Electron volt (eV)', id: PowerElectricalFormats.EV },
|
||||
{ name: 'Ampere (A)', id: PowerElectricalFormats.AMP },
|
||||
{ name: 'Kiloampere (kA)', id: PowerElectricalFormats.KAMP },
|
||||
{ name: 'Milliampere (mA)', id: PowerElectricalFormats.MAMP },
|
||||
{ name: 'Volt (V)', id: PowerElectricalFormats.VOLT },
|
||||
{ name: 'Kilovolt (kV)', id: PowerElectricalFormats.KVOLT },
|
||||
{ name: 'Millivolt (mV)', id: PowerElectricalFormats.MVOLT },
|
||||
{ name: 'Decibel-milliwatt (dBm)', id: PowerElectricalFormats.DBM },
|
||||
{ name: 'Ohm (Ω)', id: PowerElectricalFormats.OHM },
|
||||
{ name: 'Kiloohm (kΩ)', id: PowerElectricalFormats.KOHM },
|
||||
{ name: 'Megaohm (MΩ)', id: PowerElectricalFormats.MOHM },
|
||||
{ name: 'Farad (F)', id: PowerElectricalFormats.FARAD },
|
||||
{ name: 'Microfarad (µF)', id: PowerElectricalFormats.µFARAD },
|
||||
{ name: 'Nanofarad (nF)', id: PowerElectricalFormats.NFARAD },
|
||||
{ name: 'Picofarad (pF)', id: PowerElectricalFormats.PFARAD },
|
||||
{ name: 'Femtofarad (fF)', id: PowerElectricalFormats.FFARAD },
|
||||
{ name: 'Henry (H)', id: PowerElectricalFormats.HENRY },
|
||||
{ name: 'Millihenry (mH)', id: PowerElectricalFormats.MHENRY },
|
||||
{ name: 'Microhenry (µH)', id: PowerElectricalFormats.µHENRY },
|
||||
{ name: 'Lumens (Lm)', id: PowerElectricalFormats.LUMENS },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Flow',
|
||||
name: CategoryNames.Flow,
|
||||
formats: [
|
||||
{ name: 'Gallons/min (gpm)', id: 'flowgpm' },
|
||||
{ name: 'Cubic meters/sec (cms)', id: 'flowcms' },
|
||||
{ name: 'Cubic feet/sec (cfs)', id: 'flowcfs' },
|
||||
{ name: 'Cubic feet/min (cfm)', id: 'flowcfm' },
|
||||
{ name: 'Litre/hour', id: 'litreh' },
|
||||
{ name: 'Litre/min (L/min)', id: 'flowlpm' },
|
||||
{ name: 'milliLitre/min (mL/min)', id: 'flowmlpm' },
|
||||
{ name: 'Lux (lx)', id: 'lux' },
|
||||
{ name: 'Gallons/min (gpm)', id: FlowFormats.FLOWGPM },
|
||||
{ name: 'Cubic meters/sec (cms)', id: FlowFormats.FLOWCMS },
|
||||
{ name: 'Cubic feet/sec (cfs)', id: FlowFormats.FLOWCFS },
|
||||
{ name: 'Cubic feet/min (cfm)', id: FlowFormats.FLOWCFM },
|
||||
{ name: 'Litre/hour', id: FlowFormats.LITREH },
|
||||
{ name: 'Litre/min (L/min)', id: FlowFormats.FLOWLPM },
|
||||
{ name: 'milliLitre/min (mL/min)', id: FlowFormats.FLOWMLPM },
|
||||
{ name: 'Lux (lx)', id: FlowFormats.LUX },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Force',
|
||||
name: CategoryNames.Force,
|
||||
formats: [
|
||||
{ name: 'Newton-meters (Nm)', id: 'forceNm' },
|
||||
{ name: 'Kilonewton-meters (kNm)', id: 'forcekNm' },
|
||||
{ name: 'Newtons (N)', id: 'forceN' },
|
||||
{ name: 'Kilonewtons (kN)', id: 'forcekN' },
|
||||
{ name: 'Newton-meters (Nm)', id: ForceFormats.FORCENM },
|
||||
{ name: 'Kilonewton-meters (kNm)', id: ForceFormats.FORCEKNM },
|
||||
{ name: 'Newtons (N)', id: ForceFormats.FORCEN },
|
||||
{ name: 'Kilonewtons (kN)', id: ForceFormats.FORCEKN },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Mass',
|
||||
name: CategoryNames.Mass,
|
||||
formats: [
|
||||
{ name: 'milligram (mg)', id: 'massmg' },
|
||||
{ name: 'gram (g)', id: 'massg' },
|
||||
{ name: 'pound (lb)', id: 'masslb' },
|
||||
{ name: 'kilogram (kg)', id: 'masskg' },
|
||||
{ name: 'metric ton (t)', id: 'masst' },
|
||||
{ name: 'milligram (mg)', id: MassFormats.MASSMG },
|
||||
{ name: 'gram (g)', id: MassFormats.MASSG },
|
||||
{ name: 'pound (lb)', id: MassFormats.MASSLB },
|
||||
{ name: 'kilogram (kg)', id: MassFormats.MASSKG },
|
||||
{ name: 'metric ton (t)', id: MassFormats.MASST },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Length',
|
||||
name: CategoryNames.Length,
|
||||
formats: [
|
||||
{ name: 'millimeter (mm)', id: 'lengthmm' },
|
||||
{ name: 'inch (in)', id: 'lengthin' },
|
||||
{ name: 'feet (ft)', id: 'lengthft' },
|
||||
{ name: 'meter (m)', id: 'lengthm' },
|
||||
{ name: 'kilometer (km)', id: 'lengthkm' },
|
||||
{ name: 'mile (mi)', id: 'lengthmi' },
|
||||
{ name: 'millimeter (mm)', id: LengthFormats.LENGTHMM },
|
||||
{ name: 'inch (in)', id: LengthFormats.LENGTHIN },
|
||||
{ name: 'feet (ft)', id: LengthFormats.LENGTHFT },
|
||||
{ name: 'meter (m)', id: LengthFormats.LENGTHM },
|
||||
{ name: 'kilometer (km)', id: LengthFormats.LENGTHKM },
|
||||
{ name: 'mile (mi)', id: LengthFormats.LENGTHMI },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Pressure',
|
||||
name: CategoryNames.Pressure,
|
||||
formats: [
|
||||
{ name: 'Millibars', id: 'pressurembar' },
|
||||
{ name: 'Bars', id: 'pressurebar' },
|
||||
{ name: 'Kilobars', id: 'pressurekbar' },
|
||||
{ name: 'Pascals', id: 'pressurepa' },
|
||||
{ name: 'Hectopascals', id: 'pressurehpa' },
|
||||
{ name: 'Kilopascals', id: 'pressurekpa' },
|
||||
{ name: 'Inches of mercury', id: 'pressurehg' },
|
||||
{ name: 'PSI', id: 'pressurepsi' },
|
||||
{ name: 'Millibars', id: PressureFormats.PRESSUREMBAR },
|
||||
{ name: 'Bars', id: PressureFormats.PRESSUREBAR },
|
||||
{ name: 'Kilobars', id: PressureFormats.PRESSUREKBAR },
|
||||
{ name: 'Pascals', id: PressureFormats.PRESSUREPA },
|
||||
{ name: 'Hectopascals', id: PressureFormats.PRESSUREHPA },
|
||||
{ name: 'Kilopascals', id: PressureFormats.PRESSUREKPA },
|
||||
{ name: 'Inches of mercury', id: PressureFormats.PRESSUREHG },
|
||||
{ name: 'PSI', id: PressureFormats.PRESSUREPSI },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Radiation',
|
||||
name: CategoryNames.Radiation,
|
||||
formats: [
|
||||
{ name: 'Becquerel (Bq)', id: 'radbq' },
|
||||
{ name: 'curie (Ci)', id: 'radci' },
|
||||
{ name: 'Gray (Gy)', id: 'radgy' },
|
||||
{ name: 'rad', id: 'radrad' },
|
||||
{ name: 'Sievert (Sv)', id: 'radsv' },
|
||||
{ name: 'milliSievert (mSv)', id: 'radmsv' },
|
||||
{ name: 'microSievert (µSv)', id: 'radusv' },
|
||||
{ name: 'rem', id: 'radrem' },
|
||||
{ name: 'Exposure (C/kg)', id: 'radexpckg' },
|
||||
{ name: 'roentgen (R)', id: 'radr' },
|
||||
{ name: 'Sievert/hour (Sv/h)', id: 'radsvh' },
|
||||
{ name: 'milliSievert/hour (mSv/h)', id: 'radmsvh' },
|
||||
{ name: 'microSievert/hour (µSv/h)', id: 'radusvh' },
|
||||
{ name: 'Becquerel (Bq)', id: RadiationFormats.RADBQ },
|
||||
{ name: 'curie (Ci)', id: RadiationFormats.RADCI },
|
||||
{ name: 'Gray (Gy)', id: RadiationFormats.RADGY },
|
||||
{ name: 'rad', id: RadiationFormats.RADRAD },
|
||||
{ name: 'Sievert (Sv)', id: RadiationFormats.RADSV },
|
||||
{ name: 'milliSievert (mSv)', id: RadiationFormats.RADMSV },
|
||||
{ name: 'microSievert (µSv)', id: RadiationFormats.RADUSV },
|
||||
{ name: 'rem', id: RadiationFormats.RADREM },
|
||||
{ name: 'Exposure (C/kg)', id: RadiationFormats.RADEXPCKG },
|
||||
{ name: 'roentgen (R)', id: RadiationFormats.RADR },
|
||||
{ name: 'Sievert/hour (Sv/h)', id: RadiationFormats.RADSVH },
|
||||
{ name: 'milliSievert/hour (mSv/h)', id: RadiationFormats.RADMSVH },
|
||||
{ name: 'microSievert/hour (µSv/h)', id: RadiationFormats.RADUSVH },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Rotational Speed',
|
||||
name: CategoryNames.RotationSpeed,
|
||||
formats: [
|
||||
{ name: 'Revolutions per minute (rpm)', id: 'rotrpm' },
|
||||
{ name: 'Hertz (Hz)', id: 'rothz' },
|
||||
{ name: 'Radians per second (rad/s)', id: 'rotrads' },
|
||||
{ name: 'Degrees per second (°/s)', id: 'rotdegs' },
|
||||
{ name: 'Revolutions per minute (rpm)', id: RotationSpeedFormats.ROTRPM },
|
||||
{ name: 'Hertz (Hz)', id: RotationSpeedFormats.ROTHZ },
|
||||
{ name: 'Radians per second (rad/s)', id: RotationSpeedFormats.ROTRADS },
|
||||
{ name: 'Degrees per second (°/s)', id: RotationSpeedFormats.ROTDEGS },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Temperature',
|
||||
name: CategoryNames.Temperature,
|
||||
formats: [
|
||||
{ name: 'Celsius (°C)', id: 'celsius' },
|
||||
{ name: 'Fahrenheit (°F)', id: 'fahrenheit' },
|
||||
{ name: 'Kelvin (K)', id: 'kelvin' },
|
||||
{ name: 'Celsius (°C)', id: TemperatureFormats.CELSIUS },
|
||||
{ name: 'Fahrenheit (°F)', id: TemperatureFormats.FAHRENHEIT },
|
||||
{ name: 'Kelvin (K)', id: TemperatureFormats.KELVIN },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Velocity',
|
||||
name: CategoryNames.Velocity,
|
||||
formats: [
|
||||
{ name: 'meters/second (m/s)', id: 'velocityms' },
|
||||
{ name: 'kilometers/hour (km/h)', id: 'velocitykmh' },
|
||||
{ name: 'miles/hour (mph)', id: 'velocitymph' },
|
||||
{ name: 'knot (kn)', id: 'velocityknot' },
|
||||
{ name: 'meters/second (m/s)', id: VelocityFormats.METERS_PER_SECOND },
|
||||
{ name: 'kilometers/hour (km/h)', id: VelocityFormats.KILOMETERS_PER_HOUR },
|
||||
{ name: 'miles/hour (mph)', id: VelocityFormats.MILES_PER_HOUR },
|
||||
{ name: 'knot (kn)', id: VelocityFormats.KNOT },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Volume',
|
||||
name: CategoryNames.Volume,
|
||||
formats: [
|
||||
{ name: 'millilitre (mL)', id: 'mlitre' },
|
||||
{ name: 'litre (L)', id: 'litre' },
|
||||
{ name: 'cubic meter', id: 'm3' },
|
||||
{ name: 'Normal cubic meter', id: 'Nm3' },
|
||||
{ name: 'cubic decimeter', id: 'dm3' },
|
||||
{ name: 'gallons', id: 'gallons' },
|
||||
{ name: 'millilitre (mL)', id: VolumeFormats.MILLILITRE },
|
||||
{ name: 'litre (L)', id: VolumeFormats.LITRE },
|
||||
{ name: 'cubic meter', id: VolumeFormats.CUBIC_METER },
|
||||
{ name: 'Normal cubic meter', id: VolumeFormats.NORMAL_CUBIC_METER },
|
||||
{ name: 'cubic decimeter', id: VolumeFormats.CUBIC_DECIMETER },
|
||||
{ name: 'gallons', id: VolumeFormats.GALLONS },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Boolean',
|
||||
name: CategoryNames.Boolean,
|
||||
formats: [
|
||||
{ name: 'True / False', id: 'bool' },
|
||||
{ name: 'Yes / No', id: 'bool_yes_no' },
|
||||
{ name: 'On / Off', id: 'bool_on_off' },
|
||||
{ name: 'True / False', id: BooleanFormats.TRUE_FALSE },
|
||||
{ name: 'Yes / No', id: BooleanFormats.YES_NO },
|
||||
{ name: 'On / Off', id: BooleanFormats.ON_OFF },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
364
frontend/src/container/NewWidget/RightContainer/types.ts
Normal file
364
frontend/src/container/NewWidget/RightContainer/types.ts
Normal file
@@ -0,0 +1,364 @@
|
||||
export enum CategoryNames {
|
||||
Time = 'Time',
|
||||
Throughput = 'Throughput',
|
||||
Data = 'Data',
|
||||
DataRate = 'DataRate',
|
||||
HashRate = 'HashRate',
|
||||
Miscellaneous = 'Miscellaneous',
|
||||
Acceleration = 'Acceleration',
|
||||
Angular = 'Angular',
|
||||
Area = 'Area',
|
||||
Flops = 'Flops',
|
||||
Concentration = 'Concentration',
|
||||
Currency = 'Currency',
|
||||
Datetime = 'Datetime',
|
||||
PowerElectrical = 'PowerElectrical',
|
||||
Flow = 'Flow',
|
||||
Force = 'Force',
|
||||
Mass = 'Mass',
|
||||
Length = 'Length',
|
||||
Pressure = 'Pressure',
|
||||
Radiation = 'Radiation',
|
||||
RotationSpeed = 'RotationSpeed',
|
||||
Temperature = 'Temperature',
|
||||
Velocity = 'Velocity',
|
||||
Volume = 'Volume',
|
||||
Boolean = 'Boolean',
|
||||
Angle = 'Angle',
|
||||
Computation = 'Computation',
|
||||
Energy = 'Energy',
|
||||
}
|
||||
|
||||
export enum TimeFormats {
|
||||
Hertz = 'hertz',
|
||||
Nanoseconds = 'ns',
|
||||
Microseconds = 'µs',
|
||||
Milliseconds = 'ms',
|
||||
Seconds = 's',
|
||||
Minutes = 'm',
|
||||
Hours = 'h',
|
||||
Days = 'd',
|
||||
DurationMs = 'dtdurationms',
|
||||
DurationS = 'dtdurations',
|
||||
DurationHms = 'dthms',
|
||||
DurationDhms = 'dtdhms',
|
||||
Timeticks = 'timeticks',
|
||||
ClockMs = 'clockms',
|
||||
ClockS = 'clocks',
|
||||
}
|
||||
|
||||
export enum ThroughputFormats {
|
||||
CountsPerSec = 'cps',
|
||||
OpsPerSec = 'ops',
|
||||
RequestsPerSec = 'reqps',
|
||||
ReadsPerSec = 'rps',
|
||||
WritesPerSec = 'wps',
|
||||
IOOpsPerSec = 'iops',
|
||||
CountsPerMin = 'cpm',
|
||||
OpsPerMin = 'opm',
|
||||
ReadsPerMin = 'rpm',
|
||||
WritesPerMin = 'wpm',
|
||||
}
|
||||
|
||||
export enum DataFormats {
|
||||
BytesIEC = 'bytes',
|
||||
BytesSI = 'decbytes',
|
||||
BitsIEC = 'bits',
|
||||
BitsSI = 'decbits',
|
||||
KibiBytes = 'kbytes',
|
||||
KiloBytes = 'deckbytes',
|
||||
MebiBytes = 'mbytes',
|
||||
MegaBytes = 'decmbytes',
|
||||
GibiBytes = 'gbytes',
|
||||
GigaBytes = 'decgbytes',
|
||||
TebiBytes = 'tbytes',
|
||||
TeraBytes = 'dectbytes',
|
||||
PebiBytes = 'pbytes',
|
||||
PetaBytes = 'decpbytes',
|
||||
}
|
||||
|
||||
export enum DataRateFormats {
|
||||
PacketsPerSec = 'pps',
|
||||
BytesPerSecIEC = 'binBps',
|
||||
BytesPerSecSI = 'Bps',
|
||||
BitsPerSecIEC = 'binbps',
|
||||
BitsPerSecSI = 'bps',
|
||||
KibiBytesPerSec = 'KiBs',
|
||||
KibiBitsPerSec = 'Kibits',
|
||||
KiloBytesPerSec = 'KBs',
|
||||
KiloBitsPerSec = 'Kbits',
|
||||
MebiBytesPerSec = 'MiBs',
|
||||
MebiBitsPerSec = 'Mibits',
|
||||
MegaBytesPerSec = 'MBs',
|
||||
MegaBitsPerSec = 'Mbits',
|
||||
GibiBytesPerSec = 'GiBs',
|
||||
GibiBitsPerSec = 'Gibits',
|
||||
GigaBytesPerSec = 'GBs',
|
||||
GigaBitsPerSec = 'Gbits',
|
||||
TebiBytesPerSec = 'TiBs',
|
||||
TebiBitsPerSec = 'Tibits',
|
||||
TeraBytesPerSec = 'TBs',
|
||||
TeraBitsPerSec = 'Tbits',
|
||||
PebiBytesPerSec = 'PiBs',
|
||||
PebiBitsPerSec = 'Pibits',
|
||||
PetaBytesPerSec = 'PBs',
|
||||
PetaBitsPerSec = 'Pbits',
|
||||
}
|
||||
|
||||
export enum HashRateFormats {
|
||||
HashesPerSec = 'Hs',
|
||||
KiloHashesPerSec = 'KHs',
|
||||
MegaHashesPerSec = 'MHs',
|
||||
GigaHashesPerSec = 'GHs',
|
||||
TeraHashesPerSec = 'THs',
|
||||
PetaHashesPerSec = 'PHs',
|
||||
ExaHashesPerSec = 'EHs',
|
||||
}
|
||||
|
||||
export enum MiscellaneousFormats {
|
||||
None = 'none',
|
||||
String = 'string',
|
||||
Short = 'short',
|
||||
Percent = 'percent',
|
||||
PercentUnit = 'percentunit',
|
||||
Humidity = 'humidity',
|
||||
Decibel = 'dB',
|
||||
Hexadecimal0x = 'hex0x',
|
||||
Hexadecimal = 'hex',
|
||||
ScientificNotation = 'sci',
|
||||
LocaleFormat = 'locale',
|
||||
Pixels = 'pixel',
|
||||
}
|
||||
|
||||
export enum AccelerationFormats {
|
||||
MetersPerSecondSquared = 'accMS2',
|
||||
FeetPerSecondSquared = 'accFS2',
|
||||
GUnit = 'accG',
|
||||
}
|
||||
|
||||
export enum AngularFormats {
|
||||
Degree = 'degree',
|
||||
Radian = 'radian',
|
||||
Gradian = 'grad',
|
||||
ArcMinute = 'arcmin',
|
||||
ArcSecond = 'arcsec',
|
||||
}
|
||||
|
||||
export enum AreaFormats {
|
||||
SquareMeters = 'areaM2',
|
||||
SquareFeet = 'areaF2',
|
||||
SquareMiles = 'areaMI2',
|
||||
}
|
||||
|
||||
export enum FlopsFormats {
|
||||
FLOPs = 'flops',
|
||||
MFLOPs = 'mflops',
|
||||
GFLOPs = 'gflops',
|
||||
TFLOPs = 'tflops',
|
||||
PFLOPs = 'pflops',
|
||||
EFLOPs = 'eflops',
|
||||
ZFLOPs = 'zflops',
|
||||
YFLOPs = 'yflops',
|
||||
}
|
||||
|
||||
export enum ConcentrationFormats {
|
||||
PPM = 'ppm',
|
||||
PPB = 'conppb',
|
||||
NgM3 = 'conngm3',
|
||||
NgNM3 = 'conngNm3',
|
||||
UgM3 = 'conμgm3',
|
||||
UgNM3 = 'conμgNm3',
|
||||
MgM3 = 'conmgm3',
|
||||
MgNM3 = 'conmgNm3',
|
||||
GM3 = 'congm3',
|
||||
GNM3 = 'congNm3',
|
||||
MgDL = 'conmgdL',
|
||||
MmolL = 'conmmolL',
|
||||
}
|
||||
|
||||
export enum CurrencyFormats {
|
||||
USD = 'currencyUSD',
|
||||
GBP = 'currencyGBP',
|
||||
EUR = 'currencyEUR',
|
||||
JPY = 'currencyJPY',
|
||||
RUB = 'currencyRUB',
|
||||
UAH = 'currencyUAH',
|
||||
BRL = 'currencyBRL',
|
||||
DKK = 'currencyDKK',
|
||||
ISK = 'currencyISK',
|
||||
NOK = 'currencyNOK',
|
||||
SEK = 'currencySEK',
|
||||
CZK = 'currencyCZK',
|
||||
CHF = 'currencyCHF',
|
||||
PLN = 'currencyPLN',
|
||||
BTC = 'currencyBTC',
|
||||
MBTC = 'currencymBTC',
|
||||
UBTC = 'currencyμBTC',
|
||||
ZAR = 'currencyZAR',
|
||||
INR = 'currencyINR',
|
||||
KRW = 'currencyKRW',
|
||||
IDR = 'currencyIDR',
|
||||
PHP = 'currencyPHP',
|
||||
VND = 'currencyVND',
|
||||
}
|
||||
|
||||
export enum DatetimeFormats {
|
||||
ISO = 'dateTimeAsIso',
|
||||
ISONoDateIfToday = 'dateTimeAsIsoNoDateIfToday',
|
||||
US = 'dateTimeAsUS',
|
||||
USNoDateIfToday = 'dateTimeAsUSNoDateIfToday',
|
||||
Local = 'dateTimeAsLocal',
|
||||
LocalNoDateIfToday = 'dateTimeAsLocalNoDateIfToday',
|
||||
System = 'dateTimeAsSystem',
|
||||
FromNow = 'dateTimeFromNow',
|
||||
}
|
||||
|
||||
export enum PowerElectricalFormats {
|
||||
WATT = 'watt',
|
||||
KWATT = 'kwatt',
|
||||
MEGWATT = 'megwatt',
|
||||
GWATT = 'gwatt',
|
||||
MWATT = 'mwatt',
|
||||
WM2 = 'Wm2',
|
||||
VOLTAMP = 'voltamp',
|
||||
KVOLTAMP = 'kvoltamp',
|
||||
VOLTAMPREACT = 'voltampreact',
|
||||
KVOLTAMPREACT = 'kvoltampreact',
|
||||
WATTH = 'watth',
|
||||
WATTHPERKG = 'watthperkg',
|
||||
KWATTH = 'kwatth',
|
||||
KWATTM = 'kwattm',
|
||||
AMPH = 'amph',
|
||||
KAMPH = 'kamph',
|
||||
MAMPH = 'mamph',
|
||||
JOULE = 'joule',
|
||||
EV = 'ev',
|
||||
AMP = 'amp',
|
||||
KAMP = 'kamp',
|
||||
MAMP = 'mamp',
|
||||
VOLT = 'volt',
|
||||
KVOLT = 'kvolt',
|
||||
MVOLT = 'mvolt',
|
||||
DBM = 'dBm',
|
||||
OHM = 'ohm',
|
||||
KOHM = 'kohm',
|
||||
MOHM = 'Mohm',
|
||||
FARAD = 'farad',
|
||||
µFARAD = 'µfarad',
|
||||
NFARAD = 'nfarad',
|
||||
PFARAD = 'pfarad',
|
||||
FFARAD = 'ffarad',
|
||||
HENRY = 'henry',
|
||||
MHENRY = 'mhenry',
|
||||
µHENRY = 'µhenry',
|
||||
LUMENS = 'lumens',
|
||||
}
|
||||
|
||||
export enum FlowFormats {
|
||||
FLOWGPM = 'flowgpm',
|
||||
FLOWCMS = 'flowcms',
|
||||
FLOWCFS = 'flowcfs',
|
||||
FLOWCFM = 'flowcfm',
|
||||
LITREH = 'litreh',
|
||||
FLOWLPM = 'flowlpm',
|
||||
FLOWMLPM = 'flowmlpm',
|
||||
LUX = 'lux',
|
||||
}
|
||||
|
||||
export enum ForceFormats {
|
||||
FORCENM = 'forceNm',
|
||||
FORCEKNM = 'forcekNm',
|
||||
FORCEN = 'forceN',
|
||||
FORCEKN = 'forcekN',
|
||||
}
|
||||
|
||||
export enum MassFormats {
|
||||
MASSMG = 'massmg',
|
||||
MASSG = 'massg',
|
||||
MASSLB = 'masslb',
|
||||
MASSKG = 'masskg',
|
||||
MASST = 'masst',
|
||||
}
|
||||
|
||||
export enum LengthFormats {
|
||||
LENGTHMM = 'lengthmm',
|
||||
LENGTHIN = 'lengthin',
|
||||
LENGTHFT = 'lengthft',
|
||||
LENGTHM = 'lengthm',
|
||||
LENGTHKM = 'lengthkm',
|
||||
LENGTHMI = 'lengthmi',
|
||||
}
|
||||
|
||||
export enum PressureFormats {
|
||||
PRESSUREMBAR = 'pressurembar',
|
||||
PRESSUREBAR = 'pressurebar',
|
||||
PRESSUREKBAR = 'pressurekbar',
|
||||
PRESSUREPA = 'pressurepa',
|
||||
PRESSUREHPA = 'pressurehpa',
|
||||
PRESSUREKPA = 'pressurekpa',
|
||||
PRESSUREHG = 'pressurehg',
|
||||
PRESSUREPSI = 'pressurepsi',
|
||||
}
|
||||
|
||||
export enum RadiationFormats {
|
||||
RADBQ = 'radbq',
|
||||
RADCI = 'radci',
|
||||
RADGY = 'radgy',
|
||||
RADRAD = 'radrad',
|
||||
RADSV = 'radsv',
|
||||
RADMSV = 'radmsv',
|
||||
RADUSV = 'radusv',
|
||||
RADREM = 'radrem',
|
||||
RADEXPCKG = 'radexpckg',
|
||||
RADR = 'radr',
|
||||
RADSVH = 'radsvh',
|
||||
RADMSVH = 'radmsvh',
|
||||
RADUSVH = 'radusvh',
|
||||
}
|
||||
|
||||
export enum RotationSpeedFormats {
|
||||
ROTRPM = 'rotrpm',
|
||||
ROTHZ = 'rothz',
|
||||
ROTRADS = 'rotrads',
|
||||
ROTDEGS = 'rotdegs',
|
||||
}
|
||||
|
||||
export enum TemperatureFormats {
|
||||
CELSIUS = 'celsius',
|
||||
FAHRENHEIT = 'fahrenheit',
|
||||
KELVIN = 'kelvin',
|
||||
}
|
||||
|
||||
export enum VelocityFormats {
|
||||
METERS_PER_SECOND = 'velocityms',
|
||||
KILOMETERS_PER_HOUR = 'velocitykmh',
|
||||
MILES_PER_HOUR = 'velocitymph',
|
||||
KNOT = 'velocityknot',
|
||||
}
|
||||
|
||||
export enum VolumeFormats {
|
||||
MILLILITRE = 'mlitre',
|
||||
LITRE = 'litre',
|
||||
CUBIC_METER = 'm3',
|
||||
NORMAL_CUBIC_METER = 'Nm3',
|
||||
CUBIC_DECIMETER = 'dm3',
|
||||
GALLONS = 'gallons',
|
||||
}
|
||||
|
||||
export enum BooleanFormats {
|
||||
TRUE_FALSE = 'bool',
|
||||
YES_NO = 'bool_yes_no',
|
||||
ON_OFF = 'bool_on_off',
|
||||
}
|
||||
|
||||
export type Format = {
|
||||
name: string;
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type Category = {
|
||||
name: string;
|
||||
formats: Format[];
|
||||
};
|
||||
|
||||
export type DataTypeCategories = Category[];
|
||||
@@ -67,7 +67,9 @@ function NewWidget({ selectedGraph, saveSettingOfPanel }: Props): JSX.Element {
|
||||
|
||||
const selectedWidget = getWidget();
|
||||
|
||||
const [title, setTitle] = useState<string>(selectedWidget?.title || '');
|
||||
const [title, setTitle] = useState<string>(
|
||||
selectedWidget?.title?.toString() || '',
|
||||
);
|
||||
const [description, setDescription] = useState<string>(
|
||||
selectedWidget?.description || '',
|
||||
);
|
||||
|
||||
@@ -5,12 +5,13 @@ import { changeHistoryColumns } from '../../PipelineListsView/config';
|
||||
import { HistoryTableWrapper } from '../../styles';
|
||||
import { historyPagination } from '../config';
|
||||
|
||||
function ChangeHistory({ piplineData }: ChangeHistoryProps): JSX.Element {
|
||||
function ChangeHistory({ pipelineData }: ChangeHistoryProps): JSX.Element {
|
||||
return (
|
||||
<HistoryTableWrapper>
|
||||
<Table
|
||||
columns={changeHistoryColumns}
|
||||
dataSource={piplineData?.history ?? []}
|
||||
dataSource={pipelineData?.history ?? []}
|
||||
rowKey="id"
|
||||
pagination={historyPagination}
|
||||
/>
|
||||
</HistoryTableWrapper>
|
||||
@@ -18,7 +19,7 @@ function ChangeHistory({ piplineData }: ChangeHistoryProps): JSX.Element {
|
||||
}
|
||||
|
||||
interface ChangeHistoryProps {
|
||||
piplineData: Pipeline;
|
||||
pipelineData: Pipeline;
|
||||
}
|
||||
|
||||
export default ChangeHistory;
|
||||
|
||||
@@ -11,13 +11,13 @@ function CreatePipelineButton({
|
||||
setActionType,
|
||||
isActionMode,
|
||||
setActionMode,
|
||||
piplineData,
|
||||
pipelineData,
|
||||
}: CreatePipelineButtonProps): JSX.Element {
|
||||
const { t } = useTranslation(['pipeline']);
|
||||
|
||||
const isAddNewPipelineVisible = useMemo(
|
||||
() => checkDataLength(piplineData?.pipelines),
|
||||
[piplineData?.pipelines],
|
||||
() => checkDataLength(pipelineData?.pipelines),
|
||||
[pipelineData?.pipelines],
|
||||
);
|
||||
const isDisabled = isActionMode === ActionMode.Editing;
|
||||
|
||||
@@ -56,7 +56,7 @@ interface CreatePipelineButtonProps {
|
||||
setActionType: (actionType: string) => void;
|
||||
isActionMode: string;
|
||||
setActionMode: (actionMode: string) => void;
|
||||
piplineData: Pipeline;
|
||||
pipelineData: Pipeline;
|
||||
}
|
||||
|
||||
export default CreatePipelineButton;
|
||||
|
||||
@@ -7,7 +7,7 @@ import PipelinesSearchSection from './PipelinesSearchSection';
|
||||
|
||||
function PipelinePageLayout({
|
||||
refetchPipelineLists,
|
||||
piplineData,
|
||||
pipelineData,
|
||||
}: PipelinePageLayoutProps): JSX.Element {
|
||||
const [isActionType, setActionType] = useState<string>();
|
||||
const [isActionMode, setActionMode] = useState<string>('viewing-mode');
|
||||
@@ -19,7 +19,7 @@ function PipelinePageLayout({
|
||||
setActionType={setActionType}
|
||||
setActionMode={setActionMode}
|
||||
isActionMode={isActionMode}
|
||||
piplineData={piplineData}
|
||||
pipelineData={pipelineData}
|
||||
/>
|
||||
<PipelinesSearchSection setPipelineSearchValue={setPipelineSearchValue} />
|
||||
<PipelineListsView
|
||||
@@ -27,7 +27,7 @@ function PipelinePageLayout({
|
||||
setActionType={setActionType}
|
||||
setActionMode={setActionMode}
|
||||
isActionMode={isActionMode}
|
||||
piplineData={piplineData}
|
||||
pipelineData={pipelineData}
|
||||
refetchPipelineLists={refetchPipelineLists}
|
||||
pipelineSearchValue={pipelineSearchValue}
|
||||
/>
|
||||
@@ -37,7 +37,7 @@ function PipelinePageLayout({
|
||||
|
||||
interface PipelinePageLayoutProps {
|
||||
refetchPipelineLists: VoidFunction;
|
||||
piplineData: Pipeline;
|
||||
pipelineData: Pipeline;
|
||||
}
|
||||
|
||||
export default PipelinePageLayout;
|
||||
|
||||
@@ -4,21 +4,21 @@ import { ModeAndConfigWrapper } from './styles';
|
||||
|
||||
function ModeAndConfiguration({
|
||||
isActionMode,
|
||||
verison,
|
||||
version,
|
||||
}: ModeAndConfigurationType): JSX.Element {
|
||||
const actionMode = isActionMode === ActionMode.Editing;
|
||||
|
||||
return (
|
||||
<ModeAndConfigWrapper>
|
||||
Mode: <span>{actionMode ? 'Editing' : 'Viewing'}</span>
|
||||
<div>Configuration Version: {verison}</div>
|
||||
<div>Configuration Version: {version}</div>
|
||||
</ModeAndConfigWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export interface ModeAndConfigurationType {
|
||||
isActionMode: string;
|
||||
verison: string | number;
|
||||
version: string | number;
|
||||
}
|
||||
|
||||
export default ModeAndConfiguration;
|
||||
|
||||
@@ -193,7 +193,8 @@ function PipelineExpandView({
|
||||
}, [setActionType]);
|
||||
|
||||
const footer = useCallback((): JSX.Element | undefined => {
|
||||
if (prevPipelineData.length === 0 || isEditingActionMode) {
|
||||
const prevPipelinesCount = prevPipelineData?.length || 0;
|
||||
if (prevPipelinesCount === 0 || isEditingActionMode) {
|
||||
return (
|
||||
<FooterButton type="link" onClick={addNewProcessorHandler}>
|
||||
<PlusCircleOutlined />
|
||||
|
||||
@@ -47,7 +47,7 @@ function PipelineListsView({
|
||||
setActionType,
|
||||
isActionMode,
|
||||
setActionMode,
|
||||
piplineData,
|
||||
pipelineData,
|
||||
refetchPipelineLists,
|
||||
pipelineSearchValue,
|
||||
}: PipelineListsViewProps): JSX.Element {
|
||||
@@ -55,10 +55,10 @@ function PipelineListsView({
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
const { notifications } = useNotifications();
|
||||
const [prevPipelineData, setPrevPipelineData] = useState<Array<PipelineData>>(
|
||||
cloneDeep(piplineData?.pipelines),
|
||||
cloneDeep(pipelineData?.pipelines),
|
||||
);
|
||||
const [currPipelineData, setCurrPipelineData] = useState<Array<PipelineData>>(
|
||||
cloneDeep(piplineData?.pipelines),
|
||||
cloneDeep(pipelineData?.pipelines),
|
||||
);
|
||||
const [
|
||||
expandedPipelineData,
|
||||
@@ -77,14 +77,14 @@ function PipelineListsView({
|
||||
const isEditingActionMode = isActionMode === ActionMode.Editing;
|
||||
|
||||
useEffect(() => {
|
||||
if (pipelineSearchValue === '') setCurrPipelineData(piplineData?.pipelines);
|
||||
if (pipelineSearchValue === '') setCurrPipelineData(pipelineData?.pipelines);
|
||||
if (pipelineSearchValue !== '') {
|
||||
const filterData = piplineData?.pipelines.filter((data: PipelineData) =>
|
||||
const filterData = pipelineData?.pipelines.filter((data: PipelineData) =>
|
||||
getDataOnSearch(data as never, pipelineSearchValue),
|
||||
);
|
||||
setCurrPipelineData(filterData);
|
||||
}
|
||||
}, [pipelineSearchValue, piplineData?.pipelines]);
|
||||
}, [pipelineSearchValue, pipelineData?.pipelines]);
|
||||
|
||||
const handleAlert = useCallback(
|
||||
({ title, descrition, buttontext, onCancel, onOk }: AlertMessage) => {
|
||||
@@ -414,7 +414,7 @@ function PipelineListsView({
|
||||
<Container>
|
||||
<ModeAndConfiguration
|
||||
isActionMode={isActionMode}
|
||||
verison={piplineData?.version}
|
||||
version={pipelineData?.version}
|
||||
/>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Table
|
||||
@@ -445,7 +445,7 @@ interface PipelineListsViewProps {
|
||||
setActionType: (actionType?: ActionType) => void;
|
||||
isActionMode: string;
|
||||
setActionMode: (actionMode: ActionMode) => void;
|
||||
piplineData: Pipeline;
|
||||
pipelineData: Pipeline;
|
||||
refetchPipelineLists: VoidFunction;
|
||||
pipelineSearchValue: string;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Pipeline, PipelineData } from 'types/api/pipeline/def';
|
||||
|
||||
export const configurationVerison = '1.0';
|
||||
export const configurationVersion = '1.0';
|
||||
|
||||
export const pipelineMockData: Array<PipelineData> = [
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ describe('PipelinePage container test', () => {
|
||||
setActionType={jest.fn()}
|
||||
isActionMode="viewing-mode"
|
||||
setActionMode={jest.fn()}
|
||||
piplineData={pipelineApiResponseMockData}
|
||||
pipelineData={pipelineApiResponseMockData}
|
||||
/>
|
||||
</I18nextProvider>
|
||||
</Provider>
|
||||
|
||||
@@ -49,7 +49,7 @@ describe('PipelinePage container test', () => {
|
||||
<Provider store={store}>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<PipelinePageLayout
|
||||
piplineData={pipelinedata}
|
||||
pipelineData={pipelinedata}
|
||||
refetchPipelineLists={refetchPipelineLists}
|
||||
/>
|
||||
</I18nextProvider>
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Select, SelectProps, Space } from 'antd';
|
||||
import { getCategorySelectOptionByName } from 'container/NewWidget/RightContainer/alertFomatCategories';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
|
||||
import { categoryToSupport } from './config';
|
||||
import { DefaultLabel, selectStyles } from './styles';
|
||||
import { IBuilderUnitsFilterProps } from './types';
|
||||
import { filterOption } from './utils';
|
||||
|
||||
function BuilderUnitsFilter({
|
||||
onChange,
|
||||
}: IBuilderUnitsFilterProps): JSX.Element {
|
||||
const { currentQuery, handleOnUnitsChange } = useQueryBuilder();
|
||||
|
||||
const selectedValue = currentQuery?.unit;
|
||||
|
||||
const allOptions = categoryToSupport.map((category) => ({
|
||||
label: category,
|
||||
options: getCategorySelectOptionByName(category),
|
||||
}));
|
||||
|
||||
const onChangeHandler: SelectProps['onChange'] = (value): void => {
|
||||
if (onChange) {
|
||||
onChange(value);
|
||||
}
|
||||
|
||||
handleOnUnitsChange(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<Space>
|
||||
<DefaultLabel>Y-axis unit</DefaultLabel>
|
||||
<Select
|
||||
style={selectStyles}
|
||||
onChange={onChangeHandler}
|
||||
value={selectedValue}
|
||||
options={allOptions}
|
||||
allowClear
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
placeholder="Select unit"
|
||||
filterOption={filterOption}
|
||||
/>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
||||
export { BuilderUnitsFilter };
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user