mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-02 05:50:40 +08:00
commit
aa5100261d
33
.editorconfig
Normal file
33
.editorconfig
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Unix-style newlines with a newline ending every file
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
# Matches multiple files with brace expansion notation
|
||||||
|
# Set default charset
|
||||||
|
[*.{js,py}]
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
# 4 space indentation
|
||||||
|
[*.py]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
# Tab indentation (no size specified)
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
# Indentation override for all JS under lib directory
|
||||||
|
[lib/**.js]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# Matches the exact files either package.json or .travis.yml
|
||||||
|
[{package.json,.travis.yml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
6
Makefile
6
Makefile
@ -115,11 +115,9 @@ down-arm:
|
|||||||
@docker-compose -f $(STANDALONE_DIRECTORY)/docker-compose.arm.yaml down -v
|
@docker-compose -f $(STANDALONE_DIRECTORY)/docker-compose.arm.yaml down -v
|
||||||
|
|
||||||
clear-standalone-data:
|
clear-standalone-data:
|
||||||
@cd $(STANDALONE_DIRECTORY)
|
@docker run --rm -v "$(PWD)/$(STANDALONE_DIRECTORY)/data:/pwd" busybox \
|
||||||
@docker run --rm -v "data:/pwd" busybox \
|
|
||||||
sh -c "cd /pwd && rm -rf alertmanager/* clickhouse/* signoz/*"
|
sh -c "cd /pwd && rm -rf alertmanager/* clickhouse/* signoz/*"
|
||||||
|
|
||||||
clear-swarm-data:
|
clear-swarm-data:
|
||||||
@cd $(SWARM_DIRECTORY)
|
@docker run --rm -v "$(PWD)/$(SWARM_DIRECTORY)/data:/pwd" busybox \
|
||||||
@docker run --rm -v "data:/pwd" busybox \
|
|
||||||
sh -c "cd /pwd && rm -rf alertmanager/* clickhouse/* signoz/*"
|
sh -c "cd /pwd && rm -rf alertmanager/* clickhouse/* signoz/*"
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<yandex>
|
<yandex>
|
||||||
<logger>
|
<logger>
|
||||||
<level>trace</level>
|
<level>information</level>
|
||||||
<log>/var/log/clickhouse-server/clickhouse-server.log</log>
|
<console>1</console>
|
||||||
<errorlog>/var/log/clickhouse-server/clickhouse-server.err.log</errorlog>
|
|
||||||
<size>1000M</size>
|
|
||||||
<count>10</count>
|
|
||||||
</logger>
|
</logger>
|
||||||
|
|
||||||
<http_port>8123</http_port>
|
<http_port>8123</http_port>
|
||||||
@ -45,6 +42,34 @@
|
|||||||
</client>
|
</client>
|
||||||
</openSSL>
|
</openSSL>
|
||||||
|
|
||||||
|
<!-- Example config for tiered storage -->
|
||||||
|
<!-- <storage_configuration>
|
||||||
|
<disks>
|
||||||
|
<default>
|
||||||
|
<keep_free_space_bytes>10485760</keep_free_space_bytes>
|
||||||
|
</default>
|
||||||
|
<s3>
|
||||||
|
<type>s3</type>
|
||||||
|
<endpoint>https://BUCKET-NAME.s3.amazonaws.com/data/</endpoint>
|
||||||
|
<access_key_id>ACCESS-KEY-ID</access_key_id>
|
||||||
|
<secret_access_key>SECRET-ACCESS-KEY</secret_access_key>
|
||||||
|
</s3>
|
||||||
|
</disks>
|
||||||
|
<policies>
|
||||||
|
<tiered>
|
||||||
|
<volumes>
|
||||||
|
<default>
|
||||||
|
<disk>default</disk>
|
||||||
|
</default>
|
||||||
|
<s3>
|
||||||
|
<disk>s3</disk>
|
||||||
|
</s3>
|
||||||
|
</volumes>
|
||||||
|
</tiered>
|
||||||
|
</policies>
|
||||||
|
</storage_configuration> -->
|
||||||
|
|
||||||
|
|
||||||
<!-- Default root page on http[s] server. For example load UI from https://tabix.io/ when opening http://localhost:8123 -->
|
<!-- Default root page on http[s] server. For example load UI from https://tabix.io/ when opening http://localhost:8123 -->
|
||||||
<!--
|
<!--
|
||||||
<http_server_default_response><![CDATA[<html ng-app="SMI2"><head><base href="http://ui.tabix.io/"></head><body><div ui-view="" class="content-ui"></div><script src="http://loader.tabix.io/master.js"></script></body></html>]]></http_server_default_response>
|
<http_server_default_response><![CDATA[<html ng-app="SMI2"><head><base href="http://ui.tabix.io/"></head><body><div ui-view="" class="content-ui"></div><script src="http://loader.tabix.io/master.js"></script></body></html>]]></http_server_default_response>
|
||||||
|
@ -3,12 +3,19 @@ version: "3.9"
|
|||||||
services:
|
services:
|
||||||
clickhouse:
|
clickhouse:
|
||||||
image: yandex/clickhouse-server:21.12.3.32
|
image: yandex/clickhouse-server:21.12.3.32
|
||||||
|
# ports:
|
||||||
|
# - "9000:9000"
|
||||||
|
# - "8123:8123"
|
||||||
volumes:
|
volumes:
|
||||||
- ./clickhouse-config.xml:/etc/clickhouse-server/config.xml
|
- ./clickhouse-config.xml:/etc/clickhouse-server/config.xml
|
||||||
- ./data/clickhouse/:/var/lib/clickhouse/
|
- ./data/clickhouse/:/var/lib/clickhouse/
|
||||||
deploy:
|
deploy:
|
||||||
restart_policy:
|
restart_policy:
|
||||||
condition: on-failure
|
condition: on-failure
|
||||||
|
logging:
|
||||||
|
options:
|
||||||
|
max-size: 50m
|
||||||
|
max-file: "3"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
# "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'"
|
# "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"]
|
||||||
@ -17,7 +24,7 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
image: signoz/alertmanager:0.6.0
|
image: signoz/alertmanager:0.6.1
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
command:
|
command:
|
||||||
@ -30,7 +37,7 @@ services:
|
|||||||
condition: on-failure
|
condition: on-failure
|
||||||
|
|
||||||
query-service:
|
query-service:
|
||||||
image: signoz/query-service:0.7.4
|
image: signoz/query-service:0.7.5
|
||||||
command: ["-config=/root/config/prometheus.yml"]
|
command: ["-config=/root/config/prometheus.yml"]
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
@ -45,6 +52,11 @@ services:
|
|||||||
- TELEMETRY_ENABLED=true
|
- TELEMETRY_ENABLED=true
|
||||||
- DEPLOYMENT_TYPE=docker-swarm
|
- DEPLOYMENT_TYPE=docker-swarm
|
||||||
|
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--spider", "-q", "localhost:8080/api/v1/version"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
deploy:
|
deploy:
|
||||||
restart_policy:
|
restart_policy:
|
||||||
condition: on-failure
|
condition: on-failure
|
||||||
@ -52,8 +64,12 @@ services:
|
|||||||
- clickhouse
|
- clickhouse
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:0.7.4
|
image: signoz/frontend:0.7.5
|
||||||
|
deploy:
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
depends_on:
|
depends_on:
|
||||||
|
- alertmanager
|
||||||
- query-service
|
- query-service
|
||||||
ports:
|
ports:
|
||||||
- "3301:3301"
|
- "3301:3301"
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<yandex>
|
<yandex>
|
||||||
<logger>
|
<logger>
|
||||||
<level>trace</level>
|
<level>information</level>
|
||||||
<log>/var/log/clickhouse-server/clickhouse-server.log</log>
|
<console>1</console>
|
||||||
<errorlog>/var/log/clickhouse-server/clickhouse-server.err.log</errorlog>
|
|
||||||
<size>1000M</size>
|
|
||||||
<count>10</count>
|
|
||||||
</logger>
|
</logger>
|
||||||
|
|
||||||
<http_port>8123</http_port>
|
<http_port>8123</http_port>
|
||||||
@ -46,30 +43,31 @@
|
|||||||
</openSSL>
|
</openSSL>
|
||||||
|
|
||||||
<!-- Example config for tiered storage -->
|
<!-- Example config for tiered storage -->
|
||||||
<!-- <storage_configuration> -->
|
<!-- <storage_configuration>
|
||||||
<!-- <disks> -->
|
<disks>
|
||||||
<!-- <default> -->
|
<default>
|
||||||
<!-- </default> -->
|
<keep_free_space_bytes>10485760</keep_free_space_bytes>
|
||||||
<!-- <s3> -->
|
</default>
|
||||||
<!-- <type>s3</type> -->
|
<s3>
|
||||||
<!-- <endpoint>http://172.17.0.1:9100/test/random/</endpoint> -->
|
<type>s3</type>
|
||||||
<!-- <access_key_id>ash</access_key_id> -->
|
<endpoint>https://BUCKET-NAME.s3.amazonaws.com/data/</endpoint>
|
||||||
<!-- <secret_access_key>password</secret_access_key> -->
|
<access_key_id>ACCESS-KEY-ID</access_key_id>
|
||||||
<!-- </s3> -->
|
<secret_access_key>SECRET-ACCESS-KEY</secret_access_key>
|
||||||
<!-- </disks> -->
|
</s3>
|
||||||
<!-- <policies> -->
|
</disks>
|
||||||
<!-- <tiered> -->
|
<policies>
|
||||||
<!-- <volumes> -->
|
<tiered>
|
||||||
<!-- <default> -->
|
<volumes>
|
||||||
<!-- <disk>default</disk> -->
|
<default>
|
||||||
<!-- </default> -->
|
<disk>default</disk>
|
||||||
<!-- <s3> -->
|
</default>
|
||||||
<!-- <disk>s3</disk> -->
|
<s3>
|
||||||
<!-- </s3> -->
|
<disk>s3</disk>
|
||||||
<!-- </volumes> -->
|
</s3>
|
||||||
<!-- </tiered> -->
|
</volumes>
|
||||||
<!-- </policies> -->
|
</tiered>
|
||||||
<!-- </storage_configuration> -->
|
</policies>
|
||||||
|
</storage_configuration> -->
|
||||||
|
|
||||||
|
|
||||||
<!-- Default root page on http[s] server. For example load UI from https://tabix.io/ when opening http://localhost:8123 -->
|
<!-- Default root page on http[s] server. For example load UI from https://tabix.io/ when opening http://localhost:8123 -->
|
||||||
|
@ -3,10 +3,17 @@ version: "2.4"
|
|||||||
services:
|
services:
|
||||||
clickhouse:
|
clickhouse:
|
||||||
image: altinity/clickhouse-server:21.12.3.32.altinitydev.arm
|
image: altinity/clickhouse-server:21.12.3.32.altinitydev.arm
|
||||||
|
# ports:
|
||||||
|
# - "9000:9000"
|
||||||
|
# - "8123:8123"
|
||||||
volumes:
|
volumes:
|
||||||
- ./clickhouse-config.xml:/etc/clickhouse-server/config.xml
|
- ./clickhouse-config.xml:/etc/clickhouse-server/config.xml
|
||||||
- ./data/clickhouse/:/var/lib/clickhouse/
|
- ./data/clickhouse/:/var/lib/clickhouse/
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
|
logging:
|
||||||
|
options:
|
||||||
|
max-size: 50m
|
||||||
|
max-file: "3"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
# "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'"
|
# "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"]
|
||||||
@ -15,17 +22,19 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
image: signoz/alertmanager:0.6.0
|
image: signoz/alertmanager:0.6.1
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
depends_on:
|
depends_on:
|
||||||
- query-service
|
query-service:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: on-failure
|
||||||
command:
|
command:
|
||||||
- --queryService.url=http://query-service:8080
|
- --queryService.url=http://query-service:8080
|
||||||
- --storage.path=/data
|
- --storage.path=/data
|
||||||
|
|
||||||
query-service:
|
query-service:
|
||||||
image: signoz/query-service:0.7.4
|
image: signoz/query-service:0.7.5
|
||||||
container_name: query-service
|
container_name: query-service
|
||||||
command: ["-config=/root/config/prometheus.yml"]
|
command: ["-config=/root/config/prometheus.yml"]
|
||||||
volumes:
|
volumes:
|
||||||
@ -40,14 +49,21 @@ services:
|
|||||||
- DEPLOYMENT_TYPE=docker-standalone-arm
|
- DEPLOYMENT_TYPE=docker-standalone-arm
|
||||||
|
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--spider", "-q", "localhost:8080/api/v1/version"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
depends_on:
|
depends_on:
|
||||||
clickhouse:
|
clickhouse:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:0.7.4
|
image: signoz/frontend:0.7.5
|
||||||
container_name: frontend
|
container_name: frontend
|
||||||
|
restart: on-failure
|
||||||
depends_on:
|
depends_on:
|
||||||
|
- alertmanager
|
||||||
- query-service
|
- query-service
|
||||||
ports:
|
ports:
|
||||||
- "3301:3301"
|
- "3301:3301"
|
||||||
|
@ -3,10 +3,17 @@ version: "2.4"
|
|||||||
services:
|
services:
|
||||||
clickhouse:
|
clickhouse:
|
||||||
image: yandex/clickhouse-server:21.12.3.32
|
image: yandex/clickhouse-server:21.12.3.32
|
||||||
|
# ports:
|
||||||
|
# - "9000:9000"
|
||||||
|
# - "8123:8123"
|
||||||
volumes:
|
volumes:
|
||||||
- ./clickhouse-config.xml:/etc/clickhouse-server/config.xml
|
- ./clickhouse-config.xml:/etc/clickhouse-server/config.xml
|
||||||
- ./data/clickhouse/:/var/lib/clickhouse/
|
- ./data/clickhouse/:/var/lib/clickhouse/
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
|
logging:
|
||||||
|
options:
|
||||||
|
max-size: 50m
|
||||||
|
max-file: "3"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
# "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'"
|
# "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"]
|
||||||
@ -15,11 +22,13 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
image: signoz/alertmanager:0.6.0
|
image: signoz/alertmanager:0.6.1
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
depends_on:
|
depends_on:
|
||||||
- query-service
|
query-service:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: on-failure
|
||||||
command:
|
command:
|
||||||
- --queryService.url=http://query-service:8080
|
- --queryService.url=http://query-service:8080
|
||||||
- --storage.path=/data
|
- --storage.path=/data
|
||||||
@ -27,7 +36,7 @@ services:
|
|||||||
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
|
# 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:
|
query-service:
|
||||||
image: signoz/query-service:0.7.4
|
image: signoz/query-service:0.7.5
|
||||||
container_name: query-service
|
container_name: query-service
|
||||||
command: ["-config=/root/config/prometheus.yml"]
|
command: ["-config=/root/config/prometheus.yml"]
|
||||||
volumes:
|
volumes:
|
||||||
@ -41,14 +50,21 @@ services:
|
|||||||
- TELEMETRY_ENABLED=true
|
- TELEMETRY_ENABLED=true
|
||||||
- DEPLOYMENT_TYPE=docker-standalone-amd
|
- DEPLOYMENT_TYPE=docker-standalone-amd
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--spider", "-q", "localhost:8080/api/v1/version"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
depends_on:
|
depends_on:
|
||||||
clickhouse:
|
clickhouse:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:0.7.4
|
image: signoz/frontend:0.7.5
|
||||||
container_name: frontend
|
container_name: frontend
|
||||||
|
restart: on-failure
|
||||||
depends_on:
|
depends_on:
|
||||||
|
- alertmanager
|
||||||
- query-service
|
- query-service
|
||||||
ports:
|
ports:
|
||||||
- "3301:3301"
|
- "3301:3301"
|
||||||
|
@ -247,7 +247,7 @@ bye() { # Prints a friendly good bye message and exits the script.
|
|||||||
echo "or reach us for support in #help channel in our Slack Community https://signoz.io/slack"
|
echo "or reach us for support in #help channel in our Slack Community https://signoz.io/slack"
|
||||||
echo "++++++++++++++++++++++++++++++++++++++++"
|
echo "++++++++++++++++++++++++++++++++++++++++"
|
||||||
|
|
||||||
if [[ email == "" ]]; then
|
if [[ $email == "" ]]; then
|
||||||
echo -e "\n📨 Please share your email to receive support with the installation"
|
echo -e "\n📨 Please share your email to receive support with the installation"
|
||||||
read -rp 'Email: ' email
|
read -rp 'Email: ' email
|
||||||
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
node_modules
|
node_modules
|
||||||
build
|
build
|
||||||
|
6
frontend/babel.config.js
Normal file
6
frontend/babel.config.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
['@babel/preset-env', { targets: { node: 'current' } }],
|
||||||
|
'@babel/preset-typescript',
|
||||||
|
],
|
||||||
|
};
|
@ -9,12 +9,19 @@ const config: Config.InitialOptions = {
|
|||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
'\\.(css|less)$': '<rootDir>/__mocks__/cssMock.ts',
|
'\\.(css|less)$': '<rootDir>/__mocks__/cssMock.ts',
|
||||||
},
|
},
|
||||||
notify: true,
|
globals: {
|
||||||
notifyMode: 'always',
|
extensionsToTreatAsEsm: ['.ts'],
|
||||||
testMatch: ['<rootDir>/src/**/?(*.)(test).(ts|js)?(x)'],
|
'ts-jest': {
|
||||||
transform: {
|
useESM: true,
|
||||||
'\\.(js|jsx|ts|tsx)?$': 'babel-jest',
|
},
|
||||||
},
|
},
|
||||||
|
testMatch: ['<rootDir>/src/**/?(*.)(test).(ts|js)?(x)'],
|
||||||
|
preset: 'ts-jest/presets/js-with-ts-esm',
|
||||||
|
transform: {
|
||||||
|
'^.+\\.(ts|tsx)?$': 'ts-jest',
|
||||||
|
'^.+\\.(js|jsx)$': 'babel-jest',
|
||||||
|
},
|
||||||
|
transformIgnorePatterns: ['node_modules/(?!(lodash-es)/)'],
|
||||||
setupFilesAfterEnv: ['<rootDir>jest.setup.ts'],
|
setupFilesAfterEnv: ['<rootDir>jest.setup.ts'],
|
||||||
testPathIgnorePatterns: ['/node_modules/', '/public/'],
|
testPathIgnorePatterns: ['/node_modules/', '/public/'],
|
||||||
moduleDirectories: ['node_modules', 'src'],
|
moduleDirectories: ['node_modules', 'src'],
|
||||||
|
@ -57,7 +57,7 @@
|
|||||||
"i18next": "^21.6.12",
|
"i18next": "^21.6.12",
|
||||||
"i18next-browser-languagedetector": "^6.1.3",
|
"i18next-browser-languagedetector": "^6.1.3",
|
||||||
"i18next-http-backend": "^1.3.2",
|
"i18next-http-backend": "^1.3.2",
|
||||||
"jest": "26.6.0",
|
"jest": "^27.5.1",
|
||||||
"less": "^4.1.2",
|
"less": "^4.1.2",
|
||||||
"less-loader": "^10.2.0",
|
"less-loader": "^10.2.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
@ -68,6 +68,7 @@
|
|||||||
"react-graph-vis": "^1.0.5",
|
"react-graph-vis": "^1.0.5",
|
||||||
"react-grid-layout": "^1.2.5",
|
"react-grid-layout": "^1.2.5",
|
||||||
"react-i18next": "^11.16.1",
|
"react-i18next": "^11.16.1",
|
||||||
|
"react-query": "^3.34.19",
|
||||||
"react-redux": "^7.2.2",
|
"react-redux": "^7.2.2",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-use": "^17.3.2",
|
"react-use": "^17.3.2",
|
||||||
@ -107,6 +108,7 @@
|
|||||||
"@babel/preset-typescript": "^7.12.17",
|
"@babel/preset-typescript": "^7.12.17",
|
||||||
"@jest/globals": "^27.5.1",
|
"@jest/globals": "^27.5.1",
|
||||||
"@testing-library/cypress": "^8.0.0",
|
"@testing-library/cypress": "^8.0.0",
|
||||||
|
"@testing-library/react-hooks": "^7.0.2",
|
||||||
"@types/color": "^3.0.3",
|
"@types/color": "^3.0.3",
|
||||||
"@types/compression-webpack-plugin": "^9.0.0",
|
"@types/compression-webpack-plugin": "^9.0.0",
|
||||||
"@types/copy-webpack-plugin": "^8.0.1",
|
"@types/copy-webpack-plugin": "^8.0.1",
|
||||||
@ -155,6 +157,7 @@
|
|||||||
"portfinder-sync": "^0.0.2",
|
"portfinder-sync": "^0.0.2",
|
||||||
"prettier": "2.2.1",
|
"prettier": "2.2.1",
|
||||||
"react-hot-loader": "^4.13.0",
|
"react-hot-loader": "^4.13.0",
|
||||||
|
"ts-jest": "^27.1.4",
|
||||||
"ts-node": "^10.2.1",
|
"ts-node": "^10.2.1",
|
||||||
"typescript-plugin-css-modules": "^3.4.0",
|
"typescript-plugin-css-modules": "^3.4.0",
|
||||||
"webpack-bundle-analyzer": "^4.5.0",
|
"webpack-bundle-analyzer": "^4.5.0",
|
||||||
|
@ -1,3 +1,27 @@
|
|||||||
{
|
{
|
||||||
"monitor_signup": "Monitor your applications. Find what is causing issues."
|
"monitor_signup": "Monitor your applications. Find what is causing issues.",
|
||||||
|
"version": "Version",
|
||||||
|
"latest_version": "Latest version",
|
||||||
|
"current_version": "Current version",
|
||||||
|
"release_notes": "Release Notes",
|
||||||
|
"read_how_to_upgrade": "Read instructions on how to upgrade",
|
||||||
|
"latest_version_signoz": "You are running the latest version of SigNoz.",
|
||||||
|
"stale_version": "You are on an older version and may be loosing on the latest features we have shipped. We recommend to upgrade to the latest version",
|
||||||
|
"oops_something_went_wrong_version": "Oops.. facing issues with fetching updated version information",
|
||||||
|
"n_a": "N/A",
|
||||||
|
"routes": {
|
||||||
|
"general": "General",
|
||||||
|
"alert_channels": "Alert Channels"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"total_retention_period": "Total Retention Period",
|
||||||
|
"move_to_s3": "Move to S3\n(should be lower than total retention period)",
|
||||||
|
"retention_success_message": "Congrats. The retention periods for {{name}} has been updated successfully.",
|
||||||
|
"retention_error_message": "There was an issue in changing the retention period for {{name}}. Please try again or reach out to support@signoz.io",
|
||||||
|
"retention_failed_message": "There was an issue in changing the retention period. Please try again or reach out to support@signoz.io",
|
||||||
|
"retention_comparison_error": "Total retention period for {{name}} can’t be lower or equal to the period after which data is moved to s3.",
|
||||||
|
"retention_null_value_error": "Retention Period for {{name}} is not set yet. Please set by choosing below",
|
||||||
|
"retention_confirmation": "Are you sure you want to change the retention period?",
|
||||||
|
"retention_confirmation_description": "This will change the amount of storage needed for saving metrics & traces."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,3 +85,7 @@ export const EditAlertChannelsAlerts = Loadable(
|
|||||||
export const AllAlertChannels = Loadable(
|
export const AllAlertChannels = Loadable(
|
||||||
() => import(/* webpackChunkName: "All Channels" */ 'pages/AllAlertChannels'),
|
() => import(/* webpackChunkName: "All Channels" */ 'pages/AllAlertChannels'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const StatusPage = Loadable(
|
||||||
|
() => import(/* webpackChunkName: "All Status" */ 'pages/Status'),
|
||||||
|
);
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
ServicesTablePage,
|
ServicesTablePage,
|
||||||
SettingsPage,
|
SettingsPage,
|
||||||
SignupPage,
|
SignupPage,
|
||||||
|
StatusPage,
|
||||||
TraceDetail,
|
TraceDetail,
|
||||||
TraceFilter,
|
TraceFilter,
|
||||||
UsageExplorerPage,
|
UsageExplorerPage,
|
||||||
@ -113,6 +114,11 @@ const routes: AppRoutes[] = [
|
|||||||
exact: true,
|
exact: true,
|
||||||
component: AllAlertChannels,
|
component: AllAlertChannels,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: ROUTES.VERSION,
|
||||||
|
exact: true,
|
||||||
|
component: StatusPage,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
interface AppRoutes {
|
interface AppRoutes {
|
||||||
|
24
frontend/src/api/disks/getDisks.ts
Normal file
24
frontend/src/api/disks/getDisks.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import axios from 'api';
|
||||||
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
import { PayloadProps } from 'types/api/disks/getDisks';
|
||||||
|
|
||||||
|
const getDisks = async (): Promise<
|
||||||
|
SuccessResponse<PayloadProps> | ErrorResponse
|
||||||
|
> => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`/disks`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: 'Success',
|
||||||
|
payload: response.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler(error as AxiosError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getDisks;
|
@ -9,7 +9,11 @@ const setRetention = async (
|
|||||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post<PayloadProps>(
|
const response = await axios.post<PayloadProps>(
|
||||||
`/settings/ttl?duration=${props.duration}&type=${props.type}`,
|
`/settings/ttl?duration=${props.totalDuration}&type=${props.type}${
|
||||||
|
props.coldStorage
|
||||||
|
? `&coldStorage=${props.coldStorage};toColdDuration=${props.toColdDuration}`
|
||||||
|
: ''
|
||||||
|
}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
28
frontend/src/api/trace/getTagValue.ts
Normal file
28
frontend/src/api/trace/getTagValue.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
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/trace/getTagValue';
|
||||||
|
|
||||||
|
const getTagValue = async (
|
||||||
|
props: Props,
|
||||||
|
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||||
|
try {
|
||||||
|
const response = await axios.post<PayloadProps>(`/getTagValues`, {
|
||||||
|
start: props.start.toString(),
|
||||||
|
end: props.end.toString(),
|
||||||
|
tagKey: props.tagKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: 'Success',
|
||||||
|
payload: response.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler(error as AxiosError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getTagValue;
|
25
frontend/src/api/user/getLatestVersion.ts
Normal file
25
frontend/src/api/user/getLatestVersion.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
|
import axios, { AxiosError } from 'axios';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
import { PayloadProps } from 'types/api/user/getLatestVersion';
|
||||||
|
|
||||||
|
const getLatestVersion = async (): Promise<
|
||||||
|
SuccessResponse<PayloadProps> | ErrorResponse
|
||||||
|
> => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(
|
||||||
|
`https://api.github.com/repos/signoz/signoz/releases/latest`,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: response.data.status,
|
||||||
|
payload: response.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler(error as AxiosError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getLatestVersion;
|
17
frontend/src/components/Graph/Plugin/EmptyGraph.ts
Normal file
17
frontend/src/components/Graph/Plugin/EmptyGraph.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { grey } from '@ant-design/colors';
|
||||||
|
import { Chart } from 'chart.js';
|
||||||
|
|
||||||
|
export const emptyGraph = {
|
||||||
|
id: 'emptyChart',
|
||||||
|
afterDraw(chart: Chart): void {
|
||||||
|
const { height, width, ctx } = chart;
|
||||||
|
chart.clear();
|
||||||
|
ctx.save();
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.font = '1.5rem sans-serif';
|
||||||
|
ctx.fillStyle = `${grey.primary}`;
|
||||||
|
ctx.fillText('No data to display', width / 2, height / 2);
|
||||||
|
ctx.restore();
|
||||||
|
},
|
||||||
|
};
|
19
frontend/src/components/Graph/hasData.ts
Normal file
19
frontend/src/components/Graph/hasData.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/* eslint-disable no-restricted-syntax */
|
||||||
|
import { ChartData } from 'chart.js';
|
||||||
|
|
||||||
|
export const hasData = (data: ChartData): boolean => {
|
||||||
|
const { datasets = [] } = data;
|
||||||
|
let hasData = false;
|
||||||
|
try {
|
||||||
|
for (const dataset of datasets) {
|
||||||
|
if (dataset.data.length > 0 && dataset.data.some((item) => item !== 0)) {
|
||||||
|
hasData = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasData;
|
||||||
|
};
|
@ -27,7 +27,9 @@ import { useSelector } from 'react-redux';
|
|||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
|
import { hasData } from './hasData';
|
||||||
import { legend } from './Plugin';
|
import { legend } from './Plugin';
|
||||||
|
import { emptyGraph } from './Plugin/EmptyGraph';
|
||||||
import { LegendsContainer } from './styles';
|
import { LegendsContainer } from './styles';
|
||||||
import { useXAxisTimeUnit } from './xAxisConfig';
|
import { useXAxisTimeUnit } from './xAxisConfig';
|
||||||
import { getYAxisFormattedValue } from './yAxisConfig';
|
import { getYAxisFormattedValue } from './yAxisConfig';
|
||||||
@ -128,6 +130,7 @@ function Graph({
|
|||||||
grid: {
|
grid: {
|
||||||
display: true,
|
display: true,
|
||||||
color: getGridColor(),
|
color: getGridColor(),
|
||||||
|
drawTicks: true,
|
||||||
},
|
},
|
||||||
adapters: {
|
adapters: {
|
||||||
date: chartjsAdapter,
|
date: chartjsAdapter,
|
||||||
@ -180,12 +183,18 @@ function Graph({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const chartHasData = hasData(data);
|
||||||
|
const chartPlugins = [];
|
||||||
|
if (chartHasData) {
|
||||||
|
chartPlugins.push(legend(name, data.datasets.length > 3));
|
||||||
|
} else {
|
||||||
|
chartPlugins.push(emptyGraph);
|
||||||
|
}
|
||||||
lineChartRef.current = new Chart(chartRef.current, {
|
lineChartRef.current = new Chart(chartRef.current, {
|
||||||
type,
|
type,
|
||||||
data,
|
data,
|
||||||
options,
|
options,
|
||||||
plugins: [legend(name, data.datasets.length > 3)],
|
plugins: chartPlugins,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* @jest-environment jsdom
|
||||||
|
*/
|
||||||
|
|
||||||
import { expect } from '@jest/globals';
|
import { expect } from '@jest/globals';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
exports[`Not Found page test should render Not Found page without errors 1`] = `
|
exports[`Not Found page test should render Not Found page without errors 1`] = `
|
||||||
<DocumentFragment>
|
<DocumentFragment>
|
||||||
<div
|
<div
|
||||||
class="sc-gtsrHT VomVY"
|
class="sc-gsDKAQ cLXpIa"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
fill="none"
|
fill="none"
|
||||||
@ -272,21 +272,21 @@ exports[`Not Found page test should render Not Found page without errors 1`] = `
|
|||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
<div
|
<div
|
||||||
class="sc-hKFxyN dunFuJ"
|
class="sc-hKwDye foaleg"
|
||||||
>
|
>
|
||||||
<p
|
<p
|
||||||
class="sc-dlnjwi cydxLA"
|
class="sc-dkPtRN fcyVIq"
|
||||||
>
|
>
|
||||||
Ah, seems like we reached a dead end!
|
Ah, seems like we reached a dead end!
|
||||||
</p>
|
</p>
|
||||||
<p
|
<p
|
||||||
class="sc-dlnjwi cydxLA"
|
class="sc-dkPtRN fcyVIq"
|
||||||
>
|
>
|
||||||
Page Not Found
|
Page Not Found
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
class="sc-bdnxRM bYqcho"
|
class="sc-bdvvtL dbTZkj"
|
||||||
href="/application"
|
href="/application"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
|
@ -17,6 +17,7 @@ const ROUTES = {
|
|||||||
ALL_CHANNELS: '/settings/channels',
|
ALL_CHANNELS: '/settings/channels',
|
||||||
CHANNELS_NEW: '/setting/channels/new',
|
CHANNELS_NEW: '/setting/channels/new',
|
||||||
CHANNELS_EDIT: '/setting/channels/edit/:id',
|
CHANNELS_EDIT: '/setting/channels/edit/:id',
|
||||||
|
VERSION: '/status',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ROUTES;
|
export default ROUTES;
|
||||||
|
@ -1,11 +1,24 @@
|
|||||||
|
import { notification } from 'antd';
|
||||||
|
import getLatestVersion from 'api/user/getLatestVersion';
|
||||||
|
import getVersion from 'api/user/getVersion';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import TopNav from 'container/Header';
|
import TopNav from 'container/Header';
|
||||||
import SideNav from 'container/SideNav';
|
import SideNav from 'container/SideNav';
|
||||||
|
import useFetch from 'hooks/useFetch';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React, { ReactNode, useEffect, useState } from 'react';
|
import React, { ReactNode, useEffect, useRef, useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
|
import AppActions from 'types/actions';
|
||||||
|
import {
|
||||||
|
UPDATE_CURRENT_ERROR,
|
||||||
|
UPDATE_CURRENT_VERSION,
|
||||||
|
UPDATE_LATEST_VERSION,
|
||||||
|
UPDATE_LATEST_VERSION_ERROR,
|
||||||
|
} from 'types/actions/app';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
import { Content, Layout } from './styles';
|
import { Content, Layout } from './styles';
|
||||||
@ -13,11 +26,24 @@ import { Content, Layout } from './styles';
|
|||||||
function AppLayout(props: AppLayoutProps): JSX.Element {
|
function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||||
const { isLoggedIn } = useSelector<AppState, AppReducer>((state) => state.app);
|
const { isLoggedIn } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [isSignUpPage, setIsSignUpPage] = useState(ROUTES.SIGN_UP === pathname);
|
const [isSignUpPage, setIsSignUpPage] = useState(ROUTES.SIGN_UP === pathname);
|
||||||
|
|
||||||
|
const { payload: versionPayload, loading, error: getVersionError } = useFetch(
|
||||||
|
getVersion,
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
payload: latestVersionPayload,
|
||||||
|
loading: latestLoading,
|
||||||
|
error: latestError,
|
||||||
|
} = useFetch(getLatestVersion);
|
||||||
|
|
||||||
const { children } = props;
|
const { children } = props;
|
||||||
|
|
||||||
|
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isLoggedIn) {
|
if (!isLoggedIn) {
|
||||||
setIsSignUpPage(true);
|
setIsSignUpPage(true);
|
||||||
@ -27,6 +53,72 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
}
|
}
|
||||||
}, [isLoggedIn, isSignUpPage]);
|
}, [isLoggedIn, isSignUpPage]);
|
||||||
|
|
||||||
|
const latestCurrentCounter = useRef(0);
|
||||||
|
const latestVersionCounter = useRef(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isLoggedIn && pathname === ROUTES.SIGN_UP) {
|
||||||
|
history.push(ROUTES.APPLICATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!latestLoading && latestError && latestCurrentCounter.current === 0) {
|
||||||
|
latestCurrentCounter.current = 1;
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: UPDATE_LATEST_VERSION_ERROR,
|
||||||
|
payload: {
|
||||||
|
isError: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
notification.error({
|
||||||
|
message: t('oops_something_went_wrong_version'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loading && getVersionError && latestVersionCounter.current === 0) {
|
||||||
|
latestVersionCounter.current = 1;
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: UPDATE_CURRENT_ERROR,
|
||||||
|
payload: {
|
||||||
|
isError: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
notification.error({
|
||||||
|
message: t('oops_something_went_wrong_version'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!latestLoading && versionPayload) {
|
||||||
|
dispatch({
|
||||||
|
type: UPDATE_CURRENT_VERSION,
|
||||||
|
payload: {
|
||||||
|
currentVersion: versionPayload.version,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loading && latestVersionPayload) {
|
||||||
|
dispatch({
|
||||||
|
type: UPDATE_LATEST_VERSION,
|
||||||
|
payload: {
|
||||||
|
latestVersion: latestVersionPayload.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
dispatch,
|
||||||
|
loading,
|
||||||
|
latestLoading,
|
||||||
|
versionPayload,
|
||||||
|
latestVersionPayload,
|
||||||
|
isLoggedIn,
|
||||||
|
pathname,
|
||||||
|
getVersionError,
|
||||||
|
latestError,
|
||||||
|
t,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
{!isSignUpPage && <SideNav />}
|
{!isSignUpPage && <SideNav />}
|
||||||
|
@ -18,12 +18,14 @@ function CreateAlertChannels({
|
|||||||
preType = 'slack',
|
preType = 'slack',
|
||||||
}: CreateAlertChannelsProps): JSX.Element {
|
}: CreateAlertChannelsProps): JSX.Element {
|
||||||
const [formInstance] = Form.useForm();
|
const [formInstance] = Form.useForm();
|
||||||
|
|
||||||
const [selectedConfig, setSelectedConfig] = useState<
|
const [selectedConfig, setSelectedConfig] = useState<
|
||||||
Partial<SlackChannel & WebhookChannel>
|
Partial<SlackChannel & WebhookChannel>
|
||||||
>({
|
>({
|
||||||
text: ` {{ range .Alerts -}}
|
text: `{{ range .Alerts -}}
|
||||||
*Alert:* {{ .Annotations.title }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }}
|
*Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }}
|
||||||
|
|
||||||
|
*Summary:* {{ .Annotations.summary }}
|
||||||
*Description:* {{ .Annotations.description }}
|
*Description:* {{ .Annotations.description }}
|
||||||
|
|
||||||
*Details:*
|
*Details:*
|
||||||
|
@ -1,108 +1,118 @@
|
|||||||
import { DownOutlined } from '@ant-design/icons';
|
import { Col, Row, Select } from 'antd';
|
||||||
import { Button, Menu } from 'antd';
|
import { find } from 'lodash-es';
|
||||||
import React from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { SettingPeroid } from '.';
|
|
||||||
import {
|
import {
|
||||||
Dropdown,
|
|
||||||
Input,
|
Input,
|
||||||
RetentionContainer,
|
RetentionContainer,
|
||||||
TextContainer,
|
RetentionFieldInputContainer,
|
||||||
Typography,
|
RetentionFieldLabel,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
|
import {
|
||||||
|
convertHoursValueToRelevantUnit,
|
||||||
|
SettingPeriod,
|
||||||
|
TimeUnits,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
|
const { Option } = Select;
|
||||||
|
|
||||||
function Retention({
|
function Retention({
|
||||||
retentionValue,
|
retentionValue,
|
||||||
setRentionValue,
|
setRetentionValue,
|
||||||
selectedRetentionPeroid,
|
|
||||||
setSelectedRetentionPeroid,
|
|
||||||
text,
|
text,
|
||||||
}: RetentionProps): JSX.Element {
|
hide,
|
||||||
const options: Option[] = [
|
}: RetentionProps): JSX.Element | null {
|
||||||
{
|
const {
|
||||||
key: 'hr',
|
value: initialValue,
|
||||||
value: 'Hrs',
|
timeUnitValue: initialTimeUnitValue,
|
||||||
},
|
} = convertHoursValueToRelevantUnit(Number(retentionValue));
|
||||||
{
|
const [selectedTimeUnit, setSelectTimeUnit] = useState(initialTimeUnitValue);
|
||||||
key: 'day',
|
const [selectedValue, setSelectedValue] = useState<number | null>(
|
||||||
value: 'Days',
|
initialValue,
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'month',
|
|
||||||
value: 'Months',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const onClickHandler = (
|
|
||||||
e: { key: string },
|
|
||||||
func: React.Dispatch<React.SetStateAction<SettingPeroid>>,
|
|
||||||
): void => {
|
|
||||||
const selected = e.key as SettingPeroid;
|
|
||||||
func(selected);
|
|
||||||
};
|
|
||||||
|
|
||||||
const menu = (
|
|
||||||
<Menu onClick={(e): void => onClickHandler(e, setSelectedRetentionPeroid)}>
|
|
||||||
{options.map((option) => (
|
|
||||||
<Menu.Item key={option.key}>{option.value}</Menu.Item>
|
|
||||||
))}
|
|
||||||
</Menu>
|
|
||||||
);
|
);
|
||||||
|
const interacted = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!interacted.current) setSelectedValue(initialValue);
|
||||||
|
}, [initialValue]);
|
||||||
|
|
||||||
const currentSelectedOption = (option: SettingPeroid): string | undefined => {
|
useEffect(() => {
|
||||||
return options.find((e) => e.key === option)?.value;
|
if (!interacted.current) setSelectTimeUnit(initialTimeUnitValue);
|
||||||
|
}, [initialTimeUnitValue]);
|
||||||
|
|
||||||
|
const menuItems = TimeUnits.map((option) => (
|
||||||
|
<Option key={option.value} value={option.value}>
|
||||||
|
{option.key}
|
||||||
|
</Option>
|
||||||
|
));
|
||||||
|
|
||||||
|
const currentSelectedOption = (option: SettingPeriod): void => {
|
||||||
|
const selectedValue = find(TimeUnits, (e) => e.value === option)?.value;
|
||||||
|
if (selectedValue) setSelectTimeUnit(selectedValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const inverseMultiplier = find(
|
||||||
|
TimeUnits,
|
||||||
|
(timeUnit) => timeUnit.value === selectedTimeUnit,
|
||||||
|
)?.multiplier;
|
||||||
|
if (!selectedValue) setRetentionValue(null);
|
||||||
|
if (selectedValue && inverseMultiplier) {
|
||||||
|
setRetentionValue(selectedValue * (1 / inverseMultiplier));
|
||||||
|
}
|
||||||
|
}, [selectedTimeUnit, selectedValue, setRetentionValue]);
|
||||||
|
|
||||||
const onChangeHandler = (
|
const onChangeHandler = (
|
||||||
e: React.ChangeEvent<HTMLInputElement>,
|
e: React.ChangeEvent<HTMLInputElement>,
|
||||||
func: React.Dispatch<React.SetStateAction<string>>,
|
func: React.Dispatch<React.SetStateAction<number | null>>,
|
||||||
): void => {
|
): void => {
|
||||||
|
interacted.current = true;
|
||||||
const { value } = e.target;
|
const { value } = e.target;
|
||||||
const integerValue = parseInt(value, 10);
|
const integerValue = parseInt(value, 10);
|
||||||
|
|
||||||
if (value.length > 0 && integerValue.toString() === value) {
|
if (value.length > 0 && integerValue.toString() === value) {
|
||||||
const parsedValue = Math.abs(integerValue).toString();
|
const parsedValue = Math.abs(integerValue);
|
||||||
func(parsedValue);
|
func(parsedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.length === 0) {
|
if (value.length === 0) {
|
||||||
func('');
|
func(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if (hide) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<RetentionContainer>
|
<RetentionContainer>
|
||||||
<TextContainer>
|
<Row justify="space-between">
|
||||||
<Typography>{text}</Typography>
|
<Col flex={1} style={{ display: 'flex' }}>
|
||||||
</TextContainer>
|
<RetentionFieldLabel>{text}</RetentionFieldLabel>
|
||||||
|
</Col>
|
||||||
<Input
|
<Col flex="150px">
|
||||||
value={retentionValue}
|
<RetentionFieldInputContainer>
|
||||||
onChange={(e): void => onChangeHandler(e, setRentionValue)}
|
<Input
|
||||||
/>
|
value={selectedValue && selectedValue >= 0 ? selectedValue : ''}
|
||||||
|
onChange={(e): void => onChangeHandler(e, setSelectedValue)}
|
||||||
<Dropdown overlay={menu}>
|
style={{ width: 75 }}
|
||||||
<Button>
|
/>
|
||||||
{currentSelectedOption(selectedRetentionPeroid)} <DownOutlined />
|
<Select
|
||||||
</Button>
|
value={selectedTimeUnit}
|
||||||
</Dropdown>
|
onChange={currentSelectedOption}
|
||||||
|
style={{ width: 100 }}
|
||||||
|
>
|
||||||
|
{menuItems}
|
||||||
|
</Select>
|
||||||
|
</RetentionFieldInputContainer>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
</RetentionContainer>
|
</RetentionContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Option {
|
|
||||||
key: SettingPeroid;
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RetentionProps {
|
interface RetentionProps {
|
||||||
retentionValue: string;
|
retentionValue: number | null;
|
||||||
text: string;
|
text: string;
|
||||||
setRentionValue: React.Dispatch<React.SetStateAction<string>>;
|
setRetentionValue: React.Dispatch<React.SetStateAction<number | null>>;
|
||||||
selectedRetentionPeroid: SettingPeroid;
|
hide: boolean;
|
||||||
setSelectedRetentionPeroid: React.Dispatch<
|
|
||||||
React.SetStateAction<SettingPeroid>
|
|
||||||
>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Retention;
|
export default Retention;
|
||||||
|
@ -1,43 +1,73 @@
|
|||||||
import { Button, Modal, notification, Typography } from 'antd';
|
/* eslint-disable sonarjs/cognitive-complexity */
|
||||||
import getRetentionperoidApi from 'api/settings/getRetention';
|
import { Button, Col, Modal, notification, Row, Typography } from 'antd';
|
||||||
|
import getDisks from 'api/disks/getDisks';
|
||||||
|
import getRetentionPeriodApi from 'api/settings/getRetention';
|
||||||
import setRetentionApi from 'api/settings/setRetention';
|
import setRetentionApi from 'api/settings/setRetention';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import TextToolTip from 'components/TextToolTip';
|
import TextToolTip from 'components/TextToolTip';
|
||||||
import useFetch from 'hooks/useFetch';
|
import useFetch from 'hooks/useFetch';
|
||||||
import convertIntoHr from 'lib/convertIntoHr';
|
import { find } from 'lodash-es';
|
||||||
import getSettingsPeroid from 'lib/getSettingsPeroid';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { IDiskType } from 'types/api/disks/getDisks';
|
||||||
import { PayloadProps } from 'types/api/settings/getRetention';
|
import { PayloadProps } from 'types/api/settings/getRetention';
|
||||||
|
|
||||||
import Retention from './Retention';
|
import Retention from './Retention';
|
||||||
import {
|
import { ButtonContainer, ErrorText, ErrorTextContainer } from './styles';
|
||||||
ButtonContainer,
|
|
||||||
Container,
|
|
||||||
ErrorText,
|
|
||||||
ErrorTextContainer,
|
|
||||||
ToolTipContainer,
|
|
||||||
} from './styles';
|
|
||||||
|
|
||||||
function GeneralSettings(): JSX.Element {
|
function GeneralSettings(): JSX.Element {
|
||||||
const [
|
const { t } = useTranslation();
|
||||||
selectedMetricsPeroid,
|
|
||||||
setSelectedMetricsPeroid,
|
|
||||||
] = useState<SettingPeroid>('month');
|
|
||||||
const [notifications, Element] = notification.useNotification();
|
const [notifications, Element] = notification.useNotification();
|
||||||
|
|
||||||
const [retentionPeroidMetrics, setRetentionPeroidMetrics] = useState<string>(
|
|
||||||
'',
|
|
||||||
);
|
|
||||||
const [modal, setModal] = useState<boolean>(false);
|
const [modal, setModal] = useState<boolean>(false);
|
||||||
const [postApiLoading, setPostApiLoading] = useState<boolean>(false);
|
const [postApiLoading, setPostApiLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
const [selectedTracePeroid, setSelectedTracePeroid] = useState<SettingPeroid>(
|
const [availableDisks, setAvailableDisks] = useState<IDiskType[] | null>(null);
|
||||||
'hr',
|
|
||||||
);
|
|
||||||
|
|
||||||
const [retentionPeroidTrace, setRetentionPeroidTrace] = useState<string>('');
|
useEffect(() => {
|
||||||
const [isDefaultMetrics, setIsDefaultMetrics] = useState<boolean>(false);
|
getDisks().then((response) => setAvailableDisks(response.payload));
|
||||||
const [isDefaultTrace, setIsDefaultTrace] = useState<boolean>(false);
|
}, []);
|
||||||
|
|
||||||
|
const { payload, loading, error, errorMessage } = useFetch<
|
||||||
|
PayloadProps,
|
||||||
|
undefined
|
||||||
|
>(getRetentionPeriodApi, undefined);
|
||||||
|
|
||||||
|
const [currentTTLValues, setCurrentTTLValues] = useState(payload);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCurrentTTLValues(payload);
|
||||||
|
}, [payload]);
|
||||||
|
|
||||||
|
const [metricsTotalRetentionPeriod, setMetricsTotalRetentionPeriod] = useState<
|
||||||
|
number | null
|
||||||
|
>(null);
|
||||||
|
const [metricsS3RetentionPeriod, setMetricsS3RetentionPeriod] = useState<
|
||||||
|
number | null
|
||||||
|
>(null);
|
||||||
|
const [tracesTotalRetentionPeriod, setTracesTotalRetentionPeriod] = useState<
|
||||||
|
number | null
|
||||||
|
>(null);
|
||||||
|
const [tracesS3RetentionPeriod, setTracesS3RetentionPeriod] = useState<
|
||||||
|
number | null
|
||||||
|
>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (currentTTLValues) {
|
||||||
|
setMetricsTotalRetentionPeriod(currentTTLValues.metrics_ttl_duration_hrs);
|
||||||
|
setMetricsS3RetentionPeriod(
|
||||||
|
currentTTLValues.metrics_move_ttl_duration_hrs
|
||||||
|
? currentTTLValues.metrics_move_ttl_duration_hrs
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
setTracesTotalRetentionPeriod(currentTTLValues.traces_ttl_duration_hrs);
|
||||||
|
setTracesS3RetentionPeriod(
|
||||||
|
currentTTLValues.traces_move_ttl_duration_hrs
|
||||||
|
? currentTTLValues.traces_move_ttl_duration_hrs
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
console.log({ changed: currentTTLValues });
|
||||||
|
}, [currentTTLValues]);
|
||||||
|
|
||||||
const onModalToggleHandler = (): void => {
|
const onModalToggleHandler = (): void => {
|
||||||
setModal((modal) => !modal);
|
setModal((modal) => !modal);
|
||||||
@ -47,182 +77,236 @@ function GeneralSettings(): JSX.Element {
|
|||||||
onModalToggleHandler();
|
onModalToggleHandler();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { payload, loading, error, errorMessage } = useFetch<
|
const s3Enabled = useMemo(
|
||||||
PayloadProps,
|
() => !!find(availableDisks, (disks: IDiskType) => disks?.type === 's3'),
|
||||||
undefined
|
[availableDisks],
|
||||||
>(getRetentionperoidApi, undefined);
|
);
|
||||||
|
|
||||||
const checkMetricTraceDefault = (trace: number, metric: number): void => {
|
const renderConfig = [
|
||||||
if (metric === -1) {
|
{
|
||||||
setIsDefaultMetrics(true);
|
name: 'Metrics',
|
||||||
} else {
|
retentionFields: [
|
||||||
setIsDefaultMetrics(false);
|
{
|
||||||
|
name: t('settings.total_retention_period'),
|
||||||
|
value: metricsTotalRetentionPeriod,
|
||||||
|
setValue: setMetricsTotalRetentionPeriod,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t('settings.move_to_s3'),
|
||||||
|
value: metricsS3RetentionPeriod,
|
||||||
|
setValue: setMetricsS3RetentionPeriod,
|
||||||
|
hide: !s3Enabled,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Traces',
|
||||||
|
retentionFields: [
|
||||||
|
{
|
||||||
|
name: t('settings.total_retention_period'),
|
||||||
|
value: tracesTotalRetentionPeriod,
|
||||||
|
setValue: setTracesTotalRetentionPeriod,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t('settings.move_to_s3'),
|
||||||
|
value: tracesS3RetentionPeriod,
|
||||||
|
setValue: setTracesS3RetentionPeriod,
|
||||||
|
hide: !s3Enabled,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
].map((category): JSX.Element | null => {
|
||||||
|
if (
|
||||||
|
Array.isArray(category.retentionFields) &&
|
||||||
|
category.retentionFields.length > 0
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<Col flex="40%" style={{ minWidth: 475 }} key={category.name}>
|
||||||
|
<Typography.Title level={3}>{category.name}</Typography.Title>
|
||||||
|
|
||||||
|
{category.retentionFields.map((retentionField) => (
|
||||||
|
<Retention
|
||||||
|
key={retentionField.name}
|
||||||
|
text={retentionField.name}
|
||||||
|
retentionValue={retentionField.value}
|
||||||
|
setRetentionValue={retentionField.setValue}
|
||||||
|
hide={!!retentionField.hide}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Col>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
if (trace === -1) {
|
});
|
||||||
setIsDefaultTrace(true);
|
|
||||||
} else {
|
|
||||||
setIsDefaultTrace(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!loading && payload !== undefined) {
|
|
||||||
const {
|
|
||||||
metrics_ttl_duration_hrs: metricTllDuration,
|
|
||||||
traces_ttl_duration_hrs: traceTllDuration,
|
|
||||||
} = payload;
|
|
||||||
|
|
||||||
checkMetricTraceDefault(traceTllDuration, metricTllDuration);
|
|
||||||
|
|
||||||
const traceValue = getSettingsPeroid(traceTllDuration);
|
|
||||||
const metricsValue = getSettingsPeroid(metricTllDuration);
|
|
||||||
|
|
||||||
setRetentionPeroidTrace(traceValue.value.toString());
|
|
||||||
setSelectedTracePeroid(traceValue.peroid);
|
|
||||||
|
|
||||||
setRetentionPeroidMetrics(metricsValue.value.toString());
|
|
||||||
setSelectedMetricsPeroid(metricsValue.peroid);
|
|
||||||
}
|
|
||||||
}, [setSelectedMetricsPeroid, loading, payload]);
|
|
||||||
|
|
||||||
const onOkHandler = async (): Promise<void> => {
|
const onOkHandler = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
setPostApiLoading(true);
|
setPostApiLoading(true);
|
||||||
const retentionTraceValue =
|
const apiCalls = [];
|
||||||
retentionPeroidTrace === '0' && (payload?.traces_ttl_duration_hrs || 0) < 0
|
|
||||||
? payload?.traces_ttl_duration_hrs || 0
|
|
||||||
: parseInt(retentionPeroidTrace, 10);
|
|
||||||
|
|
||||||
const retentionMetricsValue =
|
|
||||||
retentionPeroidMetrics === '0' &&
|
|
||||||
(payload?.metrics_ttl_duration_hrs || 0) < 0
|
|
||||||
? payload?.metrics_ttl_duration_hrs || 0
|
|
||||||
: parseInt(retentionPeroidMetrics, 10);
|
|
||||||
|
|
||||||
const [tracesResponse, metricsResponse] = await Promise.all([
|
|
||||||
setRetentionApi({
|
|
||||||
duration: `${convertIntoHr(retentionTraceValue, selectedTracePeroid)}h`,
|
|
||||||
type: 'traces',
|
|
||||||
}),
|
|
||||||
setRetentionApi({
|
|
||||||
duration: `${convertIntoHr(
|
|
||||||
retentionMetricsValue,
|
|
||||||
selectedMetricsPeroid,
|
|
||||||
)}h`,
|
|
||||||
type: 'metrics',
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
tracesResponse.statusCode === 200 &&
|
!(
|
||||||
metricsResponse.statusCode === 200
|
currentTTLValues?.metrics_move_ttl_duration_hrs ===
|
||||||
|
metricsS3RetentionPeriod &&
|
||||||
|
currentTTLValues.metrics_ttl_duration_hrs === metricsTotalRetentionPeriod
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
notifications.success({
|
apiCalls.push(() =>
|
||||||
message: 'Success!',
|
setRetentionApi({
|
||||||
placement: 'topRight',
|
type: 'metrics',
|
||||||
description: 'Congrats. The retention periods were updated correctly.',
|
totalDuration: `${metricsTotalRetentionPeriod || -1}h`,
|
||||||
});
|
coldStorage: s3Enabled ? 's3' : null,
|
||||||
|
toColdDuration: `${metricsS3RetentionPeriod || -1}h`,
|
||||||
checkMetricTraceDefault(retentionTraceValue, retentionMetricsValue);
|
}),
|
||||||
|
);
|
||||||
onModalToggleHandler();
|
|
||||||
} else {
|
} else {
|
||||||
notifications.error({
|
apiCalls.push(() => Promise.resolve(null));
|
||||||
message: 'Error',
|
|
||||||
description:
|
|
||||||
'There was an issue in changing the retention period. Please try again or reach out to support@signoz.io',
|
|
||||||
placement: 'topRight',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
currentTTLValues?.traces_move_ttl_duration_hrs ===
|
||||||
|
tracesS3RetentionPeriod &&
|
||||||
|
currentTTLValues.traces_ttl_duration_hrs === tracesTotalRetentionPeriod
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
apiCalls.push(() =>
|
||||||
|
setRetentionApi({
|
||||||
|
type: 'traces',
|
||||||
|
totalDuration: `${tracesTotalRetentionPeriod || -1}h`,
|
||||||
|
coldStorage: s3Enabled ? 's3' : null,
|
||||||
|
toColdDuration: `${tracesS3RetentionPeriod || -1}h`,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
apiCalls.push(() => Promise.resolve(null));
|
||||||
|
}
|
||||||
|
const apiCallSequence = ['metrics', 'traces'];
|
||||||
|
const apiResponses = await Promise.all(apiCalls.map((api) => api()));
|
||||||
|
|
||||||
|
apiResponses.forEach((apiResponse, idx) => {
|
||||||
|
const name = apiCallSequence[idx];
|
||||||
|
if (apiResponse) {
|
||||||
|
if (apiResponse.statusCode === 200) {
|
||||||
|
notifications.success({
|
||||||
|
message: 'Success!',
|
||||||
|
placement: 'topRight',
|
||||||
|
|
||||||
|
description: t('settings.retention_success_message', { name }),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: t('settings.retention_error_message', { name }),
|
||||||
|
placement: 'topRight',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
onModalToggleHandler();
|
||||||
setPostApiLoading(false);
|
setPostApiLoading(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Error',
|
message: 'Error',
|
||||||
description:
|
description: t('settings.retention_failed_message'),
|
||||||
'There was an issue in changing the retention period. Please try again or reach out to support@signoz.io',
|
|
||||||
placement: 'topRight',
|
placement: 'topRight',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// Updates the currentTTL Values in order to avoid pushing the same values.
|
||||||
|
setCurrentTTLValues({
|
||||||
|
metrics_ttl_duration_hrs: metricsTotalRetentionPeriod || -1,
|
||||||
|
metrics_move_ttl_duration_hrs: metricsS3RetentionPeriod || -1,
|
||||||
|
traces_ttl_duration_hrs: tracesTotalRetentionPeriod || -1,
|
||||||
|
traces_move_ttl_duration_hrs: tracesS3RetentionPeriod || -1,
|
||||||
|
});
|
||||||
|
|
||||||
|
setModal(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [isDisabled, errorText] = useMemo((): [boolean, string] => {
|
||||||
|
// Various methods to return dynamic error message text.
|
||||||
|
const messages = {
|
||||||
|
compareError: (name: string | number): string =>
|
||||||
|
t('settings.retention_comparison_error', { name }),
|
||||||
|
nullValueError: (name: string | number): string =>
|
||||||
|
t('settings.retention_null_value_error', { name }),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Defaults to button not disabled and empty error message text.
|
||||||
|
let isDisabled = false;
|
||||||
|
let errorText = '';
|
||||||
|
|
||||||
|
if (s3Enabled) {
|
||||||
|
if (
|
||||||
|
(metricsTotalRetentionPeriod || metricsS3RetentionPeriod) &&
|
||||||
|
Number(metricsTotalRetentionPeriod) <= Number(metricsS3RetentionPeriod)
|
||||||
|
) {
|
||||||
|
isDisabled = true;
|
||||||
|
errorText = messages.compareError('metrics');
|
||||||
|
} else if (
|
||||||
|
(tracesTotalRetentionPeriod || tracesS3RetentionPeriod) &&
|
||||||
|
Number(tracesTotalRetentionPeriod) <= Number(tracesS3RetentionPeriod)
|
||||||
|
) {
|
||||||
|
isDisabled = true;
|
||||||
|
errorText = messages.compareError('traces');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!metricsTotalRetentionPeriod || !tracesTotalRetentionPeriod) {
|
||||||
|
isDisabled = true;
|
||||||
|
if (!metricsTotalRetentionPeriod && !tracesTotalRetentionPeriod) {
|
||||||
|
errorText = messages.nullValueError('metrics and traces');
|
||||||
|
} else if (!metricsTotalRetentionPeriod) {
|
||||||
|
errorText = messages.nullValueError('metrics');
|
||||||
|
} else if (!tracesTotalRetentionPeriod) {
|
||||||
|
errorText = messages.nullValueError('traces');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
currentTTLValues?.metrics_ttl_duration_hrs === metricsTotalRetentionPeriod &&
|
||||||
|
currentTTLValues.metrics_move_ttl_duration_hrs ===
|
||||||
|
metricsS3RetentionPeriod &&
|
||||||
|
currentTTLValues.traces_ttl_duration_hrs === tracesTotalRetentionPeriod &&
|
||||||
|
currentTTLValues.traces_move_ttl_duration_hrs === tracesS3RetentionPeriod
|
||||||
|
) {
|
||||||
|
isDisabled = true;
|
||||||
|
}
|
||||||
|
return [isDisabled, errorText];
|
||||||
|
}, [
|
||||||
|
currentTTLValues,
|
||||||
|
metricsS3RetentionPeriod,
|
||||||
|
metricsTotalRetentionPeriod,
|
||||||
|
s3Enabled,
|
||||||
|
t,
|
||||||
|
tracesS3RetentionPeriod,
|
||||||
|
tracesTotalRetentionPeriod,
|
||||||
|
]);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return <Typography>{errorMessage}</Typography>;
|
return <Typography>{errorMessage}</Typography>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loading || payload === undefined) {
|
if (loading || currentTTLValues === undefined) {
|
||||||
return <Spinner tip="Loading.." height="70vh" />;
|
return <Spinner tip="Loading.." height="70vh" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getErrorText = (): string => {
|
|
||||||
const getValue = (value: string): string =>
|
|
||||||
`Retention Peroid for ${value} is not set yet. Please set by choosing below`;
|
|
||||||
|
|
||||||
if (!isDefaultMetrics && !isDefaultTrace) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDefaultMetrics && !isDefaultTrace) {
|
|
||||||
return `${getValue('Metrics')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isDefaultMetrics && isDefaultTrace) {
|
|
||||||
return `${getValue('Trace')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${getValue('Trace , Metrics')}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const isDisabledHandler = (): boolean => {
|
|
||||||
return !!(retentionPeroidTrace === '' || retentionPeroidMetrics === '');
|
|
||||||
};
|
|
||||||
|
|
||||||
const errorText = getErrorText();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Col xs={24} md={22} xl={20} xxl={18} style={{ margin: 'auto' }}>
|
||||||
{Element}
|
{Element}
|
||||||
|
<ErrorTextContainer>
|
||||||
|
<TextToolTip
|
||||||
|
{...{
|
||||||
|
text: `More details on how to set retention period`,
|
||||||
|
url: 'https://signoz.io/docs/userguide/retention-period/',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{errorText && <ErrorText>{errorText}</ErrorText>}
|
||||||
|
</ErrorTextContainer>
|
||||||
|
|
||||||
{errorText ? (
|
<Row justify="space-around">{renderConfig}</Row>
|
||||||
<ErrorTextContainer>
|
|
||||||
<ErrorText>{errorText}</ErrorText>
|
|
||||||
|
|
||||||
<TextToolTip
|
|
||||||
{...{
|
|
||||||
text: `More details on how to set retention period`,
|
|
||||||
url: 'https://signoz.io/docs/userguide/retention-period/',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ErrorTextContainer>
|
|
||||||
) : (
|
|
||||||
<ToolTipContainer>
|
|
||||||
<TextToolTip
|
|
||||||
{...{
|
|
||||||
text: `More details on how to set retention period`,
|
|
||||||
url: 'https://signoz.io/docs/userguide/retention-period/',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ToolTipContainer>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Retention
|
|
||||||
text="Retention Period for Metrics"
|
|
||||||
selectedRetentionPeroid={selectedMetricsPeroid}
|
|
||||||
setRentionValue={setRetentionPeroidMetrics}
|
|
||||||
retentionValue={retentionPeroidMetrics}
|
|
||||||
setSelectedRetentionPeroid={setSelectedMetricsPeroid}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Retention
|
|
||||||
text="Retention Period for Traces"
|
|
||||||
selectedRetentionPeroid={selectedTracePeroid}
|
|
||||||
setRentionValue={setRetentionPeroidTrace}
|
|
||||||
retentionValue={retentionPeroidTrace}
|
|
||||||
setSelectedRetentionPeroid={setSelectedTracePeroid}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
title="Are you sure you want to change the retention period?"
|
title={t('settings.retention_confirmation')}
|
||||||
focusTriggerAfterClose
|
focusTriggerAfterClose
|
||||||
forceRender
|
forceRender
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
@ -233,24 +317,18 @@ function GeneralSettings(): JSX.Element {
|
|||||||
visible={modal}
|
visible={modal}
|
||||||
confirmLoading={postApiLoading}
|
confirmLoading={postApiLoading}
|
||||||
>
|
>
|
||||||
<Typography>
|
<Typography>{t('settings.retention_confirmation_description')}</Typography>
|
||||||
This will change the amount of storage needed for saving metrics & traces.
|
|
||||||
</Typography>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
<Button
|
<Button onClick={onClickSaveHandler} disabled={isDisabled} type="primary">
|
||||||
onClick={onClickSaveHandler}
|
|
||||||
disabled={isDisabledHandler()}
|
|
||||||
type="primary"
|
|
||||||
>
|
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
</Container>
|
</Col>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SettingPeroid = 'hr' | 'day' | 'month';
|
export type SettingPeriod = 'hr' | 'day' | 'month';
|
||||||
|
|
||||||
export default GeneralSettings;
|
export default GeneralSettings;
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
import {
|
import {
|
||||||
|
Col,
|
||||||
Dropdown as DropDownComponent,
|
Dropdown as DropDownComponent,
|
||||||
Input as InputComponent,
|
Input as InputComponent,
|
||||||
Typography as TypographyComponent,
|
Typography as TypographyComponent,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const RetentionContainer = styled.div`
|
export const RetentionContainer = styled(Col)`
|
||||||
width: 50%;
|
margin: 0.75rem 0;
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Input = styled(InputComponent)`
|
export const Input = styled(InputComponent)`
|
||||||
@ -37,13 +34,6 @@ export const ButtonContainer = styled.div`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Container = styled.div`
|
|
||||||
&&& {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const Dropdown = styled(DropDownComponent)`
|
export const Dropdown = styled(DropDownComponent)`
|
||||||
&&& {
|
&&& {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -66,6 +56,7 @@ export const ErrorTextContainer = styled.div`
|
|||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
|
||||||
> article {
|
> article {
|
||||||
margin-right: 1rem;
|
margin-right: 1rem;
|
||||||
@ -90,3 +81,12 @@ export const ErrorText = styled(TypographyComponent)`
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const RetentionFieldLabel = styled(TypographyComponent)`
|
||||||
|
vertical-align: middle;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const RetentionFieldInputContainer = styled.div`
|
||||||
|
display: inline-flex;
|
||||||
|
`;
|
||||||
|
42
frontend/src/container/GeneralSettings/utils.ts
Normal file
42
frontend/src/container/GeneralSettings/utils.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
export type SettingPeriod = 'hr' | 'day' | 'month';
|
||||||
|
|
||||||
|
export interface ITimeUnit {
|
||||||
|
value: SettingPeriod;
|
||||||
|
key: string;
|
||||||
|
multiplier: number;
|
||||||
|
}
|
||||||
|
export const TimeUnits: ITimeUnit[] = [
|
||||||
|
{
|
||||||
|
value: 'hr',
|
||||||
|
key: 'Hours',
|
||||||
|
multiplier: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'day',
|
||||||
|
key: 'Days',
|
||||||
|
multiplier: 1 / 24,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'month',
|
||||||
|
key: 'Months',
|
||||||
|
multiplier: 1 / (24 * 30),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const convertHoursValueToRelevantUnit = (
|
||||||
|
value: number,
|
||||||
|
): { value: number; timeUnitValue: SettingPeriod } => {
|
||||||
|
if (value)
|
||||||
|
for (let idx = TimeUnits.length - 1; idx >= 0; idx -= 1) {
|
||||||
|
const timeUnit = TimeUnits[idx];
|
||||||
|
const convertedValue = timeUnit.multiplier * value;
|
||||||
|
|
||||||
|
if (
|
||||||
|
convertedValue >= 1 &&
|
||||||
|
convertedValue === parseInt(`${convertedValue}`, 10)
|
||||||
|
) {
|
||||||
|
return { value: convertedValue, timeUnitValue: timeUnit.value };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { value, timeUnitValue: TimeUnits[0].value };
|
||||||
|
};
|
@ -1,91 +0,0 @@
|
|||||||
import Graph, { GraphOnClickHandler } from 'components/Graph';
|
|
||||||
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
|
|
||||||
import GetMaxMinTime from 'lib/getMaxMinTime';
|
|
||||||
import { colors } from 'lib/getRandomColor';
|
|
||||||
import getStartAndEndTime from 'lib/getStartAndEndTime';
|
|
||||||
import getTimeString from 'lib/getTimeString';
|
|
||||||
import React, { useCallback } from 'react';
|
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { AppState } from 'store/reducers';
|
|
||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
|
||||||
|
|
||||||
function EmptyGraph({
|
|
||||||
selectedTime,
|
|
||||||
widget,
|
|
||||||
onClickHandler,
|
|
||||||
}: EmptyGraphProps): JSX.Element {
|
|
||||||
const { minTime, maxTime, loading } = useSelector<AppState, GlobalReducer>(
|
|
||||||
(state) => state.globalTime,
|
|
||||||
);
|
|
||||||
|
|
||||||
const maxMinTime = GetMaxMinTime({
|
|
||||||
graphType: widget.panelTypes,
|
|
||||||
maxTime,
|
|
||||||
minTime,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { end, start } = getStartAndEndTime({
|
|
||||||
type: selectedTime.enum,
|
|
||||||
maxTime: maxMinTime.maxTime,
|
|
||||||
minTime: maxMinTime.minTime,
|
|
||||||
});
|
|
||||||
|
|
||||||
const dateFunction = useCallback(() => {
|
|
||||||
if (!loading) {
|
|
||||||
const dates: Date[] = [];
|
|
||||||
|
|
||||||
const startString = getTimeString(start);
|
|
||||||
const endString = getTimeString(end);
|
|
||||||
|
|
||||||
const parsedStart = parseInt(startString, 10);
|
|
||||||
const parsedEnd = parseInt(endString, 10);
|
|
||||||
|
|
||||||
let startDate = parsedStart;
|
|
||||||
const endDate = parsedEnd;
|
|
||||||
|
|
||||||
while (endDate >= startDate) {
|
|
||||||
const newDate = new Date(startDate);
|
|
||||||
|
|
||||||
startDate += 20000;
|
|
||||||
|
|
||||||
dates.push(newDate);
|
|
||||||
}
|
|
||||||
return dates;
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}, [start, end, loading]);
|
|
||||||
|
|
||||||
const date = dateFunction();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Graph
|
|
||||||
name=""
|
|
||||||
{...{
|
|
||||||
type: 'line',
|
|
||||||
onClickHandler,
|
|
||||||
data: {
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
data: new Array(date?.length).fill(0),
|
|
||||||
borderColor: colors[0],
|
|
||||||
showLine: true,
|
|
||||||
borderWidth: 1.5,
|
|
||||||
spanGaps: true,
|
|
||||||
pointRadius: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
labels: date,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EmptyGraphProps {
|
|
||||||
selectedTime: timePreferance;
|
|
||||||
widget: Widgets;
|
|
||||||
onClickHandler: GraphOnClickHandler | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default EmptyGraph;
|
|
@ -23,14 +23,12 @@ import { AppState } from 'store/reducers';
|
|||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
import EmptyGraph from './EmptyGraph';
|
|
||||||
import { NotFoundContainer, TimeContainer } from './styles';
|
import { NotFoundContainer, TimeContainer } from './styles';
|
||||||
|
|
||||||
function FullView({
|
function FullView({
|
||||||
widget,
|
widget,
|
||||||
fullViewOptions = true,
|
fullViewOptions = true,
|
||||||
onClickHandler,
|
onClickHandler,
|
||||||
noDataGraph = false,
|
|
||||||
name,
|
name,
|
||||||
yAxisUnit,
|
yAxisUnit,
|
||||||
}: FullViewProps): JSX.Element {
|
}: FullViewProps): JSX.Element {
|
||||||
@ -166,38 +164,6 @@ function FullView({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.loading === false && state.payload.datasets.length === 0) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{fullViewOptions && (
|
|
||||||
<TimeContainer>
|
|
||||||
<TimePreference
|
|
||||||
{...{
|
|
||||||
selectedTime,
|
|
||||||
setSelectedTime,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Button onClick={onFetchDataHandler} type="primary">
|
|
||||||
Refresh
|
|
||||||
</Button>
|
|
||||||
</TimeContainer>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{noDataGraph ? (
|
|
||||||
<EmptyGraph
|
|
||||||
onClickHandler={onClickHandler}
|
|
||||||
widget={widget}
|
|
||||||
selectedTime={selectedTime}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<NotFoundContainer>
|
|
||||||
<Typography>No Data</Typography>
|
|
||||||
</NotFoundContainer>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{fullViewOptions && (
|
{fullViewOptions && (
|
||||||
@ -243,7 +209,6 @@ interface FullViewProps {
|
|||||||
widget: Widgets;
|
widget: Widgets;
|
||||||
fullViewOptions?: boolean;
|
fullViewOptions?: boolean;
|
||||||
onClickHandler?: GraphOnClickHandler;
|
onClickHandler?: GraphOnClickHandler;
|
||||||
noDataGraph?: boolean;
|
|
||||||
name: string;
|
name: string;
|
||||||
yAxisUnit?: string;
|
yAxisUnit?: string;
|
||||||
}
|
}
|
||||||
@ -251,7 +216,6 @@ interface FullViewProps {
|
|||||||
FullView.defaultProps = {
|
FullView.defaultProps = {
|
||||||
fullViewOptions: undefined,
|
fullViewOptions: undefined,
|
||||||
onClickHandler: undefined,
|
onClickHandler: undefined,
|
||||||
noDataGraph: undefined,
|
|
||||||
yAxisUnit: undefined,
|
yAxisUnit: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ const breadcrumbNameMap = {
|
|||||||
[ROUTES.INSTRUMENTATION]: 'Add instrumentation',
|
[ROUTES.INSTRUMENTATION]: 'Add instrumentation',
|
||||||
[ROUTES.SETTINGS]: 'Settings',
|
[ROUTES.SETTINGS]: 'Settings',
|
||||||
[ROUTES.DASHBOARD]: 'Dashboard',
|
[ROUTES.DASHBOARD]: 'Dashboard',
|
||||||
|
[ROUTES.VERSION]: 'Status',
|
||||||
};
|
};
|
||||||
|
|
||||||
function ShowBreadcrumbs(props: RouteComponentProps): JSX.Element {
|
function ShowBreadcrumbs(props: RouteComponentProps): JSX.Element {
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import React, { useCallback } from 'react';
|
import { ExclamationCircleOutlined } from '@ant-design/icons';
|
||||||
|
import { Modal } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { bindActionCreators, Dispatch } from 'redux';
|
import { bindActionCreators, Dispatch } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
@ -8,14 +10,29 @@ import AppActions from 'types/actions';
|
|||||||
import { Data } from '../index';
|
import { Data } from '../index';
|
||||||
import { TableLinkText } from './styles';
|
import { TableLinkText } from './styles';
|
||||||
|
|
||||||
function DeleteButton({ deleteDashboard, id }: DeleteButtonProps): JSX.Element {
|
const { confirm } = Modal;
|
||||||
const onClickHandler = useCallback(() => {
|
|
||||||
deleteDashboard({
|
|
||||||
uuid: id,
|
|
||||||
});
|
|
||||||
}, [id, deleteDashboard]);
|
|
||||||
|
|
||||||
return <TableLinkText onClick={onClickHandler}>Delete</TableLinkText>;
|
function DeleteButton({ deleteDashboard, id }: DeleteButtonProps): JSX.Element {
|
||||||
|
const openConfirmationDialog = (): void => {
|
||||||
|
confirm({
|
||||||
|
title: 'Do you really want to delete this dashboard?',
|
||||||
|
icon: <ExclamationCircleOutlined style={{ color: '#e42b35' }} />,
|
||||||
|
onOk() {
|
||||||
|
deleteDashboard({
|
||||||
|
uuid: id,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
okText: 'Delete',
|
||||||
|
okButtonProps: { danger: true },
|
||||||
|
centered: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableLinkText type="danger" onClick={openConfirmationDialog}>
|
||||||
|
Delete
|
||||||
|
</TableLinkText>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
|
@ -157,7 +157,7 @@ function ListOfAllDashboard(): JSX.Element {
|
|||||||
<TextToolTip
|
<TextToolTip
|
||||||
{...{
|
{...{
|
||||||
text: `More details on how to create dashboards`,
|
text: `More details on how to create dashboards`,
|
||||||
url: 'https://signoz.io/docs/userguide/metrics-dashboard',
|
url: 'https://signoz.io/docs/userguide/dashboards',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
history.replace(
|
history.replace(
|
||||||
`${
|
`${
|
||||||
ROUTES.TRACE
|
ROUTES.TRACE
|
||||||
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"],"status":["ok","error"]}&filterToFetchData=["duration","status","serviceName"]&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"]}&isSelectedFilterSkipped=true`,
|
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=[]&&isFilterExclude={"serviceName":false}&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
history.replace(
|
history.replace(
|
||||||
`${
|
`${
|
||||||
ROUTES.TRACE
|
ROUTES.TRACE
|
||||||
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"],"status":["error"]}&filterToFetchData=["duration","status","serviceName"]&userSelectedFilter={"status":["error"],"serviceName":["${servicename}"]}&isSelectedFilterSkipped=true`,
|
}?${urlParams.toString()}?selected={"serviceName":["${servicename}"],"status":["error"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=[]&isFilterExclude={"serviceName":false,"status":false}&userSelectedFilter={"serviceName":["${servicename}"],"status":["error"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -179,7 +179,6 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
<GraphContainer>
|
<GraphContainer>
|
||||||
<FullView
|
<FullView
|
||||||
name="request_per_sec"
|
name="request_per_sec"
|
||||||
noDataGraph
|
|
||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
onClickHandler={(event, element, chart, data): void => {
|
onClickHandler={(event, element, chart, data): void => {
|
||||||
onClickhandler(event, element, chart, data, 'Request');
|
onClickhandler(event, element, chart, data, 'Request');
|
||||||
@ -214,7 +213,6 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
<GraphContainer>
|
<GraphContainer>
|
||||||
<FullView
|
<FullView
|
||||||
name="error_percentage_%"
|
name="error_percentage_%"
|
||||||
noDataGraph
|
|
||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
onClickHandler={(ChartEvent, activeElements, chart, data): void => {
|
onClickHandler={(ChartEvent, activeElements, chart, data): void => {
|
||||||
onClickhandler(ChartEvent, activeElements, chart, data, 'Error');
|
onClickhandler(ChartEvent, activeElements, chart, data, 'Error');
|
||||||
|
@ -17,7 +17,6 @@ function DBCall({ getWidget }: DBCallProps): JSX.Element {
|
|||||||
<GraphContainer>
|
<GraphContainer>
|
||||||
<FullView
|
<FullView
|
||||||
name="database_call_rps"
|
name="database_call_rps"
|
||||||
noDataGraph
|
|
||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
@ -37,7 +36,6 @@ function DBCall({ getWidget }: DBCallProps): JSX.Element {
|
|||||||
<GraphContainer>
|
<GraphContainer>
|
||||||
<FullView
|
<FullView
|
||||||
name="database_call_avg_duration"
|
name="database_call_avg_duration"
|
||||||
noDataGraph
|
|
||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
|
@ -21,7 +21,6 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
|||||||
<FullView
|
<FullView
|
||||||
name="external_call_error_percentage"
|
name="external_call_error_percentage"
|
||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
noDataGraph
|
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
query: `max((sum(rate(signoz_external_call_latency_count{service_name="${servicename}", status_code="STATUS_CODE_ERROR"}[1m]) OR rate(signoz_external_call_latency_count{service_name="${servicename}", http_status_code=~"5.."}[1m]) OR vector(0)) by (http_url))*100/sum(rate(signoz_external_call_latency_count{service_name="${servicename}"}[1m])) by (http_url)) < 1000 OR vector(0)`,
|
query: `max((sum(rate(signoz_external_call_latency_count{service_name="${servicename}", status_code="STATUS_CODE_ERROR"}[1m]) OR rate(signoz_external_call_latency_count{service_name="${servicename}", http_status_code=~"5.."}[1m]) OR vector(0)) by (http_url))*100/sum(rate(signoz_external_call_latency_count{service_name="${servicename}"}[1m])) by (http_url)) < 1000 OR vector(0)`,
|
||||||
@ -40,7 +39,6 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
|||||||
<GraphContainer>
|
<GraphContainer>
|
||||||
<FullView
|
<FullView
|
||||||
name="external_call_duration"
|
name="external_call_duration"
|
||||||
noDataGraph
|
|
||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
@ -62,7 +60,6 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
|||||||
<GraphContainer>
|
<GraphContainer>
|
||||||
<FullView
|
<FullView
|
||||||
name="external_call_rps_by_address"
|
name="external_call_rps_by_address"
|
||||||
noDataGraph
|
|
||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
@ -81,7 +78,6 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
|||||||
<GraphTitle>External Call duration(by Address)</GraphTitle>
|
<GraphTitle>External Call duration(by Address)</GraphTitle>
|
||||||
<GraphContainer>
|
<GraphContainer>
|
||||||
<FullView
|
<FullView
|
||||||
noDataGraph
|
|
||||||
name="external_call_duration_by_address"
|
name="external_call_duration_by_address"
|
||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
|
@ -33,7 +33,7 @@ function TopEndpointsTable(props: TopEndpointsTableProps): JSX.Element {
|
|||||||
history.push(
|
history.push(
|
||||||
`${
|
`${
|
||||||
ROUTES.TRACE
|
ROUTES.TRACE
|
||||||
}?${urlParams.toString()}&selected={"status":["error","ok"],"serviceName":["${servicename}"],"operation":["${operation}"]}&filterToFetchData=["duration","status","serviceName","operation"]&isSelectedFilterSkipped=true&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"],"operation":["${operation}"]}&isSelectedFilterSkipped=true`,
|
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"],"operation":["${operation}"]}&filterToFetchData=["duration","status","serviceName","operation"]&spanAggregateCurrentPage=1&selectedTags=[]&&isFilterExclude={"serviceName":false,"operation":false}&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"],"operation":["${operation}"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ function Query({
|
|||||||
<TextToolTip
|
<TextToolTip
|
||||||
{...{
|
{...{
|
||||||
text: `More details on how to plot metrics graphs`,
|
text: `More details on how to plot metrics graphs`,
|
||||||
url: 'https://signoz.io/docs/userguide/prometheus-metrics/',
|
url: 'https://signoz.io/docs/userguide/send-metrics/#related-videos',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { CheckCircleTwoTone, WarningOutlined } from '@ant-design/icons';
|
||||||
import { Menu, Typography } from 'antd';
|
import { Menu, Typography } from 'antd';
|
||||||
import getLocalStorageKey from 'api/browser/localstorage/get';
|
import getLocalStorageKey from 'api/browser/localstorage/get';
|
||||||
import { IS_SIDEBAR_COLLAPSED } from 'constants/app';
|
import { IS_SIDEBAR_COLLAPSED } from 'constants/app';
|
||||||
@ -5,6 +6,7 @@ import ROUTES from 'constants/routes';
|
|||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import setTheme, { AppMode } from 'lib/theme/setTheme';
|
import setTheme, { AppMode } from 'lib/theme/setTheme';
|
||||||
import React, { useCallback, useLayoutEffect, useState } from 'react';
|
import React, { useCallback, useLayoutEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { connect, useDispatch, useSelector } from 'react-redux';
|
import { connect, useDispatch, useSelector } from 'react-redux';
|
||||||
import { NavLink, useLocation } from 'react-router-dom';
|
import { NavLink, useLocation } from 'react-router-dom';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
@ -19,11 +21,13 @@ import menus from './menuItems';
|
|||||||
import Slack from './Slack';
|
import Slack from './Slack';
|
||||||
import {
|
import {
|
||||||
Logo,
|
Logo,
|
||||||
|
RedDot,
|
||||||
Sider,
|
Sider,
|
||||||
SlackButton,
|
SlackButton,
|
||||||
SlackMenuItemContainer,
|
SlackMenuItemContainer,
|
||||||
ThemeSwitcherWrapper,
|
ThemeSwitcherWrapper,
|
||||||
ToggleButton,
|
ToggleButton,
|
||||||
|
VersionContainer,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
|
|
||||||
function SideNav({ toggleDarkMode }: Props): JSX.Element {
|
function SideNav({ toggleDarkMode }: Props): JSX.Element {
|
||||||
@ -31,9 +35,15 @@ function SideNav({ toggleDarkMode }: Props): JSX.Element {
|
|||||||
const [collapsed, setCollapsed] = useState<boolean>(
|
const [collapsed, setCollapsed] = useState<boolean>(
|
||||||
getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true',
|
getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true',
|
||||||
);
|
);
|
||||||
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
|
const {
|
||||||
|
isDarkMode,
|
||||||
|
currentVersion,
|
||||||
|
latestVersion,
|
||||||
|
isCurrentVersionError,
|
||||||
|
} = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
|
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
|
const { t } = useTranslation('');
|
||||||
|
|
||||||
const toggleTheme = useCallback(() => {
|
const toggleTheme = useCallback(() => {
|
||||||
const preMode: AppMode = isDarkMode ? 'lightMode' : 'darkMode';
|
const preMode: AppMode = isDarkMode ? 'lightMode' : 'darkMode';
|
||||||
@ -77,6 +87,38 @@ function SideNav({ toggleDarkMode }: Props): JSX.Element {
|
|||||||
window.open('https://signoz.io/slack', '_blank');
|
window.open('https://signoz.io/slack', '_blank');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onClickVersionHandler = (): void => {
|
||||||
|
history.push(ROUTES.VERSION);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isNotCurrentVersion = currentVersion !== latestVersion;
|
||||||
|
|
||||||
|
const sidebar = [
|
||||||
|
{
|
||||||
|
onClick: onClickSlackHandler,
|
||||||
|
icon: <Slack />,
|
||||||
|
text: <SlackButton>Support</SlackButton>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onClick: onClickVersionHandler,
|
||||||
|
icon: isNotCurrentVersion ? (
|
||||||
|
<WarningOutlined style={{ color: '#E87040' }} />
|
||||||
|
) : (
|
||||||
|
<CheckCircleTwoTone twoToneColor={['#D5F2BB', '#1f1f1f']} />
|
||||||
|
),
|
||||||
|
text: (
|
||||||
|
<VersionContainer>
|
||||||
|
{!isCurrentVersionError ? (
|
||||||
|
<SlackButton>{currentVersion}</SlackButton>
|
||||||
|
) : (
|
||||||
|
<SlackButton>{t('n_a')}</SlackButton>
|
||||||
|
)}
|
||||||
|
{isNotCurrentVersion && <RedDot />}
|
||||||
|
</VersionContainer>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sider collapsible collapsed={collapsed} onCollapse={onCollapse} width={200}>
|
<Sider collapsible collapsed={collapsed} onCollapse={onCollapse} width={200}>
|
||||||
<ThemeSwitcherWrapper>
|
<ThemeSwitcherWrapper>
|
||||||
@ -87,7 +129,7 @@ function SideNav({ toggleDarkMode }: Props): JSX.Element {
|
|||||||
/>
|
/>
|
||||||
</ThemeSwitcherWrapper>
|
</ThemeSwitcherWrapper>
|
||||||
<NavLink to={ROUTES.APPLICATION}>
|
<NavLink to={ROUTES.APPLICATION}>
|
||||||
<Logo src="/signoz.svg" alt="SigNoz" collapsed={collapsed} />
|
<Logo index={0} src="/signoz.svg" alt="SigNoz" collapsed={collapsed} />
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
||||||
<Menu
|
<Menu
|
||||||
@ -105,11 +147,21 @@ function SideNav({ toggleDarkMode }: Props): JSX.Element {
|
|||||||
<Typography>{name}</Typography>
|
<Typography>{name}</Typography>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
))}
|
))}
|
||||||
<SlackMenuItemContainer collapsed={collapsed}>
|
{sidebar.map((props, index) => (
|
||||||
<Menu.Item onClick={onClickSlackHandler} icon={<Slack />}>
|
<SlackMenuItemContainer
|
||||||
<SlackButton>Support</SlackButton>
|
index={index + 1}
|
||||||
</Menu.Item>
|
key={`${index + 1}`}
|
||||||
</SlackMenuItemContainer>
|
collapsed={collapsed}
|
||||||
|
>
|
||||||
|
<Menu.Item
|
||||||
|
eventKey={index.toString()}
|
||||||
|
onClick={props.onClick}
|
||||||
|
icon={props.icon}
|
||||||
|
>
|
||||||
|
{props.text}
|
||||||
|
</Menu.Item>
|
||||||
|
</SlackMenuItemContainer>
|
||||||
|
))}
|
||||||
</Menu>
|
</Menu>
|
||||||
</Sider>
|
</Sider>
|
||||||
);
|
);
|
||||||
|
@ -19,6 +19,7 @@ export const Logo = styled.img<LogoProps>`
|
|||||||
|
|
||||||
interface LogoProps {
|
interface LogoProps {
|
||||||
collapsed: boolean;
|
collapsed: boolean;
|
||||||
|
index: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Sider = styled(SiderComponent)`
|
export const Sider = styled(SiderComponent)`
|
||||||
@ -50,9 +51,10 @@ export const SlackButton = styled(Typography)`
|
|||||||
|
|
||||||
export const SlackMenuItemContainer = styled.div<LogoProps>`
|
export const SlackMenuItemContainer = styled.div<LogoProps>`
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 48px;
|
bottom: ${({ index }): string => `${index * 48 + (index + 16)}px`};
|
||||||
background: #262626;
|
background: #262626;
|
||||||
width: ${({ collapsed }): string => (!collapsed ? '200px' : '80px')};
|
width: ${({ collapsed }): string => (!collapsed ? '200px' : '80px')};
|
||||||
|
transition: inherit;
|
||||||
|
|
||||||
&&& {
|
&&& {
|
||||||
li {
|
li {
|
||||||
@ -60,11 +62,14 @@ export const SlackMenuItemContainer = styled.div<LogoProps>`
|
|||||||
collapsed &&
|
collapsed &&
|
||||||
css`
|
css`
|
||||||
padding-left: 24px;
|
padding-left: 24px;
|
||||||
|
padding-top: 6px;
|
||||||
`}
|
`}
|
||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
margin-left: ${({ collapsed }): string => (collapsed ? '0' : '24px')};
|
margin-left: ${({ collapsed }): string => (collapsed ? '0' : '24px')};
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
|
||||||
${({ collapsed }): StyledCSS =>
|
${({ collapsed }): StyledCSS =>
|
||||||
collapsed &&
|
collapsed &&
|
||||||
@ -73,5 +78,24 @@ export const SlackMenuItemContainer = styled.div<LogoProps>`
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
`}
|
`}
|
||||||
}
|
}
|
||||||
|
.ant-menu-title-content {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const RedDot = styled.div`
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background: #d32029;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
margin-left: 1rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const VersionContainer = styled.div`
|
||||||
|
&&& {
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -93,11 +93,15 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element {
|
|||||||
if (response.statusCode === 200) {
|
if (response.statusCode === 200) {
|
||||||
const updatedFilter = getFilter(response.payload);
|
const updatedFilter = getFilter(response.payload);
|
||||||
|
|
||||||
updatedFilter.forEach((value, key) => {
|
// updatedFilter.forEach((value, key) => {
|
||||||
if (key !== 'duration' && name !== key) {
|
// if (key !== 'duration' && name !== key) {
|
||||||
preUserSelectedMap.set(key, Object.keys(value));
|
// preUserSelectedMap.set(key, Object.keys(value));
|
||||||
}
|
// }
|
||||||
});
|
|
||||||
|
// if (key === 'duration') {
|
||||||
|
// newSelectedMap.set('duration', [value.maxDuration, value.minDuration]);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
updatedFilter.set(name, {
|
updatedFilter.set(name, {
|
||||||
[`${keyValue}`]: '-1',
|
[`${keyValue}`]: '-1',
|
||||||
@ -115,6 +119,7 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element {
|
|||||||
selectedFilter: newSelectedMap,
|
selectedFilter: newSelectedMap,
|
||||||
userSelected: preUserSelectedMap,
|
userSelected: preUserSelectedMap,
|
||||||
isFilterExclude: preIsFilterExclude,
|
isFilterExclude: preIsFilterExclude,
|
||||||
|
order: spansAggregate.order,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -125,9 +130,9 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element {
|
|||||||
filterToFetchData,
|
filterToFetchData,
|
||||||
spansAggregate.currentPage,
|
spansAggregate.currentPage,
|
||||||
selectedTags,
|
selectedTags,
|
||||||
updatedFilter,
|
|
||||||
preIsFilterExclude,
|
preIsFilterExclude,
|
||||||
preUserSelectedMap,
|
preUserSelectedMap,
|
||||||
|
spansAggregate.order,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
@ -18,16 +18,26 @@ function CommonCheckBox(props: CommonCheckBoxProps): JSX.Element {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{statusObj.map((e) => (
|
{statusObj
|
||||||
<CheckBoxComponent
|
.sort((a, b) => {
|
||||||
key={e}
|
const countA = +status[a];
|
||||||
{...{
|
const countB = +status[b];
|
||||||
name,
|
|
||||||
keyValue: e,
|
if (countA === countB) {
|
||||||
value: status[e],
|
return a.length - b.length;
|
||||||
}}
|
}
|
||||||
/>
|
return countA - countB;
|
||||||
))}
|
})
|
||||||
|
.map((e) => (
|
||||||
|
<CheckBoxComponent
|
||||||
|
key={e}
|
||||||
|
{...{
|
||||||
|
name,
|
||||||
|
keyValue: e,
|
||||||
|
value: status[e],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import getFilters from 'api/trace/getFilters';
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import durationPlugin from 'dayjs/plugin/duration';
|
import durationPlugin from 'dayjs/plugin/duration';
|
||||||
import useDebouncedFn from 'hooks/useDebouncedFunction';
|
import useDebouncedFn from 'hooks/useDebouncedFunction';
|
||||||
import React, { useState } from 'react';
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
import { getFilter, updateURL } from 'store/actions/trace/util';
|
import { getFilter, updateURL } from 'store/actions/trace/util';
|
||||||
@ -20,11 +20,13 @@ import { Container, InputContainer, Text } from './styles';
|
|||||||
dayjs.extend(durationPlugin);
|
dayjs.extend(durationPlugin);
|
||||||
|
|
||||||
const getMs = (value: string): string => {
|
const getMs = (value: string): string => {
|
||||||
return dayjs
|
return parseFloat(
|
||||||
.duration({
|
dayjs
|
||||||
milliseconds: parseInt(value, 10) / 1000000,
|
.duration({
|
||||||
})
|
milliseconds: parseInt(value, 10) / 1000000,
|
||||||
.format('SSS');
|
})
|
||||||
|
.format('SSS'),
|
||||||
|
).toFixed(2);
|
||||||
};
|
};
|
||||||
|
|
||||||
function Duration(): JSX.Element {
|
function Duration(): JSX.Element {
|
||||||
@ -43,9 +45,10 @@ function Duration(): JSX.Element {
|
|||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
);
|
);
|
||||||
|
|
||||||
const getDuration = ():
|
const preLocalMaxDuration = useRef<number>();
|
||||||
| { maxDuration: string; minDuration: string }
|
const preLocalMinDuration = useRef<number>();
|
||||||
| Record<string, string> => {
|
|
||||||
|
const getDuration = useMemo(() => {
|
||||||
const selectedDuration = selectedFilter.get('duration');
|
const selectedDuration = selectedFilter.get('duration');
|
||||||
|
|
||||||
if (selectedDuration) {
|
if (selectedDuration) {
|
||||||
@ -56,17 +59,29 @@ function Duration(): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return filter.get('duration') || {};
|
return filter.get('duration') || {};
|
||||||
};
|
}, [selectedFilter, filter]);
|
||||||
|
|
||||||
const duration = getDuration();
|
const [preMax, setPreMax] = useState<string>('');
|
||||||
|
const [preMin, setPreMin] = useState<string>('');
|
||||||
|
|
||||||
const maxDuration = duration.maxDuration || '0';
|
useEffect(() => {
|
||||||
const minDuration = duration.minDuration || '0';
|
const duration = getDuration || {};
|
||||||
|
|
||||||
const [localMax, setLocalMax] = useState<string>(maxDuration);
|
const maxDuration = duration.maxDuration || '0';
|
||||||
const [localMin, setLocalMin] = useState<string>(minDuration);
|
const minDuration = duration.minDuration || '0';
|
||||||
|
|
||||||
const defaultValue = [parseFloat(minDuration), parseFloat(maxDuration)];
|
if (preLocalMaxDuration.current === undefined) {
|
||||||
|
preLocalMaxDuration.current = parseFloat(maxDuration);
|
||||||
|
}
|
||||||
|
if (preLocalMinDuration.current === undefined) {
|
||||||
|
preLocalMinDuration.current = parseFloat(minDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
setPreMax(maxDuration);
|
||||||
|
setPreMin(minDuration);
|
||||||
|
}, [getDuration]);
|
||||||
|
|
||||||
|
const defaultValue = [parseFloat(preMin), parseFloat(preMax)];
|
||||||
|
|
||||||
const updatedUrl = async (min: number, max: number): Promise<void> => {
|
const updatedUrl = async (min: number, max: number): Promise<void> => {
|
||||||
const preSelectedFilter = new Map(selectedFilter);
|
const preSelectedFilter = new Map(selectedFilter);
|
||||||
@ -74,7 +89,6 @@ function Duration(): JSX.Element {
|
|||||||
|
|
||||||
preSelectedFilter.set('duration', [String(max), String(min)]);
|
preSelectedFilter.set('duration', [String(max), String(min)]);
|
||||||
|
|
||||||
console.log('on the update Url');
|
|
||||||
const response = await getFilters({
|
const response = await getFilters({
|
||||||
end: String(globalTime.maxTime),
|
end: String(globalTime.maxTime),
|
||||||
getFilters: filterToFetchData,
|
getFilters: filterToFetchData,
|
||||||
@ -87,8 +101,9 @@ function Duration(): JSX.Element {
|
|||||||
const preFilter = getFilter(response.payload);
|
const preFilter = getFilter(response.payload);
|
||||||
|
|
||||||
preFilter.forEach((value, key) => {
|
preFilter.forEach((value, key) => {
|
||||||
if (key !== 'duration') {
|
const values = Object.keys(value);
|
||||||
preUserSelected.set(key, Object.keys(value));
|
if (key !== 'duration' && values.length) {
|
||||||
|
preUserSelected.set(key, values);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -102,6 +117,7 @@ function Duration(): JSX.Element {
|
|||||||
selectedTags,
|
selectedTags,
|
||||||
userSelected: preUserSelected,
|
userSelected: preUserSelected,
|
||||||
isFilterExclude,
|
isFilterExclude,
|
||||||
|
order: spansAggregate.order,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -110,9 +126,9 @@ function Duration(): JSX.Element {
|
|||||||
filterToFetchData,
|
filterToFetchData,
|
||||||
spansAggregate.currentPage,
|
spansAggregate.currentPage,
|
||||||
selectedTags,
|
selectedTags,
|
||||||
preFilter,
|
|
||||||
isFilterExclude,
|
isFilterExclude,
|
||||||
userSelectedFilter,
|
userSelectedFilter,
|
||||||
|
spansAggregate.order,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -120,13 +136,12 @@ function Duration(): JSX.Element {
|
|||||||
const onRangeSliderHandler = (number: [number, number]): void => {
|
const onRangeSliderHandler = (number: [number, number]): void => {
|
||||||
const [min, max] = number;
|
const [min, max] = number;
|
||||||
|
|
||||||
setLocalMin(min.toString());
|
setPreMin(min.toString());
|
||||||
setLocalMax(max.toString());
|
setPreMax(max.toString());
|
||||||
};
|
};
|
||||||
|
|
||||||
const debouncedFunction = useDebouncedFn(
|
const debouncedFunction = useDebouncedFn(
|
||||||
(min, max) => {
|
(min, max) => {
|
||||||
console.log('debounce function');
|
|
||||||
updatedUrl(min as number, max as number);
|
updatedUrl(min as number, max as number);
|
||||||
},
|
},
|
||||||
500,
|
500,
|
||||||
@ -137,11 +152,9 @@ function Duration(): JSX.Element {
|
|||||||
event,
|
event,
|
||||||
) => {
|
) => {
|
||||||
const { value } = event.target;
|
const { value } = event.target;
|
||||||
const min = parseFloat(localMin);
|
const min = parseFloat(preMin);
|
||||||
const max = parseFloat(value) * 1000000;
|
const max = parseFloat(value) * 1000000;
|
||||||
|
|
||||||
console.log('on change in max');
|
|
||||||
|
|
||||||
onRangeSliderHandler([min, max]);
|
onRangeSliderHandler([min, max]);
|
||||||
debouncedFunction(min, max);
|
debouncedFunction(min, max);
|
||||||
};
|
};
|
||||||
@ -151,9 +164,8 @@ function Duration(): JSX.Element {
|
|||||||
) => {
|
) => {
|
||||||
const { value } = event.target;
|
const { value } = event.target;
|
||||||
const min = parseFloat(value) * 1000000;
|
const min = parseFloat(value) * 1000000;
|
||||||
const max = parseFloat(localMax);
|
const max = parseFloat(preMax);
|
||||||
onRangeSliderHandler([min, max]);
|
onRangeSliderHandler([min, max]);
|
||||||
console.log('on change in min');
|
|
||||||
debouncedFunction(min, max);
|
debouncedFunction(min, max);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -170,7 +182,7 @@ function Duration(): JSX.Element {
|
|||||||
<Input
|
<Input
|
||||||
addonAfter="ms"
|
addonAfter="ms"
|
||||||
onChange={onChangeMinHandler}
|
onChange={onChangeMinHandler}
|
||||||
value={getMs(localMin)}
|
value={getMs(preMin)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<InputContainer>
|
<InputContainer>
|
||||||
@ -179,27 +191,27 @@ function Duration(): JSX.Element {
|
|||||||
<Input
|
<Input
|
||||||
addonAfter="ms"
|
addonAfter="ms"
|
||||||
onChange={onChangeMaxHandler}
|
onChange={onChangeMaxHandler}
|
||||||
value={getMs(localMax)}
|
value={getMs(preMax)}
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
<Container>
|
<Container>
|
||||||
<Slider
|
<Slider
|
||||||
defaultValue={[defaultValue[0], defaultValue[1]]}
|
defaultValue={[defaultValue[0], defaultValue[1]]}
|
||||||
min={parseFloat((filter.get('duration') || {}).minDuration)}
|
min={parseFloat((preLocalMinDuration.current || 0).toString())}
|
||||||
max={parseFloat((filter.get('duration') || {}).maxDuration)}
|
max={parseFloat((preLocalMaxDuration.current || 0).toString())}
|
||||||
range
|
range
|
||||||
tipFormatter={(value): JSX.Element => {
|
tipFormatter={(value): JSX.Element => {
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
return <div />;
|
return <div />;
|
||||||
}
|
}
|
||||||
return <div>{`${getMs(value.toString())}ms`}</div>;
|
return <div>{`${getMs(value?.toString())}ms`}</div>;
|
||||||
}}
|
}}
|
||||||
onChange={([min, max]): void => {
|
onChange={([min, max]): void => {
|
||||||
onRangeSliderHandler([min, max]);
|
onRangeSliderHandler([min, max]);
|
||||||
}}
|
}}
|
||||||
onAfterChange={onRangeHandler}
|
onAfterChange={onRangeHandler}
|
||||||
value={[parseFloat(localMin), parseFloat(localMax)]}
|
value={[parseFloat(preMin), parseFloat(preMax)]}
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,7 +21,7 @@ import {
|
|||||||
ButtonContainer,
|
ButtonContainer,
|
||||||
Container,
|
Container,
|
||||||
IconContainer,
|
IconContainer,
|
||||||
TextCotainer,
|
TextContainer,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
@ -64,16 +64,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element {
|
|||||||
const getprepdatedSelectedFilter = new Map(selectedFilter);
|
const getprepdatedSelectedFilter = new Map(selectedFilter);
|
||||||
const getPreUserSelected = new Map(userSelectedFilter);
|
const getPreUserSelected = new Map(userSelectedFilter);
|
||||||
|
|
||||||
if (!isDefaultOpen) {
|
updatedFilterData = [PanelName];
|
||||||
updatedFilterData = [PanelName];
|
|
||||||
} else {
|
|
||||||
// removing the selected filter
|
|
||||||
updatedFilterData = [
|
|
||||||
...filterToFetchData.filter((name) => name !== PanelName),
|
|
||||||
];
|
|
||||||
getprepdatedSelectedFilter.delete(PanelName);
|
|
||||||
getPreUserSelected.delete(PanelName);
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await getFilters({
|
const response = await getFilters({
|
||||||
end: String(global.maxTime),
|
end: String(global.maxTime),
|
||||||
@ -86,33 +77,14 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element {
|
|||||||
if (response.statusCode === 200) {
|
if (response.statusCode === 200) {
|
||||||
const updatedFilter = getFilter(response.payload);
|
const updatedFilter = getFilter(response.payload);
|
||||||
|
|
||||||
// is closed
|
if (!getPreUserSelected.has(PanelName)) {
|
||||||
if (!isDefaultOpen) {
|
|
||||||
// getprepdatedSelectedFilter.set(
|
|
||||||
// props.name,
|
|
||||||
// Object.keys(updatedFilter.get(props.name) || {}),
|
|
||||||
// );
|
|
||||||
|
|
||||||
getPreUserSelected.set(
|
getPreUserSelected.set(
|
||||||
PanelName,
|
PanelName,
|
||||||
Object.keys(updatedFilter.get(PanelName) || {}),
|
Object.keys(updatedFilter.get(PanelName) || []),
|
||||||
);
|
);
|
||||||
|
|
||||||
updatedFilterData = [...filterToFetchData, PanelName];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// now append the non prop.name trace filter enum over the list
|
updatedFilterData = [...filterToFetchData, PanelName];
|
||||||
// selectedFilter.forEach((value, key) => {
|
|
||||||
// if (key !== props.name) {
|
|
||||||
// getprepdatedSelectedFilter.set(key, value);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
getPreUserSelected.forEach((value, key) => {
|
|
||||||
if (key !== PanelName) {
|
|
||||||
getPreUserSelected.set(key, value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
filter.forEach((value, key) => {
|
filter.forEach((value, key) => {
|
||||||
if (key !== PanelName) {
|
if (key !== PanelName) {
|
||||||
updatedFilter.set(key, value);
|
updatedFilter.set(key, value);
|
||||||
@ -129,6 +101,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element {
|
|||||||
selectedTags,
|
selectedTags,
|
||||||
userSelected: getPreUserSelected,
|
userSelected: getPreUserSelected,
|
||||||
isFilterExclude,
|
isFilterExclude,
|
||||||
|
order: spansAggregate.order,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -137,9 +110,9 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element {
|
|||||||
updatedFilterData,
|
updatedFilterData,
|
||||||
spansAggregate.currentPage,
|
spansAggregate.currentPage,
|
||||||
selectedTags,
|
selectedTags,
|
||||||
updatedFilter,
|
|
||||||
isFilterExclude,
|
isFilterExclude,
|
||||||
getPreUserSelected,
|
getPreUserSelected,
|
||||||
|
spansAggregate.order,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
notification.error({
|
notification.error({
|
||||||
@ -155,6 +128,43 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description this function removes the selected filter
|
||||||
|
*/
|
||||||
|
const onCloseHandler = (): void => {
|
||||||
|
const preSelectedFilter = new Map(selectedFilter);
|
||||||
|
// removing the filter from filter to fetch the data
|
||||||
|
const preFilterToFetchTheData = [
|
||||||
|
...filterToFetchData.filter((name) => name !== PanelName),
|
||||||
|
];
|
||||||
|
|
||||||
|
// preSelectedFilter.delete(PanelName);
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: UPDATE_ALL_FILTERS,
|
||||||
|
payload: {
|
||||||
|
current: spansAggregate.currentPage,
|
||||||
|
filter,
|
||||||
|
filterToFetchData: preFilterToFetchTheData,
|
||||||
|
selectedFilter: preSelectedFilter,
|
||||||
|
selectedTags,
|
||||||
|
userSelected: userSelectedFilter,
|
||||||
|
isFilterExclude,
|
||||||
|
order: spansAggregate.order,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
updateURL(
|
||||||
|
preSelectedFilter,
|
||||||
|
preFilterToFetchTheData,
|
||||||
|
spansAggregate.currentPage,
|
||||||
|
selectedTags,
|
||||||
|
isFilterExclude,
|
||||||
|
userSelectedFilter,
|
||||||
|
spansAggregate.order,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const onClearAllHandler = async (): Promise<void> => {
|
const onClearAllHandler = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
@ -177,18 +187,19 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.statusCode === 200 && response.payload) {
|
if (response.statusCode === 200 && response.payload) {
|
||||||
const getUpatedFilter = getFilter(response.payload);
|
const getUpdatedFilter = getFilter(response.payload);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UPDATE_ALL_FILTERS,
|
type: UPDATE_ALL_FILTERS,
|
||||||
payload: {
|
payload: {
|
||||||
current: spansAggregate.currentPage,
|
current: spansAggregate.currentPage,
|
||||||
filter: getUpatedFilter,
|
filter: getUpdatedFilter,
|
||||||
filterToFetchData,
|
filterToFetchData,
|
||||||
selectedFilter: updatedFilter,
|
selectedFilter: updatedFilter,
|
||||||
selectedTags,
|
selectedTags,
|
||||||
userSelected: preUserSelected,
|
userSelected: preUserSelected,
|
||||||
isFilterExclude: postIsFilterExclude,
|
isFilterExclude: postIsFilterExclude,
|
||||||
|
order: spansAggregate.order,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -197,9 +208,9 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element {
|
|||||||
filterToFetchData,
|
filterToFetchData,
|
||||||
spansAggregate.currentPage,
|
spansAggregate.currentPage,
|
||||||
selectedTags,
|
selectedTags,
|
||||||
getUpatedFilter,
|
|
||||||
postIsFilterExclude,
|
postIsFilterExclude,
|
||||||
preUserSelected,
|
preUserSelected,
|
||||||
|
spansAggregate.order,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
notification.error({
|
notification.error({
|
||||||
@ -280,7 +291,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element {
|
|||||||
aria-disabled={filterLoading || isLoading}
|
aria-disabled={filterLoading || isLoading}
|
||||||
aria-expanded={IsPanelOpen}
|
aria-expanded={IsPanelOpen}
|
||||||
>
|
>
|
||||||
<TextCotainer onClick={onExpandHandler}>
|
<TextContainer onClick={isDefaultOpen ? onCloseHandler : onExpandHandler}>
|
||||||
<IconContainer>
|
<IconContainer>
|
||||||
{!IsPanelOpen ? <RightOutlined /> : <DownOutlined />}
|
{!IsPanelOpen ? <RightOutlined /> : <DownOutlined />}
|
||||||
</IconContainer>
|
</IconContainer>
|
||||||
@ -288,7 +299,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element {
|
|||||||
<Text style={{ textTransform: 'capitalize' }} ellipsis>
|
<Text style={{ textTransform: 'capitalize' }} ellipsis>
|
||||||
{AllPanelHeading.find((e) => e.key === PanelName)?.displayValue || ''}
|
{AllPanelHeading.find((e) => e.key === PanelName)?.displayValue || ''}
|
||||||
</Text>
|
</Text>
|
||||||
</TextCotainer>
|
</TextContainer>
|
||||||
|
|
||||||
{PanelName !== 'duration' && (
|
{PanelName !== 'duration' && (
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
|
@ -30,7 +30,7 @@ export const IconContainer = styled.div`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const TextCotainer = styled.div`
|
export const TextContainer = styled.div`
|
||||||
&&& {
|
&&& {
|
||||||
display: flex;
|
display: flex;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
70
frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx
Normal file
70
frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { Select } from 'antd';
|
||||||
|
import { DefaultOptionType } from 'antd/lib/select';
|
||||||
|
import getTagValue from 'api/trace/getTagValue';
|
||||||
|
import useFetch from 'hooks/useFetch';
|
||||||
|
import React from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
|
import { PayloadProps, Props } from 'types/api/trace/getTagValue';
|
||||||
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
import { TraceReducer } from 'types/reducer/trace';
|
||||||
|
|
||||||
|
import { Value } from '.';
|
||||||
|
import { SelectComponent } from './styles';
|
||||||
|
|
||||||
|
function TagValue(props: TagValueProps): JSX.Element {
|
||||||
|
const { tag, setLocalSelectedTags, index, tagKey } = props;
|
||||||
|
const {
|
||||||
|
Key: selectedKey,
|
||||||
|
Operator: selectedOperator,
|
||||||
|
Values: selectedValues,
|
||||||
|
} = tag;
|
||||||
|
|
||||||
|
const globalReducer = useSelector<AppState, GlobalReducer>(
|
||||||
|
(state) => state.globalTime,
|
||||||
|
);
|
||||||
|
|
||||||
|
const valueSuggestion = useFetch<PayloadProps, Props>(getTagValue, {
|
||||||
|
end: globalReducer.maxTime,
|
||||||
|
start: globalReducer.minTime,
|
||||||
|
tagKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SelectComponent
|
||||||
|
value={selectedValues[0]}
|
||||||
|
onSelect={(value: unknown): void => {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
setLocalSelectedTags((tags) => [
|
||||||
|
...tags.slice(0, index),
|
||||||
|
{
|
||||||
|
Key: selectedKey,
|
||||||
|
Operator: selectedOperator,
|
||||||
|
Values: [...selectedValues, value],
|
||||||
|
},
|
||||||
|
...tags.slice(index + 1, tags.length),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
loading={valueSuggestion.loading || false}
|
||||||
|
>
|
||||||
|
{valueSuggestion.payload &&
|
||||||
|
valueSuggestion.payload.map((suggestion) => (
|
||||||
|
<Select.Option key={suggestion.tagValues} value={suggestion.tagValues}>
|
||||||
|
{suggestion.tagValues}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</SelectComponent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TagValueProps {
|
||||||
|
index: number;
|
||||||
|
tag: FlatArray<TraceReducer['selectedTags'], 1>;
|
||||||
|
setLocalSelectedTags: React.Dispatch<
|
||||||
|
React.SetStateAction<TraceReducer['selectedTags']>
|
||||||
|
>;
|
||||||
|
tagKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TagValue;
|
@ -5,13 +5,9 @@ import { useSelector } from 'react-redux';
|
|||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { TraceReducer } from 'types/reducer/trace';
|
import { TraceReducer } from 'types/reducer/trace';
|
||||||
|
|
||||||
import {
|
import { Container, IconContainer, SelectComponent } from './styles';
|
||||||
Container,
|
|
||||||
IconContainer,
|
|
||||||
SelectComponent,
|
|
||||||
ValueSelect,
|
|
||||||
} from './styles';
|
|
||||||
import TagsKey from './TagKey';
|
import TagsKey from './TagKey';
|
||||||
|
import TagValue from './TagValue';
|
||||||
|
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
|
|
||||||
@ -68,7 +64,6 @@ function SingleTags(props: AllTagsProps): JSX.Element {
|
|||||||
tag={tag}
|
tag={tag}
|
||||||
setLocalSelectedTags={setLocalSelectedTags}
|
setLocalSelectedTags={setLocalSelectedTags}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SelectComponent
|
<SelectComponent
|
||||||
onChange={onChangeOperatorHandler}
|
onChange={onChangeOperatorHandler}
|
||||||
value={AllMenu.find((e) => e.key === selectedOperator)?.value || ''}
|
value={AllMenu.find((e) => e.key === selectedOperator)?.value || ''}
|
||||||
@ -80,21 +75,16 @@ function SingleTags(props: AllTagsProps): JSX.Element {
|
|||||||
))}
|
))}
|
||||||
</SelectComponent>
|
</SelectComponent>
|
||||||
|
|
||||||
<ValueSelect
|
{selectedKey[0] ? (
|
||||||
value={selectedValues}
|
<TagValue
|
||||||
onChange={(value): void => {
|
index={index}
|
||||||
setLocalSelectedTags((tags) => [
|
tag={tag}
|
||||||
...tags.slice(0, index),
|
setLocalSelectedTags={setLocalSelectedTags}
|
||||||
{
|
tagKey={selectedKey[0]}
|
||||||
Key: selectedKey,
|
/>
|
||||||
Operator: selectedOperator,
|
) : (
|
||||||
Values: value as string[],
|
<SelectComponent />
|
||||||
},
|
)}
|
||||||
...tags.slice(index + 1, tags.length),
|
|
||||||
]);
|
|
||||||
}}
|
|
||||||
mode="tags"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<IconContainer role="button" onClick={(): void => onDeleteTagHandler(index)}>
|
<IconContainer role="button" onClick={(): void => onDeleteTagHandler(index)}>
|
||||||
<CloseOutlined />
|
<CloseOutlined />
|
||||||
@ -112,4 +102,10 @@ interface AllTagsProps {
|
|||||||
>;
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Value {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default SingleTags;
|
export default SingleTags;
|
||||||
|
@ -15,12 +15,6 @@ export const SelectComponent = styled(Select)`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ValueSelect = styled(Select)`
|
|
||||||
&&& {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const Container = styled.div`
|
export const Container = styled.div`
|
||||||
&&& {
|
&&& {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -5,7 +5,7 @@ import { connect, useSelector } from 'react-redux';
|
|||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
import { UpdateTagIsError } from 'store/actions/trace/updateIsTagsError';
|
import { UpdateTagIsError } from 'store/actions/trace/updateIsTagsError';
|
||||||
import { UpdateTagVisiblity } from 'store/actions/trace/updateTagPanelVisiblity';
|
import { UpdateTagVisibility } from 'store/actions/trace/updateTagPanelVisiblity';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { TraceReducer } from 'types/reducer/trace';
|
import { TraceReducer } from 'types/reducer/trace';
|
||||||
@ -27,7 +27,7 @@ const { Paragraph } = Typography;
|
|||||||
function AllTags({
|
function AllTags({
|
||||||
updateTagIsError,
|
updateTagIsError,
|
||||||
onChangeHandler,
|
onChangeHandler,
|
||||||
updateTagVisiblity,
|
updateTagVisibility,
|
||||||
updateFilters,
|
updateFilters,
|
||||||
}: AllTagsProps): JSX.Element {
|
}: AllTagsProps): JSX.Element {
|
||||||
const traces = useSelector<AppState, TraceReducer>((state) => state.traces);
|
const traces = useSelector<AppState, TraceReducer>((state) => state.traces);
|
||||||
@ -63,7 +63,7 @@ function AllTags({
|
|||||||
onChangeHandler(parsedQuery.payload);
|
onChangeHandler(parsedQuery.payload);
|
||||||
updateFilters(localSelectedTags);
|
updateFilters(localSelectedTags);
|
||||||
updateTagIsError(false);
|
updateTagIsError(false);
|
||||||
updateTagVisiblity(false);
|
updateTagVisibility(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ function AllTags({
|
|||||||
return (
|
return (
|
||||||
<ErrorContainer>
|
<ErrorContainer>
|
||||||
<Paragraph style={{ color: '#E89A3C' }}>
|
<Paragraph style={{ color: '#E89A3C' }}>
|
||||||
Unrecognised query format. Please reset your query by clicking `X` in the
|
Unrecognized query format. Please reset your query by clicking `X` in the
|
||||||
search bar above.
|
search bar above.
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
|
|
||||||
@ -116,14 +116,16 @@ function AllTags({
|
|||||||
</Wrapper>
|
</Wrapper>
|
||||||
|
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
<Button onClick={onResetHandler}>Reset</Button>
|
<Space align="start">
|
||||||
<Button
|
<Button onClick={onResetHandler}>Reset</Button>
|
||||||
type="primary"
|
<Button
|
||||||
onClick={onRunQueryHandler}
|
type="primary"
|
||||||
icon={<CaretRightFilled />}
|
onClick={onRunQueryHandler}
|
||||||
>
|
icon={<CaretRightFilled />}
|
||||||
Run Query
|
>
|
||||||
</Button>
|
Run Query
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
@ -131,14 +133,14 @@ function AllTags({
|
|||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
updateTagIsError: (value: boolean) => void;
|
updateTagIsError: (value: boolean) => void;
|
||||||
updateTagVisiblity: (value: boolean) => void;
|
updateTagVisibility: (value: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = (
|
const mapDispatchToProps = (
|
||||||
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
|
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
|
||||||
): DispatchProps => ({
|
): DispatchProps => ({
|
||||||
updateTagIsError: bindActionCreators(UpdateTagIsError, dispatch),
|
updateTagIsError: bindActionCreators(UpdateTagIsError, dispatch),
|
||||||
updateTagVisiblity: bindActionCreators(UpdateTagVisiblity, dispatch),
|
updateTagVisibility: bindActionCreators(UpdateTagVisibility, dispatch),
|
||||||
});
|
});
|
||||||
|
|
||||||
interface AllTagsProps extends DispatchProps {
|
interface AllTagsProps extends DispatchProps {
|
||||||
|
@ -6,7 +6,7 @@ export const Container = styled(Card)`
|
|||||||
min-height: 20vh;
|
min-height: 20vh;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
position: absolute;
|
position: absolute !important;
|
||||||
|
|
||||||
.ant-card-body {
|
.ant-card-body {
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
@ -35,20 +35,16 @@ export const Wrapper = styled.div`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ButtonContainer = styled.div`
|
export const ButtonContainer = styled(Card)`
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: #303030;
|
|
||||||
padding-top: 11px;
|
|
||||||
padding-bottom: 11px;
|
|
||||||
padding-right: 38.01px;
|
|
||||||
|
|
||||||
margin-top: 1rem;
|
padding-top: 11px !important;
|
||||||
|
padding-bottom: 11px !important;
|
||||||
|
padding-right: 38.01px !important;
|
||||||
|
|
||||||
> button:nth-child(1) {
|
margin-top: 1rem !important;
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const CurrentTagsContainer = styled.div`
|
export const CurrentTagsContainer = styled.div`
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import { CaretRightFilled } from '@ant-design/icons';
|
import { CaretRightFilled } from '@ant-design/icons';
|
||||||
import { Space } from 'antd';
|
|
||||||
import useClickOutside from 'hooks/useClickOutside';
|
import useClickOutside from 'hooks/useClickOutside';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { connect, useDispatch, useSelector } from 'react-redux';
|
import { connect, useDispatch, useSelector } from 'react-redux';
|
||||||
import { bindActionCreators, Dispatch } from 'redux';
|
import { bindActionCreators, Dispatch } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
import { UpdateTagIsError } from 'store/actions/trace/updateIsTagsError';
|
import { UpdateTagIsError } from 'store/actions/trace/updateIsTagsError';
|
||||||
import { UpdateTagVisiblity } from 'store/actions/trace/updateTagPanelVisiblity';
|
import { UpdateTagVisibility } from 'store/actions/trace/updateTagPanelVisiblity';
|
||||||
import { updateURL } from 'store/actions/trace/util';
|
import { updateURL } from 'store/actions/trace/util';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
@ -18,7 +17,7 @@ import { Container, SearchComponent } from './styles';
|
|||||||
import { parseQueryToTags, parseTagsToQuery } from './util';
|
import { parseQueryToTags, parseTagsToQuery } from './util';
|
||||||
|
|
||||||
function Search({
|
function Search({
|
||||||
updateTagVisiblity,
|
updateTagVisibility,
|
||||||
updateTagIsError,
|
updateTagIsError,
|
||||||
}: SearchProps): JSX.Element {
|
}: SearchProps): JSX.Element {
|
||||||
const traces = useSelector<AppState, TraceReducer>((state) => state.traces);
|
const traces = useSelector<AppState, TraceReducer>((state) => state.traces);
|
||||||
@ -66,7 +65,7 @@ function Search({
|
|||||||
!(e.ariaSelected === 'true') &&
|
!(e.ariaSelected === 'true') &&
|
||||||
traces.isTagModalOpen
|
traces.isTagModalOpen
|
||||||
) {
|
) {
|
||||||
updateTagVisiblity(false);
|
updateTagVisibility(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -75,7 +74,7 @@ function Search({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const setIsTagsModalHandler = (value: boolean): void => {
|
const setIsTagsModalHandler = (value: boolean): void => {
|
||||||
updateTagVisiblity(value);
|
updateTagVisibility(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onFocusHandler: React.FocusEventHandler<HTMLInputElement> = (e) => {
|
const onFocusHandler: React.FocusEventHandler<HTMLInputElement> = (e) => {
|
||||||
@ -96,6 +95,7 @@ function Search({
|
|||||||
selectedFilter: traces.selectedFilter,
|
selectedFilter: traces.selectedFilter,
|
||||||
userSelected: traces.userSelectedFilter,
|
userSelected: traces.userSelectedFilter,
|
||||||
isFilterExclude: traces.isFilterExclude,
|
isFilterExclude: traces.isFilterExclude,
|
||||||
|
order: traces.spansAggregate.order,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -104,60 +104,58 @@ function Search({
|
|||||||
traces.filterToFetchData,
|
traces.filterToFetchData,
|
||||||
traces.spansAggregate.currentPage,
|
traces.spansAggregate.currentPage,
|
||||||
selectedTags,
|
selectedTags,
|
||||||
traces.filter,
|
|
||||||
traces.isFilterExclude,
|
traces.isFilterExclude,
|
||||||
traces.userSelectedFilter,
|
traces.userSelectedFilter,
|
||||||
|
traces.spansAggregate.order,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" style={{ width: '100%' }}>
|
<Container ref={tagRef}>
|
||||||
<Container ref={tagRef}>
|
<SearchComponent
|
||||||
<SearchComponent
|
onChange={(event): void => onChangeHandler(event.target.value)}
|
||||||
onChange={(event): void => onChangeHandler(event.target.value)}
|
value={value}
|
||||||
value={value}
|
allowClear
|
||||||
allowClear
|
disabled={traces.filterLoading}
|
||||||
disabled={traces.filterLoading}
|
onFocus={onFocusHandler}
|
||||||
onFocus={onFocusHandler}
|
placeholder="Click to filter by tags"
|
||||||
placeholder="Click to filter by tags"
|
type="search"
|
||||||
type="search"
|
enterButton={<CaretRightFilled />}
|
||||||
enterButton={<CaretRightFilled />}
|
onSearch={(string): void => {
|
||||||
onSearch={(string): void => {
|
if (string.length === 0) {
|
||||||
if (string.length === 0) {
|
updateTagVisibility(false);
|
||||||
updateTagVisiblity(false);
|
updateFilters([]);
|
||||||
updateFilters([]);
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const { isError, payload } = parseQueryToTags(string);
|
const { isError, payload } = parseQueryToTags(string);
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
updateTagIsError(true);
|
updateTagIsError(true);
|
||||||
} else {
|
} else {
|
||||||
updateTagIsError(false);
|
updateTagIsError(false);
|
||||||
updateTagVisiblity(false);
|
updateTagVisibility(false);
|
||||||
updateFilters(payload);
|
updateFilters(payload);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{traces.isTagModalOpen && (
|
{traces.isTagModalOpen && (
|
||||||
<Tags updateFilters={updateFilters} onChangeHandler={onChangeHandler} />
|
<Tags updateFilters={updateFilters} onChangeHandler={onChangeHandler} />
|
||||||
)}
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
</Space>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
updateTagVisiblity: (value: boolean) => void;
|
updateTagVisibility: (value: boolean) => void;
|
||||||
updateTagIsError: (value: boolean) => void;
|
updateTagIsError: (value: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = (
|
const mapDispatchToProps = (
|
||||||
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
|
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
|
||||||
): DispatchProps => ({
|
): DispatchProps => ({
|
||||||
updateTagVisiblity: bindActionCreators(UpdateTagVisiblity, dispatch),
|
updateTagVisibility: bindActionCreators(UpdateTagVisibility, dispatch),
|
||||||
updateTagIsError: bindActionCreators(UpdateTagIsError, dispatch),
|
updateTagIsError: bindActionCreators(UpdateTagIsError, dispatch),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ const { Search } = Input;
|
|||||||
export const Container = styled.div`
|
export const Container = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const SearchComponent = styled(Search)`
|
export const SearchComponent = styled(Search)`
|
||||||
|
@ -3,35 +3,36 @@ import Table, { ColumnsType } from 'antd/lib/table';
|
|||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import duration from 'dayjs/plugin/duration';
|
import duration from 'dayjs/plugin/duration';
|
||||||
|
import history from 'lib/history';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { Link } from 'react-router-dom';
|
import { Dispatch } from 'redux';
|
||||||
import { bindActionCreators } from 'redux';
|
import { updateURL } from 'store/actions/trace/util';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
|
||||||
import {
|
|
||||||
GetSpansAggregate,
|
|
||||||
GetSpansAggregateProps,
|
|
||||||
} from 'store/actions/trace/getInitialSpansAggregate';
|
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import {
|
||||||
|
UPDATE_SPAN_ORDER,
|
||||||
|
UPDATE_SPANS_AGGREGATE_PAGE_NUMBER,
|
||||||
|
} from 'types/actions/trace';
|
||||||
import { TraceReducer } from 'types/reducer/trace';
|
import { TraceReducer } from 'types/reducer/trace';
|
||||||
|
|
||||||
dayjs.extend(duration);
|
dayjs.extend(duration);
|
||||||
|
|
||||||
function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element {
|
function TraceTable(): JSX.Element {
|
||||||
const {
|
const {
|
||||||
spansAggregate,
|
spansAggregate,
|
||||||
selectedFilter,
|
selectedFilter,
|
||||||
selectedTags,
|
selectedTags,
|
||||||
filterLoading,
|
filterLoading,
|
||||||
|
userSelectedFilter,
|
||||||
|
filter,
|
||||||
|
isFilterExclude,
|
||||||
|
filterToFetchData,
|
||||||
} = useSelector<AppState, TraceReducer>((state) => state.traces);
|
} = useSelector<AppState, TraceReducer>((state) => state.traces);
|
||||||
|
|
||||||
const globalTime = useSelector<AppState, GlobalReducer>(
|
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||||
(state) => state.globalTime,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { loading, total } = spansAggregate;
|
const { loading, total, order: spansAggregateOrder } = spansAggregate;
|
||||||
|
|
||||||
type TableType = FlatArray<TraceReducer['spansAggregate']['data'], 1>;
|
type TableType = FlatArray<TraceReducer['spansAggregate']['data'], 1>;
|
||||||
|
|
||||||
@ -39,30 +40,17 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element {
|
|||||||
return `${ROUTES.TRACE}/${record.traceID}?spanId=${record.spanID}`;
|
return `${ROUTES.TRACE}/${record.traceID}?spanId=${record.spanID}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getValue = (value: string, record: TableType): JSX.Element => {
|
const getValue = (value: string): JSX.Element => {
|
||||||
return (
|
return <Typography>{value}</Typography>;
|
||||||
<Link to={getLink(record)}>
|
|
||||||
<Typography>{value}</Typography>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getHttpMethodOrStatus = (
|
const getHttpMethodOrStatus = (
|
||||||
value: TableType['httpMethod'],
|
value: TableType['httpMethod'],
|
||||||
record: TableType,
|
|
||||||
): JSX.Element => {
|
): JSX.Element => {
|
||||||
if (value.length === 0) {
|
if (value.length === 0) {
|
||||||
return (
|
return <Typography>-</Typography>;
|
||||||
<Link to={getLink(record)}>
|
|
||||||
<Typography>-</Typography>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return (
|
return <Tag color="magenta">{value}</Tag>;
|
||||||
<Link to={getLink(record)}>
|
|
||||||
<Tag color="magenta">{value}</Tag>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns: ColumnsType<TableType> = [
|
const columns: ColumnsType<TableType> = [
|
||||||
@ -71,13 +59,9 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element {
|
|||||||
dataIndex: 'timestamp',
|
dataIndex: 'timestamp',
|
||||||
key: 'timestamp',
|
key: 'timestamp',
|
||||||
sorter: true,
|
sorter: true,
|
||||||
render: (value: TableType['timestamp'], record): JSX.Element => {
|
render: (value: TableType['timestamp']): JSX.Element => {
|
||||||
const day = dayjs(value);
|
const day = dayjs(value);
|
||||||
return (
|
return <Typography>{day.format('YYYY/MM/DD HH:mm:ss')}</Typography>;
|
||||||
<Link to={getLink(record)}>
|
|
||||||
<Typography>{day.format('YYYY/MM/DD HH:mm:ss')}</Typography>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -96,14 +80,13 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element {
|
|||||||
title: 'Duration',
|
title: 'Duration',
|
||||||
dataIndex: 'durationNano',
|
dataIndex: 'durationNano',
|
||||||
key: 'durationNano',
|
key: 'durationNano',
|
||||||
render: (value: TableType['durationNano'], record): JSX.Element => (
|
render: (value: TableType['durationNano']): JSX.Element => (
|
||||||
<Link to={getLink(record)}>
|
<Typography>
|
||||||
<Typography>
|
{`${dayjs
|
||||||
{`${dayjs
|
.duration({ milliseconds: value / 1000000 })
|
||||||
.duration({ milliseconds: value / 1000000 })
|
.asMilliseconds()
|
||||||
.asMilliseconds()} ms`}
|
.toFixed(2)} ms`}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Link>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -126,17 +109,33 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element {
|
|||||||
sort,
|
sort,
|
||||||
) => {
|
) => {
|
||||||
if (!Array.isArray(sort)) {
|
if (!Array.isArray(sort)) {
|
||||||
const { order = 'ascend' } = sort;
|
const { order = spansAggregateOrder } = sort;
|
||||||
if (props.current && props.pageSize) {
|
if (props.current && props.pageSize) {
|
||||||
getSpansAggregate({
|
const spanOrder = order || spansAggregateOrder;
|
||||||
maxTime: globalTime.maxTime,
|
|
||||||
minTime: globalTime.minTime,
|
dispatch({
|
||||||
selectedFilter,
|
type: UPDATE_SPAN_ORDER,
|
||||||
current: props.current,
|
payload: {
|
||||||
pageSize: props.pageSize,
|
order: spanOrder,
|
||||||
selectedTags,
|
},
|
||||||
order: order === 'ascend' ? 'ascending' : 'descending',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: UPDATE_SPANS_AGGREGATE_PAGE_NUMBER,
|
||||||
|
payload: {
|
||||||
|
currentPage: props.current,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
updateURL(
|
||||||
|
selectedFilter,
|
||||||
|
filterToFetchData,
|
||||||
|
props.current,
|
||||||
|
selectedTags,
|
||||||
|
isFilterExclude,
|
||||||
|
userSelectedFilter,
|
||||||
|
spanOrder,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -147,10 +146,17 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element {
|
|||||||
dataSource={spansAggregate.data}
|
dataSource={spansAggregate.data}
|
||||||
loading={loading || filterLoading}
|
loading={loading || filterLoading}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
rowKey="timestamp"
|
rowKey={(record): string => `${record.traceID}-${record.spanID}`}
|
||||||
style={{
|
style={{
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
}}
|
}}
|
||||||
|
onRow={(record): React.HTMLAttributes<TableType> => ({
|
||||||
|
onClick: (event): void => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
history.push(getLink(record));
|
||||||
|
},
|
||||||
|
})}
|
||||||
pagination={{
|
pagination={{
|
||||||
current: spansAggregate.currentPage,
|
current: spansAggregate.currentPage,
|
||||||
pageSize: spansAggregate.pageSize,
|
pageSize: spansAggregate.pageSize,
|
||||||
@ -158,20 +164,9 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element {
|
|||||||
position: ['bottomLeft'],
|
position: ['bottomLeft'],
|
||||||
total,
|
total,
|
||||||
}}
|
}}
|
||||||
|
sortDirections={['ascend', 'descend']}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
export default TraceTable;
|
||||||
getSpansAggregate: (props: GetSpansAggregateProps) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = (
|
|
||||||
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
|
|
||||||
): DispatchProps => ({
|
|
||||||
getSpansAggregate: bindActionCreators(GetSpansAggregate, dispatch),
|
|
||||||
});
|
|
||||||
|
|
||||||
type TraceProps = DispatchProps;
|
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps)(TraceTable);
|
|
||||||
|
@ -1,42 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* @jest-environment jsdom
|
||||||
|
*/
|
||||||
|
|
||||||
import { expect } from '@jest/globals';
|
import { expect } from '@jest/globals';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
|
import { renderHook } from '@testing-library/react-hooks';
|
||||||
import TraceFlameGraph from 'container/TraceFlameGraph';
|
import TraceFlameGraph from 'container/TraceFlameGraph';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import store from 'store';
|
||||||
|
|
||||||
test('loads and displays greeting', () => {
|
test('loads and displays greeting', () => {
|
||||||
const onSpanHover = useState('');
|
const { rerender } = renderHook(() => useState(''));
|
||||||
|
|
||||||
const { asFragment } = render(
|
const { asFragment } = render(
|
||||||
<TraceFlameGraph
|
<Provider store={store}>
|
||||||
{...{
|
<TraceFlameGraph
|
||||||
hoveredSpanId: '',
|
{...{
|
||||||
intervalUnit: { multiplier: 0, name: 'm' },
|
hoveredSpanId: '',
|
||||||
onSpanHover: onSpanHover[1],
|
intervalUnit: { multiplier: 0, name: 'm' },
|
||||||
onSpanSelect: (): void => {},
|
onSpanHover: rerender,
|
||||||
selectedSpanId: '',
|
onSpanSelect: (): void => {},
|
||||||
traceMetaData: {
|
selectedSpanId: '',
|
||||||
globalEnd: 0,
|
traceMetaData: {
|
||||||
globalStart: 0,
|
globalEnd: 0,
|
||||||
levels: 0,
|
globalStart: 0,
|
||||||
spread: 0,
|
levels: 0,
|
||||||
totalSpans: 0,
|
spread: 0,
|
||||||
},
|
totalSpans: 0,
|
||||||
treeData: {
|
},
|
||||||
children: [],
|
treeData: {
|
||||||
id: '',
|
children: [],
|
||||||
name: '',
|
id: '',
|
||||||
serviceColour: '',
|
name: '',
|
||||||
serviceName: '',
|
serviceColour: '#a0e',
|
||||||
startTime: 0,
|
serviceName: '',
|
||||||
tags: [],
|
startTime: 0,
|
||||||
time: 0,
|
tags: [],
|
||||||
value: 0,
|
time: 100,
|
||||||
event: [],
|
value: 100,
|
||||||
hasError: false,
|
event: [],
|
||||||
parent: undefined,
|
hasError: false,
|
||||||
},
|
parent: undefined,
|
||||||
}}
|
},
|
||||||
/>,
|
}}
|
||||||
|
/>
|
||||||
|
</Provider>,
|
||||||
);
|
);
|
||||||
expect(asFragment()).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
@ -1,3 +1,17 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`loads and displays greeting 1`] = `<DocumentFragment />`;
|
exports[`loads and displays greeting 1`] = `
|
||||||
|
<DocumentFragment>
|
||||||
|
<div
|
||||||
|
class="sc-gsDKAQ jFDWPs"
|
||||||
|
height="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="sc-bdvvtL fyFVjh"
|
||||||
|
title="
|
||||||
|
0 m"
|
||||||
|
width="Infinity"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</DocumentFragment>
|
||||||
|
`;
|
||||||
|
110
frontend/src/container/Version/index.tsx
Normal file
110
frontend/src/container/Version/index.tsx
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import { WarningFilled } from '@ant-design/icons';
|
||||||
|
import { Button, Card, Form, Space, Typography } from 'antd';
|
||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
|
import { InputComponent } from './styles';
|
||||||
|
|
||||||
|
const { Title } = Typography;
|
||||||
|
|
||||||
|
function Version(): JSX.Element {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const onClickUpgradeHandler = useCallback((link: string) => {
|
||||||
|
window.open(link, '_blank');
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const {
|
||||||
|
currentVersion,
|
||||||
|
latestVersion,
|
||||||
|
isCurrentVersionError,
|
||||||
|
isLatestVersionError,
|
||||||
|
} = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
|
|
||||||
|
const isLatestVersion = currentVersion === latestVersion;
|
||||||
|
const isError = isCurrentVersionError || isLatestVersionError;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<Title ellipsis level={4}>
|
||||||
|
{t('version')}
|
||||||
|
</Title>
|
||||||
|
|
||||||
|
<Form
|
||||||
|
wrapperCol={{
|
||||||
|
span: 14,
|
||||||
|
}}
|
||||||
|
labelCol={{
|
||||||
|
span: 3,
|
||||||
|
}}
|
||||||
|
layout="horizontal"
|
||||||
|
form={form}
|
||||||
|
labelAlign="left"
|
||||||
|
>
|
||||||
|
<Form.Item label={t('current_version')}>
|
||||||
|
<InputComponent
|
||||||
|
readOnly
|
||||||
|
value={isCurrentVersionError ? t('n_a').toString() : currentVersion}
|
||||||
|
placeholder={t('current_version')}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label={t('latest_version')}>
|
||||||
|
<InputComponent
|
||||||
|
readOnly
|
||||||
|
value={isLatestVersionError ? t('n_a').toString() : latestVersion}
|
||||||
|
placeholder={t('latest_version')}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
onClick={(): void =>
|
||||||
|
onClickUpgradeHandler('https://github.com/SigNoz/signoz/releases')
|
||||||
|
}
|
||||||
|
type="link"
|
||||||
|
>
|
||||||
|
{t('release_notes')}
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
|
||||||
|
{!isError && isLatestVersion && (
|
||||||
|
<div>
|
||||||
|
<Space align="start">
|
||||||
|
<span>✅</span>
|
||||||
|
<Typography.Paragraph italic>
|
||||||
|
{t('latest_version_signoz')}
|
||||||
|
</Typography.Paragraph>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isError && !isLatestVersion && (
|
||||||
|
<div>
|
||||||
|
<Space align="start">
|
||||||
|
<span>
|
||||||
|
<WarningFilled style={{ color: '#E87040' }} />
|
||||||
|
</span>
|
||||||
|
<Typography.Paragraph italic>{t('stale_version')}</Typography.Paragraph>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isError && !isLatestVersion && (
|
||||||
|
<Button
|
||||||
|
onClick={(): void =>
|
||||||
|
onClickUpgradeHandler(
|
||||||
|
'https://signoz.io/docs/operate/docker-standalone/#upgrade',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t('read_how_to_upgrade')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Version;
|
8
frontend/src/container/Version/styles.ts
Normal file
8
frontend/src/container/Version/styles.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Input } from 'antd';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const InputComponent = styled(Input)`
|
||||||
|
&&& {
|
||||||
|
max-width: 183px;
|
||||||
|
}
|
||||||
|
`;
|
@ -4,6 +4,7 @@ import './ReactI18';
|
|||||||
import AppRoutes from 'AppRoutes';
|
import AppRoutes from 'AppRoutes';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import reportWebVitals from 'reportWebVitals';
|
import reportWebVitals from 'reportWebVitals';
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
@ -12,12 +13,16 @@ if (process.env.NODE_ENV === 'development') {
|
|||||||
reportWebVitals(console.log);
|
reportWebVitals(console.log);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Provider store={store}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<React.StrictMode>
|
<Provider store={store}>
|
||||||
<AppRoutes />
|
<React.StrictMode>
|
||||||
</React.StrictMode>
|
<AppRoutes />
|
||||||
</Provider>,
|
</React.StrictMode>
|
||||||
|
</Provider>
|
||||||
|
</QueryClientProvider>,
|
||||||
document.querySelector('#root'),
|
document.querySelector('#root'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { expect } from '@jest/globals';
|
import { expect } from '@jest/globals';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import getStep, { DefaultStepSize } from 'lib/getStep';
|
import getStep, { DefaultStepSize, MaxDataPoints } from 'lib/getStep';
|
||||||
|
|
||||||
describe('lib/getStep', () => {
|
describe('lib/getStep', () => {
|
||||||
test('should return default step when the given range is less than 1 day', () => {
|
test('should return default step when the given range is less than 1 day', () => {
|
||||||
@ -40,7 +40,8 @@ describe('lib/getStep', () => {
|
|||||||
const startUnix = start.valueOf();
|
const startUnix = start.valueOf();
|
||||||
const endUnix = end.valueOf();
|
const endUnix = end.valueOf();
|
||||||
|
|
||||||
const expectedStepSize = end.diff(start, 'days') * DefaultStepSize;
|
const expectedStepSize = Math.floor(end.diff(start, 's') / MaxDataPoints);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
getStep({
|
getStep({
|
||||||
start: startUnix / 1e3,
|
start: startUnix / 1e3,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { SettingPeroid } from 'container/GeneralSettings';
|
import { SettingPeriod } from 'container/GeneralSettings';
|
||||||
|
|
||||||
const converIntoHr = (value: number, peroid: SettingPeroid): number => {
|
const converIntoHr = (value: number, peroid: SettingPeriod): number => {
|
||||||
if (peroid === 'day') {
|
if (peroid === 'day') {
|
||||||
return value * 24;
|
return value * 24;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { SettingPeroid } from 'container/GeneralSettings';
|
import { SettingPeriod } from 'container/GeneralSettings';
|
||||||
|
|
||||||
const getSettingsPeroid = (hr: number): PayloadProps => {
|
const getSettingsPeroid = (hr: number): PayloadProps => {
|
||||||
if (hr <= 0) {
|
if (hr <= 0) {
|
||||||
@ -30,7 +30,7 @@ const getSettingsPeroid = (hr: number): PayloadProps => {
|
|||||||
|
|
||||||
interface PayloadProps {
|
interface PayloadProps {
|
||||||
value: number;
|
value: number;
|
||||||
peroid: SettingPeroid;
|
peroid: SettingPeriod;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default getSettingsPeroid;
|
export default getSettingsPeroid;
|
||||||
|
@ -30,6 +30,7 @@ const convertToMs = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const DefaultStepSize = 60;
|
export const DefaultStepSize = 60;
|
||||||
|
export const MaxDataPoints = 200;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns relevant step size based on given start and end date.
|
* Returns relevant step size based on given start and end date.
|
||||||
@ -37,13 +38,9 @@ export const DefaultStepSize = 60;
|
|||||||
const getStep = ({ start, end, inputFormat = 'ms' }: GetStepInput): number => {
|
const getStep = ({ start, end, inputFormat = 'ms' }: GetStepInput): number => {
|
||||||
const startDate = dayjs(convertToMs(Number(start), inputFormat));
|
const startDate = dayjs(convertToMs(Number(start), inputFormat));
|
||||||
const endDate = dayjs(convertToMs(Number(end), inputFormat));
|
const endDate = dayjs(convertToMs(Number(end), inputFormat));
|
||||||
const diffDays = Math.abs(endDate.diff(startDate, 'days'));
|
const diffSec = Math.abs(endDate.diff(startDate, 's'));
|
||||||
|
|
||||||
if (diffDays > 1) {
|
return Math.max(Math.floor(diffSec / MaxDataPoints), DefaultStepSize);
|
||||||
return DefaultStepSize * diffDays;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DefaultStepSize;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default getStep;
|
export default getStep;
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
//@ts-nocheck
|
//@ts-nocheck
|
||||||
|
|
||||||
|
import { Card } from 'antd';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import { ForceGraph2D } from 'react-force-graph';
|
import { ForceGraph2D } from 'react-force-graph';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||||
import { getDetailedServiceMapItems, getServiceMapItems } from 'store/actions';
|
import { getDetailedServiceMapItems, ServiceMapStore } from 'store/actions';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { GlobalTime } from 'types/actions/globalTime';
|
import { GlobalTime } from 'types/actions/globalTime';
|
||||||
@ -31,9 +32,8 @@ const Container = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
interface ServiceMapProps extends RouteComponentProps<any> {
|
interface ServiceMapProps extends RouteComponentProps<any> {
|
||||||
serviceMap: serviceMapStore;
|
serviceMap: ServiceMapStore;
|
||||||
globalTime: GlobalTime;
|
globalTime: GlobalTime;
|
||||||
getServiceMapItems: (time: GlobalTime) => void;
|
|
||||||
getDetailedServiceMapItems: (time: GlobalTime) => void;
|
getDetailedServiceMapItems: (time: GlobalTime) => void;
|
||||||
}
|
}
|
||||||
interface graphNode {
|
interface graphNode {
|
||||||
@ -53,29 +53,32 @@ export interface graphDataType {
|
|||||||
function ServiceMap(props: ServiceMapProps): JSX.Element {
|
function ServiceMap(props: ServiceMapProps): JSX.Element {
|
||||||
const fgRef = useRef();
|
const fgRef = useRef();
|
||||||
|
|
||||||
const {
|
const { getDetailedServiceMapItems, globalTime, serviceMap } = props;
|
||||||
getDetailedServiceMapItems,
|
|
||||||
getServiceMapItems,
|
|
||||||
globalTime,
|
|
||||||
serviceMap,
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
/*
|
/*
|
||||||
Call the apis only when the route is loaded.
|
Call the apis only when the route is loaded.
|
||||||
Check this issue: https://github.com/SigNoz/signoz/issues/110
|
Check this issue: https://github.com/SigNoz/signoz/issues/110
|
||||||
*/
|
*/
|
||||||
getServiceMapItems(globalTime);
|
|
||||||
getDetailedServiceMapItems(globalTime);
|
getDetailedServiceMapItems(globalTime);
|
||||||
}, [globalTime, getServiceMapItems, getDetailedServiceMapItems]);
|
}, [globalTime, getDetailedServiceMapItems]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fgRef.current && fgRef.current.d3Force('charge').strength(-400);
|
fgRef.current && fgRef.current.d3Force('charge').strength(-400);
|
||||||
});
|
});
|
||||||
if (!serviceMap.items.length || !serviceMap.services.length) {
|
|
||||||
|
if (serviceMap.loading) {
|
||||||
return <Spinner size="large" tip="Loading..." />;
|
return <Spinner size="large" tip="Loading..." />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!serviceMap.loading && serviceMap.items.length === 0) {
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Card>No Service Found</Card>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const zoomToService = (value: string): void => {
|
const zoomToService = (value: string): void => {
|
||||||
fgRef &&
|
fgRef &&
|
||||||
fgRef.current &&
|
fgRef.current &&
|
||||||
@ -149,7 +152,6 @@ const mapStateToProps = (
|
|||||||
|
|
||||||
export default withRouter(
|
export default withRouter(
|
||||||
connect(mapStateToProps, {
|
connect(mapStateToProps, {
|
||||||
getServiceMapItems,
|
|
||||||
getDetailedServiceMapItems,
|
getDetailedServiceMapItems,
|
||||||
})(ServiceMap),
|
})(ServiceMap),
|
||||||
);
|
);
|
||||||
|
@ -84,7 +84,7 @@ function _UsageExplorer(props: UsageExplorerProps): JSX.Element {
|
|||||||
if (selectedTime && selectedInterval) {
|
if (selectedTime && selectedInterval) {
|
||||||
const maxTime = new Date().getTime() * 1000000;
|
const maxTime = new Date().getTime() * 1000000;
|
||||||
const minTime = maxTime - selectedTime.value * 24 * 3600000 * 1000000;
|
const minTime = maxTime - selectedTime.value * 24 * 3600000 * 1000000;
|
||||||
|
|
||||||
getUsageData(minTime, maxTime, selectedInterval.value, selectedService);
|
getUsageData(minTime, maxTime, selectedInterval.value, selectedService);
|
||||||
}
|
}
|
||||||
}, [selectedTime, selectedInterval, selectedService, getUsageData]);
|
}, [selectedTime, selectedInterval, selectedService, getUsageData]);
|
||||||
|
@ -5,29 +5,32 @@ import CreateAlertChannels from 'container/CreateAlertChannels';
|
|||||||
import GeneralSettings from 'container/GeneralSettings';
|
import GeneralSettings from 'container/GeneralSettings';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
function SettingsPage(): JSX.Element {
|
function SettingsPage(): JSX.Element {
|
||||||
const pathName = history.location.pathname;
|
const pathName = history.location.pathname;
|
||||||
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<RouteTab
|
<RouteTab
|
||||||
{...{
|
{...{
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
Component: GeneralSettings,
|
Component: GeneralSettings,
|
||||||
name: 'General Settings',
|
name: t('routes.general'),
|
||||||
route: ROUTES.SETTINGS,
|
route: ROUTES.SETTINGS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Component: (): JSX.Element => {
|
Component: (): JSX.Element => {
|
||||||
return <CreateAlertChannels preType="slack" />;
|
return <CreateAlertChannels preType="slack" />;
|
||||||
},
|
},
|
||||||
name: 'Alert Channels',
|
name: t('routes.alert_channels'),
|
||||||
route: ROUTES.ALL_CHANNELS,
|
route: ROUTES.ALL_CHANNELS,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
activeKey:
|
activeKey:
|
||||||
pathName === ROUTES.SETTINGS ? 'General Settings' : 'Alert Channels',
|
pathName === ROUTES.SETTINGS
|
||||||
|
? t('routes.general')
|
||||||
|
: t('routes.alert_channels'),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -4,27 +4,30 @@ import AlertChannels from 'container/AllAlertChannels';
|
|||||||
import GeneralSettings from 'container/GeneralSettings';
|
import GeneralSettings from 'container/GeneralSettings';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
function AllAlertChannels(): JSX.Element {
|
function AllAlertChannels(): JSX.Element {
|
||||||
const pathName = history.location.pathname;
|
const pathName = history.location.pathname;
|
||||||
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<RouteTab
|
<RouteTab
|
||||||
{...{
|
{...{
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
Component: GeneralSettings,
|
Component: GeneralSettings,
|
||||||
name: 'General Settings',
|
name: t('routes.general'),
|
||||||
route: ROUTES.SETTINGS,
|
route: ROUTES.SETTINGS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Component: AlertChannels,
|
Component: AlertChannels,
|
||||||
name: 'Alert Channels',
|
name: t('routes.alert_channels'),
|
||||||
route: ROUTES.ALL_CHANNELS,
|
route: ROUTES.ALL_CHANNELS,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
activeKey:
|
activeKey:
|
||||||
pathName === ROUTES.SETTINGS ? 'General Settings' : 'Alert Channels',
|
pathName === ROUTES.SETTINGS
|
||||||
|
? t('routes.general')
|
||||||
|
: t('routes.alert_channels'),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -14,7 +14,7 @@ function SettingsPage(): JSX.Element {
|
|||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
Component: GeneralSettings,
|
Component: GeneralSettings,
|
||||||
name: 'General Settings',
|
name: 'General',
|
||||||
route: ROUTES.SETTINGS,
|
route: ROUTES.SETTINGS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -23,8 +23,7 @@ function SettingsPage(): JSX.Element {
|
|||||||
route: ROUTES.ALL_CHANNELS,
|
route: ROUTES.ALL_CHANNELS,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
activeKey:
|
activeKey: pathName === ROUTES.ALL_CHANNELS ? 'Alert Channels' : 'General',
|
||||||
pathName === ROUTES.ALL_CHANNELS ? 'Alert Channels' : 'General Settings',
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
8
frontend/src/pages/Status/index.tsx
Normal file
8
frontend/src/pages/Status/index.tsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import Version from 'container/Version';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function Status(): JSX.Element {
|
||||||
|
return <Version />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Status;
|
@ -5,6 +5,7 @@ import TraceGraph from 'container/Trace/Graph';
|
|||||||
import Search from 'container/Trace/Search';
|
import Search from 'container/Trace/Search';
|
||||||
import TraceGraphFilter from 'container/Trace/TraceGraphFilter';
|
import TraceGraphFilter from 'container/Trace/TraceGraphFilter';
|
||||||
import TraceTable from 'container/Trace/TraceTable';
|
import TraceTable from 'container/Trace/TraceTable';
|
||||||
|
import getStep from 'lib/getStep';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { connect, useDispatch, useSelector } from 'react-redux';
|
import { connect, useDispatch, useSelector } from 'react-redux';
|
||||||
@ -63,7 +64,7 @@ function Trace({
|
|||||||
current: spansAggregate.currentPage,
|
current: spansAggregate.currentPage,
|
||||||
pageSize: spansAggregate.pageSize,
|
pageSize: spansAggregate.pageSize,
|
||||||
selectedTags,
|
selectedTags,
|
||||||
order: 'ascending',
|
order: spansAggregate.order === 'ascend' ? 'ascending' : 'descending',
|
||||||
});
|
});
|
||||||
}, [
|
}, [
|
||||||
selectedTags,
|
selectedTags,
|
||||||
@ -73,6 +74,7 @@ function Trace({
|
|||||||
getSpansAggregate,
|
getSpansAggregate,
|
||||||
spansAggregate.currentPage,
|
spansAggregate.currentPage,
|
||||||
spansAggregate.pageSize,
|
spansAggregate.pageSize,
|
||||||
|
spansAggregate.order,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -83,7 +85,7 @@ function Trace({
|
|||||||
selectedFilter,
|
selectedFilter,
|
||||||
selectedTags,
|
selectedTags,
|
||||||
start: minTime,
|
start: minTime,
|
||||||
step: 60,
|
step: getStep({ start: minTime, end: maxTime, inputFormat: 'ns' }),
|
||||||
isFilterExclude,
|
isFilterExclude,
|
||||||
});
|
});
|
||||||
}, [
|
}, [
|
||||||
@ -93,8 +95,8 @@ function Trace({
|
|||||||
selectedTags,
|
selectedTags,
|
||||||
maxTime,
|
maxTime,
|
||||||
minTime,
|
minTime,
|
||||||
isFilterExclude,
|
|
||||||
getSpans,
|
getSpans,
|
||||||
|
isFilterExclude,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -2,27 +2,30 @@ import { Typography } from 'antd';
|
|||||||
import getTraceItem from 'api/trace/getTraceItem';
|
import getTraceItem from 'api/trace/getTraceItem';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import TraceDetailContainer from 'container/TraceDetail';
|
import TraceDetailContainer from 'container/TraceDetail';
|
||||||
import useFetch from 'hooks/useFetch';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useQuery } from 'react-query';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { Props as TraceDetailProps } from 'types/api/trace/getTraceItem';
|
import { Props as TraceDetailProps } from 'types/api/trace/getTraceItem';
|
||||||
|
|
||||||
function TraceDetail(): JSX.Element {
|
function TraceDetail(): JSX.Element {
|
||||||
const { id } = useParams<TraceDetailProps>();
|
const { id } = useParams<TraceDetailProps>();
|
||||||
|
const { data: traceDetailResponse, error, isLoading, isError } = useQuery(
|
||||||
|
`getTraceItem/${id}`,
|
||||||
|
() => getTraceItem({ id }),
|
||||||
|
{
|
||||||
|
cacheTime: 3000,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const traceDetailResponse = useFetch(getTraceItem, {
|
if (traceDetailResponse?.error || error || isError) {
|
||||||
id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (traceDetailResponse.error) {
|
|
||||||
return (
|
return (
|
||||||
<Typography>
|
<Typography>
|
||||||
{traceDetailResponse.errorMessage || 'Something went wrong'}
|
{traceDetailResponse?.error || 'Something went wrong'}
|
||||||
</Typography>
|
</Typography>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (traceDetailResponse.loading || traceDetailResponse.payload === undefined) {
|
if (isLoading || !(traceDetailResponse && traceDetailResponse.payload)) {
|
||||||
return <Spinner tip="Loading.." />;
|
return <Spinner tip="Loading.." />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems
|
|||||||
import GetMaxMinTime from 'lib/getMaxMinTime';
|
import GetMaxMinTime from 'lib/getMaxMinTime';
|
||||||
import GetMinMax from 'lib/getMinMax';
|
import GetMinMax from 'lib/getMinMax';
|
||||||
import GetStartAndEndTime from 'lib/getStartAndEndTime';
|
import GetStartAndEndTime from 'lib/getStartAndEndTime';
|
||||||
|
import getStep from 'lib/getStep';
|
||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
@ -45,7 +46,7 @@ export const GetQueryResults = (
|
|||||||
end,
|
end,
|
||||||
query: encodeURIComponent(query.query),
|
query: encodeURIComponent(query.query),
|
||||||
start,
|
start,
|
||||||
step: '60',
|
step: `${getStep({ start, end, inputFormat: 'ms' })}`,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
query: query.query,
|
query: query.query,
|
||||||
|
@ -7,6 +7,7 @@ import { ActionTypes } from './types';
|
|||||||
export interface ServiceMapStore {
|
export interface ServiceMapStore {
|
||||||
items: ServicesMapItem[];
|
items: ServicesMapItem[];
|
||||||
services: ServicesItem[];
|
services: ServicesItem[];
|
||||||
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ServicesItem {
|
export interface ServicesItem {
|
||||||
@ -37,38 +38,39 @@ export interface ServicesAction {
|
|||||||
payload: ServicesItem[];
|
payload: ServicesItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getServiceMapItems = (globalTime: GlobalTime) => {
|
export interface ServiceMapLoading {
|
||||||
return async (dispatch: Dispatch): Promise<void> => {
|
type: ActionTypes.serviceMapLoading;
|
||||||
dispatch<ServiceMapItemAction>({
|
payload: {
|
||||||
type: ActionTypes.getServiceMapItems,
|
loading: ServiceMapStore['loading'];
|
||||||
payload: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
const requestString = `/serviceMapDependencies?start=${globalTime.minTime}&end=${globalTime.maxTime}`;
|
|
||||||
|
|
||||||
const response = await api.get<ServicesMapItem[]>(requestString);
|
|
||||||
|
|
||||||
dispatch<ServiceMapItemAction>({
|
|
||||||
type: ActionTypes.getServiceMapItems,
|
|
||||||
payload: response.data,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export const getDetailedServiceMapItems = (globalTime: GlobalTime) => {
|
export const getDetailedServiceMapItems = (globalTime: GlobalTime) => {
|
||||||
return async (dispatch: Dispatch): Promise<void> => {
|
return async (dispatch: Dispatch): Promise<void> => {
|
||||||
dispatch<ServicesAction>({
|
|
||||||
type: ActionTypes.getServices,
|
|
||||||
payload: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
const requestString = `/services?start=${globalTime.minTime}&end=${globalTime.maxTime}`;
|
const requestString = `/services?start=${globalTime.minTime}&end=${globalTime.maxTime}`;
|
||||||
|
|
||||||
const response = await api.get<ServicesItem[]>(requestString);
|
const serviceMapDependencies = `/serviceMapDependencies?start=${globalTime.minTime}&end=${globalTime.maxTime}`;
|
||||||
|
|
||||||
|
const [serviceMapDependenciesResponse, response] = await Promise.all([
|
||||||
|
api.get<ServicesMapItem[]>(serviceMapDependencies),
|
||||||
|
api.get<ServicesItem[]>(requestString),
|
||||||
|
]);
|
||||||
|
|
||||||
dispatch<ServicesAction>({
|
dispatch<ServicesAction>({
|
||||||
type: ActionTypes.getServices,
|
type: ActionTypes.getServices,
|
||||||
payload: response.data,
|
payload: response.data,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
dispatch<ServiceMapItemAction>({
|
||||||
|
type: ActionTypes.getServiceMapItems,
|
||||||
|
payload: serviceMapDependenciesResponse.data,
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch<ServiceMapLoading>({
|
||||||
|
type: ActionTypes.serviceMapLoading,
|
||||||
|
payload: {
|
||||||
|
loading: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -18,6 +18,7 @@ import {
|
|||||||
parseIsSkippedSelection,
|
parseIsSkippedSelection,
|
||||||
parseQueryIntoCurrent,
|
parseQueryIntoCurrent,
|
||||||
parseQueryIntoFilter,
|
parseQueryIntoFilter,
|
||||||
|
parseQueryIntoOrder,
|
||||||
parseQueryIntoSelectedTags,
|
parseQueryIntoSelectedTags,
|
||||||
parseSelectedFilter,
|
parseSelectedFilter,
|
||||||
} from './util';
|
} from './util';
|
||||||
@ -66,6 +67,11 @@ export const GetInitialTraceFilter = (
|
|||||||
traces.spansAggregate.currentPage,
|
traces.spansAggregate.currentPage,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const parsedQueryOrder = parseQueryIntoOrder(
|
||||||
|
query,
|
||||||
|
traces.spansAggregate.order,
|
||||||
|
);
|
||||||
|
|
||||||
const isSelectionSkipped = parseIsSkippedSelection(query);
|
const isSelectionSkipped = parseIsSkippedSelection(query);
|
||||||
|
|
||||||
const parsedSelectedTags = parseQueryIntoSelectedTags(
|
const parsedSelectedTags = parseQueryIntoSelectedTags(
|
||||||
@ -148,6 +154,7 @@ export const GetInitialTraceFilter = (
|
|||||||
selectedTags: parsedSelectedTags.currentValue,
|
selectedTags: parsedSelectedTags.currentValue,
|
||||||
userSelected: getUserSelected.currentValue,
|
userSelected: getUserSelected.currentValue,
|
||||||
isFilterExclude: getIsFilterExcluded.currentValue,
|
isFilterExclude: getIsFilterExcluded.currentValue,
|
||||||
|
order: parsedQueryOrder.currentValue,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -3,11 +3,13 @@ import getSpansAggregate from 'api/trace/getSpansAggregate';
|
|||||||
import { Dispatch, Store } from 'redux';
|
import { Dispatch, Store } from 'redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { UPDATE_SPANS_AGGREEGATE } from 'types/actions/trace';
|
import { UPDATE_SPANS_AGGREGATE } from 'types/actions/trace';
|
||||||
import { Props as GetSpanAggregateProps } from 'types/api/trace/getSpanAggregate';
|
import { Props as GetSpanAggregateProps } from 'types/api/trace/getSpanAggregate';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import { TraceReducer } from 'types/reducer/trace';
|
import { TraceReducer } from 'types/reducer/trace';
|
||||||
|
|
||||||
|
import { updateURL } from './util';
|
||||||
|
|
||||||
export const GetSpansAggregate = (
|
export const GetSpansAggregate = (
|
||||||
props: GetSpansAggregateProps,
|
props: GetSpansAggregateProps,
|
||||||
): ((
|
): ((
|
||||||
@ -29,10 +31,12 @@ export const GetSpansAggregate = (
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const order = props.order === 'ascending' ? 'ascend' : 'descend';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// triggering loading
|
// triggering loading
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UPDATE_SPANS_AGGREEGATE,
|
type: UPDATE_SPANS_AGGREGATE,
|
||||||
payload: {
|
payload: {
|
||||||
spansAggregate: {
|
spansAggregate: {
|
||||||
currentPage: props.current,
|
currentPage: props.current,
|
||||||
@ -41,6 +45,7 @@ export const GetSpansAggregate = (
|
|||||||
error: false,
|
error: false,
|
||||||
total: spansAggregate.total,
|
total: spansAggregate.total,
|
||||||
pageSize: props.pageSize,
|
pageSize: props.pageSize,
|
||||||
|
order,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -53,12 +58,12 @@ export const GetSpansAggregate = (
|
|||||||
offset: props.current * props.pageSize - props.pageSize,
|
offset: props.current * props.pageSize - props.pageSize,
|
||||||
selectedTags: props.selectedTags,
|
selectedTags: props.selectedTags,
|
||||||
isFilterExclude: traces.isFilterExclude,
|
isFilterExclude: traces.isFilterExclude,
|
||||||
order: props.order,
|
order,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.statusCode === 200) {
|
if (response.statusCode === 200) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UPDATE_SPANS_AGGREEGATE,
|
type: UPDATE_SPANS_AGGREGATE,
|
||||||
payload: {
|
payload: {
|
||||||
spansAggregate: {
|
spansAggregate: {
|
||||||
currentPage: props.current,
|
currentPage: props.current,
|
||||||
@ -67,16 +72,27 @@ export const GetSpansAggregate = (
|
|||||||
error: false,
|
error: false,
|
||||||
total: response.payload.totalSpans,
|
total: response.payload.totalSpans,
|
||||||
pageSize: props.pageSize,
|
pageSize: props.pageSize,
|
||||||
|
order,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateURL(
|
||||||
|
traces.selectedFilter,
|
||||||
|
traces.filterToFetchData,
|
||||||
|
props.current,
|
||||||
|
traces.selectedTags,
|
||||||
|
traces.isFilterExclude,
|
||||||
|
traces.userSelectedFilter,
|
||||||
|
order,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
notification.error({
|
notification.error({
|
||||||
message: response.error || 'Something went wrong',
|
message: response.error || 'Something went wrong',
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UPDATE_SPANS_AGGREEGATE,
|
type: UPDATE_SPANS_AGGREGATE,
|
||||||
payload: {
|
payload: {
|
||||||
spansAggregate: {
|
spansAggregate: {
|
||||||
currentPage: props.current,
|
currentPage: props.current,
|
||||||
@ -85,13 +101,14 @@ export const GetSpansAggregate = (
|
|||||||
error: true,
|
error: true,
|
||||||
total: spansAggregate.total,
|
total: spansAggregate.total,
|
||||||
pageSize: props.pageSize,
|
pageSize: props.pageSize,
|
||||||
|
order,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UPDATE_SPANS_AGGREEGATE,
|
type: UPDATE_SPANS_AGGREGATE,
|
||||||
payload: {
|
payload: {
|
||||||
spansAggregate: {
|
spansAggregate: {
|
||||||
currentPage: props.current,
|
currentPage: props.current,
|
||||||
@ -100,6 +117,7 @@ export const GetSpansAggregate = (
|
|||||||
error: true,
|
error: true,
|
||||||
total: spansAggregate.total,
|
total: spansAggregate.total,
|
||||||
pageSize: props.pageSize,
|
pageSize: props.pageSize,
|
||||||
|
order,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
export * from './current';
|
|
||||||
export * from './filter';
|
export * from './filter';
|
||||||
export * from './filterToFetchData';
|
export * from './filterToFetchData';
|
||||||
export * from './isFilterExclude';
|
export * from './isFilterExclude';
|
||||||
@ -6,3 +5,5 @@ export * from './minMaxTime';
|
|||||||
export * from './selectedFilter';
|
export * from './selectedFilter';
|
||||||
export * from './selectedTags';
|
export * from './selectedTags';
|
||||||
export * from './skippedSelected';
|
export * from './skippedSelected';
|
||||||
|
export * from './spanAggregateCurrentPage';
|
||||||
|
export * from './spanAggregateOrder';
|
||||||
|
@ -10,7 +10,7 @@ export const parseQueryIntoCurrent = (
|
|||||||
|
|
||||||
let current = 1;
|
let current = 1;
|
||||||
|
|
||||||
const selected = url.get('current');
|
const selected = url.get('spanAggregateCurrentPage');
|
||||||
|
|
||||||
if (selected) {
|
if (selected) {
|
||||||
try {
|
try {
|
@ -0,0 +1,39 @@
|
|||||||
|
import { TraceReducer } from 'types/reducer/trace';
|
||||||
|
|
||||||
|
import { ParsedUrl } from '../util';
|
||||||
|
|
||||||
|
export const parseQueryIntoOrder = (
|
||||||
|
query: string,
|
||||||
|
stateCurrent: TraceReducer['spansAggregate']['order'],
|
||||||
|
): ParsedUrl<TraceReducer['spansAggregate']['order']> => {
|
||||||
|
const url = new URLSearchParams(query);
|
||||||
|
|
||||||
|
let current = 'ascend';
|
||||||
|
|
||||||
|
const selected = url.get('spanAggregateOrder');
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
try {
|
||||||
|
const parsedValue = selected;
|
||||||
|
|
||||||
|
if (parsedValue && typeof parsedValue === 'string') {
|
||||||
|
current = parsedValue;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
console.log('error while parsing json');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
return {
|
||||||
|
currentValue: current,
|
||||||
|
urlValue: current,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentValue: stateCurrent,
|
||||||
|
urlValue: current,
|
||||||
|
};
|
||||||
|
};
|
@ -44,9 +44,9 @@ export const SelectedTraceFilter = (props: {
|
|||||||
traces.filterToFetchData,
|
traces.filterToFetchData,
|
||||||
traces.spansAggregate.currentPage,
|
traces.spansAggregate.currentPage,
|
||||||
traces.selectedTags,
|
traces.selectedTags,
|
||||||
traces.filter,
|
|
||||||
traces.isFilterExclude,
|
traces.isFilterExclude,
|
||||||
traces.userSelectedFilter,
|
traces.userSelectedFilter,
|
||||||
|
traces.spansAggregate.order,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { UPDATE_TAG_MODAL_VISIBLITY } from 'types/actions/trace';
|
import { UPDATE_TAG_MODAL_VISIBILITY } from 'types/actions/trace';
|
||||||
import { TraceReducer } from 'types/reducer/trace';
|
import { TraceReducer } from 'types/reducer/trace';
|
||||||
|
|
||||||
export const UpdateTagVisiblity = (
|
export const UpdateTagVisibility = (
|
||||||
isTagModalOpen: TraceReducer['isTagModalOpen'],
|
isTagModalOpen: TraceReducer['isTagModalOpen'],
|
||||||
): ((dispatch: Dispatch<AppActions>) => void) => {
|
): ((dispatch: Dispatch<AppActions>) => void) => {
|
||||||
return (dispatch): void => {
|
return (dispatch): void => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UPDATE_TAG_MODAL_VISIBLITY,
|
type: UPDATE_TAG_MODAL_VISIBILITY,
|
||||||
payload: {
|
payload: {
|
||||||
isTagModalOpen,
|
isTagModalOpen,
|
||||||
},
|
},
|
||||||
|
@ -18,11 +18,11 @@ export function isTraceFilterEnum(
|
|||||||
export const updateURL = (
|
export const updateURL = (
|
||||||
selectedFilter: TraceReducer['selectedFilter'],
|
selectedFilter: TraceReducer['selectedFilter'],
|
||||||
filterToFetchData: TraceReducer['filterToFetchData'],
|
filterToFetchData: TraceReducer['filterToFetchData'],
|
||||||
current: TraceReducer['spansAggregate']['total'],
|
spanAggregateCurrentPage: TraceReducer['spansAggregate']['currentPage'],
|
||||||
selectedTags: TraceReducer['selectedTags'],
|
selectedTags: TraceReducer['selectedTags'],
|
||||||
filter: TraceReducer['filter'],
|
|
||||||
isFilterExclude: TraceReducer['isFilterExclude'],
|
isFilterExclude: TraceReducer['isFilterExclude'],
|
||||||
userSelectedFilter: TraceReducer['userSelectedFilter'],
|
userSelectedFilter: TraceReducer['userSelectedFilter'],
|
||||||
|
spanAggregateOrder: TraceReducer['spansAggregate']['order'],
|
||||||
): void => {
|
): void => {
|
||||||
const search = new URLSearchParams(window.location.search);
|
const search = new URLSearchParams(window.location.search);
|
||||||
const preResult: { key: string; value: string }[] = [];
|
const preResult: { key: string; value: string }[] = [];
|
||||||
@ -30,11 +30,12 @@ export const updateURL = (
|
|||||||
const keyToSkip = [
|
const keyToSkip = [
|
||||||
'selected',
|
'selected',
|
||||||
'filterToFetchData',
|
'filterToFetchData',
|
||||||
'current',
|
|
||||||
'selectedTags',
|
'selectedTags',
|
||||||
'filter',
|
'filter',
|
||||||
'isFilterExclude',
|
'isFilterExclude',
|
||||||
'userSelectedFilter',
|
'userSelectedFilter',
|
||||||
|
'spanAggregateCurrentPage',
|
||||||
|
'spanAggregateOrder',
|
||||||
];
|
];
|
||||||
|
|
||||||
search.forEach((value, key) => {
|
search.forEach((value, key) => {
|
||||||
@ -51,15 +52,15 @@ export const updateURL = (
|
|||||||
Object.fromEntries(selectedFilter),
|
Object.fromEntries(selectedFilter),
|
||||||
)}&filterToFetchData=${JSON.stringify(
|
)}&filterToFetchData=${JSON.stringify(
|
||||||
filterToFetchData,
|
filterToFetchData,
|
||||||
)}¤t=${current}&selectedTags=${JSON.stringify(
|
)}&spanAggregateCurrentPage=${spanAggregateCurrentPage}&selectedTags=${JSON.stringify(
|
||||||
selectedTags,
|
selectedTags,
|
||||||
)}&filter=${JSON.stringify(Object.fromEntries(filter))}&${preResult
|
)}&${preResult
|
||||||
.map((e) => `${e.key}=${e.value}`)
|
.map((e) => `${e.key}=${e.value}`)
|
||||||
.join('&')}&isFilterExclude=${JSON.stringify(
|
.join('&')}&isFilterExclude=${JSON.stringify(
|
||||||
Object.fromEntries(isFilterExclude),
|
Object.fromEntries(isFilterExclude),
|
||||||
)}&userSelectedFilter=${JSON.stringify(
|
)}&userSelectedFilter=${JSON.stringify(
|
||||||
Object.fromEntries(userSelectedFilter),
|
Object.fromEntries(userSelectedFilter),
|
||||||
)}`,
|
)}&spanAggregateCurrentPage=${spanAggregateCurrentPage}&spanAggregateOrder=${spanAggregateOrder}`,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,14 +1,22 @@
|
|||||||
import { ServiceMapItemAction, ServicesAction } from './serviceMap';
|
import {
|
||||||
|
ServiceMapItemAction,
|
||||||
|
ServiceMapLoading,
|
||||||
|
ServicesAction,
|
||||||
|
} from './serviceMap';
|
||||||
import { GetUsageDataAction } from './usage';
|
import { GetUsageDataAction } from './usage';
|
||||||
|
|
||||||
export enum ActionTypes {
|
export enum ActionTypes {
|
||||||
updateTraceFilters = 'UPDATE_TRACES_FILTER',
|
|
||||||
updateTimeInterval = 'UPDATE_TIME_INTERVAL',
|
updateTimeInterval = 'UPDATE_TIME_INTERVAL',
|
||||||
getServiceMapItems = 'GET_SERVICE_MAP_ITEMS',
|
getServiceMapItems = 'GET_SERVICE_MAP_ITEMS',
|
||||||
getServices = 'GET_SERVICES',
|
getServices = 'GET_SERVICES',
|
||||||
getUsageData = 'GET_USAGE_DATE',
|
getUsageData = 'GET_USAGE_DATE',
|
||||||
fetchTraces = 'FETCH_TRACES',
|
fetchTraces = 'FETCH_TRACES',
|
||||||
fetchTraceItem = 'FETCH_TRACE_ITEM',
|
fetchTraceItem = 'FETCH_TRACE_ITEM',
|
||||||
|
serviceMapLoading = 'UPDATE_SERVICE_MAP_LOADING',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Action = GetUsageDataAction | ServicesAction | ServiceMapItemAction;
|
export type Action =
|
||||||
|
| GetUsageDataAction
|
||||||
|
| ServicesAction
|
||||||
|
| ServiceMapItemAction
|
||||||
|
| ServiceMapLoading;
|
||||||
|
@ -7,6 +7,10 @@ import {
|
|||||||
LOGGED_IN,
|
LOGGED_IN,
|
||||||
SIDEBAR_COLLAPSE,
|
SIDEBAR_COLLAPSE,
|
||||||
SWITCH_DARK_MODE,
|
SWITCH_DARK_MODE,
|
||||||
|
UPDATE_CURRENT_ERROR,
|
||||||
|
UPDATE_CURRENT_VERSION,
|
||||||
|
UPDATE_LATEST_VERSION,
|
||||||
|
UPDATE_LATEST_VERSION_ERROR,
|
||||||
} from 'types/actions/app';
|
} from 'types/actions/app';
|
||||||
import InitialValueTypes from 'types/reducer/app';
|
import InitialValueTypes from 'types/reducer/app';
|
||||||
|
|
||||||
@ -14,6 +18,10 @@ const InitialValue: InitialValueTypes = {
|
|||||||
isDarkMode: getTheme() === 'darkMode',
|
isDarkMode: getTheme() === 'darkMode',
|
||||||
isLoggedIn: getLocalStorageKey(IS_LOGGED_IN) === 'yes',
|
isLoggedIn: getLocalStorageKey(IS_LOGGED_IN) === 'yes',
|
||||||
isSideBarCollapsed: getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true',
|
isSideBarCollapsed: getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true',
|
||||||
|
currentVersion: '',
|
||||||
|
latestVersion: '',
|
||||||
|
isCurrentVersionError: false,
|
||||||
|
isLatestVersionError: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const appReducer = (
|
const appReducer = (
|
||||||
@ -42,6 +50,28 @@ const appReducer = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case UPDATE_CURRENT_VERSION: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
currentVersion: action.payload.currentVersion,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case UPDATE_LATEST_VERSION: {
|
||||||
|
return { ...state, latestVersion: action.payload.latestVersion };
|
||||||
|
}
|
||||||
|
|
||||||
|
case UPDATE_CURRENT_ERROR: {
|
||||||
|
return { ...state, isCurrentVersionError: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
case UPDATE_LATEST_VERSION_ERROR: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
isLatestVersionError: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import { Action, ActionTypes, ServiceMapStore } from 'store/actions';
|
|||||||
const initialState: ServiceMapStore = {
|
const initialState: ServiceMapStore = {
|
||||||
items: [],
|
items: [],
|
||||||
services: [],
|
services: [],
|
||||||
|
loading: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ServiceMapReducer = (
|
export const ServiceMapReducer = (
|
||||||
@ -20,6 +21,12 @@ export const ServiceMapReducer = (
|
|||||||
...state,
|
...state,
|
||||||
services: action.payload,
|
services: action.payload,
|
||||||
};
|
};
|
||||||
|
case ActionTypes.serviceMapLoading: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: action.payload.loading,
|
||||||
|
};
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,10 @@ import {
|
|||||||
UPDATE_SELECTED_FUNCTION,
|
UPDATE_SELECTED_FUNCTION,
|
||||||
UPDATE_SELECTED_GROUP_BY,
|
UPDATE_SELECTED_GROUP_BY,
|
||||||
UPDATE_SELECTED_TAGS,
|
UPDATE_SELECTED_TAGS,
|
||||||
UPDATE_SPANS_AGGREEGATE,
|
UPDATE_SPAN_ORDER,
|
||||||
UPDATE_TAG_MODAL_VISIBLITY,
|
UPDATE_SPANS_AGGREGATE,
|
||||||
|
UPDATE_SPANS_AGGREGATE_PAGE_NUMBER,
|
||||||
|
UPDATE_TAG_MODAL_VISIBILITY,
|
||||||
UPDATE_TRACE_FILTER,
|
UPDATE_TRACE_FILTER,
|
||||||
UPDATE_TRACE_FILTER_LOADING,
|
UPDATE_TRACE_FILTER_LOADING,
|
||||||
UPDATE_TRACE_GRAPH_ERROR,
|
UPDATE_TRACE_GRAPH_ERROR,
|
||||||
@ -37,6 +39,7 @@ const initialValue: TraceReducer = {
|
|||||||
error: false,
|
error: false,
|
||||||
total: 0,
|
total: 0,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
|
order: 'ascend',
|
||||||
},
|
},
|
||||||
selectedGroupBy: '',
|
selectedGroupBy: '',
|
||||||
selectedFunction: 'count',
|
selectedFunction: 'count',
|
||||||
@ -71,6 +74,7 @@ const traceReducer = (
|
|||||||
selectedTags,
|
selectedTags,
|
||||||
userSelected,
|
userSelected,
|
||||||
isFilterExclude,
|
isFilterExclude,
|
||||||
|
order,
|
||||||
} = payload;
|
} = payload;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -84,6 +88,7 @@ const traceReducer = (
|
|||||||
spansAggregate: {
|
spansAggregate: {
|
||||||
...state.spansAggregate,
|
...state.spansAggregate,
|
||||||
currentPage: current,
|
currentPage: current,
|
||||||
|
order,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -115,14 +120,14 @@ const traceReducer = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
case UPDATE_SPANS_AGGREEGATE: {
|
case UPDATE_SPANS_AGGREGATE: {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
spansAggregate: action.payload.spansAggregate,
|
spansAggregate: action.payload.spansAggregate,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
case UPDATE_TAG_MODAL_VISIBLITY: {
|
case UPDATE_TAG_MODAL_VISIBILITY: {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
isTagModalOpen: action.payload.isTagModalOpen,
|
isTagModalOpen: action.payload.isTagModalOpen,
|
||||||
@ -199,6 +204,26 @@ const traceReducer = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case UPDATE_SPAN_ORDER: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
spansAggregate: {
|
||||||
|
...state.spansAggregate,
|
||||||
|
order: action.payload.order,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case UPDATE_SPANS_AGGREGATE_PAGE_NUMBER: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
spansAggregate: {
|
||||||
|
...state.spansAggregate,
|
||||||
|
currentPage: action.payload.currentPage,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
export const SWITCH_DARK_MODE = 'SWITCH_DARK_MODE';
|
export const SWITCH_DARK_MODE = 'SWITCH_DARK_MODE';
|
||||||
export const LOGGED_IN = 'LOGGED_IN';
|
export const LOGGED_IN = 'LOGGED_IN';
|
||||||
export const SIDEBAR_COLLAPSE = 'SIDEBAR_COLLAPSE';
|
export const SIDEBAR_COLLAPSE = 'SIDEBAR_COLLAPSE';
|
||||||
|
|
||||||
|
export const UPDATE_CURRENT_VERSION = 'UPDATE_CURRENT_VERSION';
|
||||||
|
export const UPDATE_LATEST_VERSION = 'UPDATE_LATEST_VERSION';
|
||||||
|
|
||||||
|
export const UPDATE_CURRENT_ERROR = 'UPDATE_CURRENT_ERROR';
|
||||||
|
export const UPDATE_LATEST_VERSION_ERROR = 'UPDATE_LATEST_VERSION_ERROR';
|
||||||
|
|
||||||
export interface SwitchDarkMode {
|
export interface SwitchDarkMode {
|
||||||
type: typeof SWITCH_DARK_MODE;
|
type: typeof SWITCH_DARK_MODE;
|
||||||
}
|
}
|
||||||
@ -15,4 +23,31 @@ export interface SideBarCollapse {
|
|||||||
payload: boolean;
|
payload: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AppAction = SwitchDarkMode | LoggedInUser | SideBarCollapse;
|
export interface UpdateAppVersion {
|
||||||
|
type: typeof UPDATE_CURRENT_VERSION;
|
||||||
|
payload: {
|
||||||
|
currentVersion: AppReducer['currentVersion'];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateLatestVersion {
|
||||||
|
type: typeof UPDATE_LATEST_VERSION;
|
||||||
|
payload: {
|
||||||
|
latestVersion: AppReducer['latestVersion'];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateVersionError {
|
||||||
|
type: typeof UPDATE_CURRENT_ERROR | typeof UPDATE_LATEST_VERSION_ERROR;
|
||||||
|
payload: {
|
||||||
|
isError: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AppAction =
|
||||||
|
| SwitchDarkMode
|
||||||
|
| LoggedInUser
|
||||||
|
| SideBarCollapse
|
||||||
|
| UpdateAppVersion
|
||||||
|
| UpdateLatestVersion
|
||||||
|
| UpdateVersionError;
|
||||||
|
@ -7,9 +7,9 @@ export const UPDATE_TRACE_FILTER_LOADING = 'UPDATE_TRACE_FILTER_LOADING';
|
|||||||
export const SELECT_TRACE_FILTER = 'SELECT_TRACE_FILTER';
|
export const SELECT_TRACE_FILTER = 'SELECT_TRACE_FILTER';
|
||||||
export const UPDATE_ALL_FILTERS = 'UPDATE_ALL_FILTERS';
|
export const UPDATE_ALL_FILTERS = 'UPDATE_ALL_FILTERS';
|
||||||
export const UPDATE_SELECTED_TAGS = 'UPDATE_SELECTED_TAGS';
|
export const UPDATE_SELECTED_TAGS = 'UPDATE_SELECTED_TAGS';
|
||||||
export const UPDATE_TAG_MODAL_VISIBLITY = 'UPDATE_TAG_MODAL_VISIBLITY';
|
export const UPDATE_TAG_MODAL_VISIBILITY = 'UPDATE_TAG_MODAL_VISIBILITY';
|
||||||
|
|
||||||
export const UPDATE_SPANS_AGGREEGATE = 'UPDATE_SPANS_AGGREEGATE';
|
export const UPDATE_SPANS_AGGREGATE = 'UPDATE_SPANS_AGGREGATE';
|
||||||
|
|
||||||
export const UPDATE_IS_TAG_ERROR = 'UPDATE_IS_TAG_ERROR';
|
export const UPDATE_IS_TAG_ERROR = 'UPDATE_IS_TAG_ERROR';
|
||||||
|
|
||||||
@ -25,6 +25,10 @@ export const UPDATE_FILTER_RESPONSE_SELECTED =
|
|||||||
'UPDATE_FILTER_RESPONSE_SELECTED';
|
'UPDATE_FILTER_RESPONSE_SELECTED';
|
||||||
export const UPDATE_FILTER_EXCLUDE = 'UPDATE_FILTER_EXCLUDE';
|
export const UPDATE_FILTER_EXCLUDE = 'UPDATE_FILTER_EXCLUDE';
|
||||||
|
|
||||||
|
export const UPDATE_SPAN_ORDER = 'UPDATE_SPAN_ORDER';
|
||||||
|
export const UPDATE_SPANS_AGGREGATE_PAGE_NUMBER =
|
||||||
|
'UPDATE_SPANS_AGGREGATE_PAGE_NUMBER';
|
||||||
|
|
||||||
export interface UpdateFilter {
|
export interface UpdateFilter {
|
||||||
type: typeof UPDATE_TRACE_FILTER;
|
type: typeof UPDATE_TRACE_FILTER;
|
||||||
payload: {
|
payload: {
|
||||||
@ -33,14 +37,14 @@ export interface UpdateFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateSpansAggregate {
|
export interface UpdateSpansAggregate {
|
||||||
type: typeof UPDATE_SPANS_AGGREEGATE;
|
type: typeof UPDATE_SPANS_AGGREGATE;
|
||||||
payload: {
|
payload: {
|
||||||
spansAggregate: TraceReducer['spansAggregate'];
|
spansAggregate: TraceReducer['spansAggregate'];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateTagVisiblity {
|
export interface UpdateTagVisibility {
|
||||||
type: typeof UPDATE_TAG_MODAL_VISIBLITY;
|
type: typeof UPDATE_TAG_MODAL_VISIBILITY;
|
||||||
payload: {
|
payload: {
|
||||||
isTagModalOpen: TraceReducer['isTagModalOpen'];
|
isTagModalOpen: TraceReducer['isTagModalOpen'];
|
||||||
};
|
};
|
||||||
@ -70,6 +74,7 @@ export interface UpdateAllFilters {
|
|||||||
selectedTags: TraceReducer['selectedTags'];
|
selectedTags: TraceReducer['selectedTags'];
|
||||||
userSelected: TraceReducer['userSelectedFilter'];
|
userSelected: TraceReducer['userSelectedFilter'];
|
||||||
isFilterExclude: TraceReducer['isFilterExclude'];
|
isFilterExclude: TraceReducer['isFilterExclude'];
|
||||||
|
order: TraceReducer['spansAggregate']['order'];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,6 +154,20 @@ export interface UpdateSpans {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UpdateSpansAggregatePageNumber {
|
||||||
|
type: typeof UPDATE_SPANS_AGGREGATE_PAGE_NUMBER;
|
||||||
|
payload: {
|
||||||
|
currentPage: TraceReducer['spansAggregate']['currentPage'];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateSpanOrder {
|
||||||
|
type: typeof UPDATE_SPAN_ORDER;
|
||||||
|
payload: {
|
||||||
|
order: TraceReducer['spansAggregate']['order'];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type TraceActions =
|
export type TraceActions =
|
||||||
| UpdateFilter
|
| UpdateFilter
|
||||||
| GetTraceFilter
|
| GetTraceFilter
|
||||||
@ -156,7 +175,7 @@ export type TraceActions =
|
|||||||
| SelectTraceFilter
|
| SelectTraceFilter
|
||||||
| UpdateAllFilters
|
| UpdateAllFilters
|
||||||
| UpdateSelectedTags
|
| UpdateSelectedTags
|
||||||
| UpdateTagVisiblity
|
| UpdateTagVisibility
|
||||||
| UpdateSpansAggregate
|
| UpdateSpansAggregate
|
||||||
| UpdateIsTagError
|
| UpdateIsTagError
|
||||||
| UpdateSelectedGroupBy
|
| UpdateSelectedGroupBy
|
||||||
@ -166,4 +185,6 @@ export type TraceActions =
|
|||||||
| UpdateSpans
|
| UpdateSpans
|
||||||
| ResetTraceFilter
|
| ResetTraceFilter
|
||||||
| UpdateSelected
|
| UpdateSelected
|
||||||
| UpdateFilterExclude;
|
| UpdateFilterExclude
|
||||||
|
| UpdateSpanOrder
|
||||||
|
| UpdateSpansAggregatePageNumber;
|
||||||
|
5
frontend/src/types/api/disks/getDisks.ts
Normal file
5
frontend/src/types/api/disks/getDisks.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export type PayloadProps = IDiskType[];
|
||||||
|
export interface IDiskType {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
export interface PayloadProps {
|
export interface PayloadProps {
|
||||||
metrics_ttl_duration_hrs: number;
|
metrics_ttl_duration_hrs: number;
|
||||||
|
metrics_move_ttl_duration_hrs?: number;
|
||||||
traces_ttl_duration_hrs: number;
|
traces_ttl_duration_hrs: number;
|
||||||
|
traces_move_ttl_duration_hrs?: number;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
export interface Props {
|
export interface Props {
|
||||||
type: 'metrics' | 'traces';
|
type: 'metrics' | 'traces';
|
||||||
duration: string;
|
totalDuration: string;
|
||||||
|
coldStorage?: 's3' | null;
|
||||||
|
toColdDuration?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PayloadProps {
|
export interface PayloadProps {
|
||||||
|
@ -7,7 +7,7 @@ export interface Props {
|
|||||||
limit: number;
|
limit: number;
|
||||||
offset: number;
|
offset: number;
|
||||||
selectedTags: TraceReducer['selectedTags'];
|
selectedTags: TraceReducer['selectedTags'];
|
||||||
order?: 'descending' | 'ascending';
|
order?: TraceReducer['spansAggregate']['order'];
|
||||||
isFilterExclude: TraceReducer['isFilterExclude'];
|
isFilterExclude: TraceReducer['isFilterExclude'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
13
frontend/src/types/api/trace/getTagValue.ts
Normal file
13
frontend/src/types/api/trace/getTagValue.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
start: GlobalReducer['minTime'];
|
||||||
|
end: GlobalReducer['maxTime'];
|
||||||
|
tagKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Value {
|
||||||
|
tagValues: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PayloadProps = Value[];
|
18
frontend/src/types/api/user/getLatestVersion.ts
Normal file
18
frontend/src/types/api/user/getLatestVersion.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export interface PayloadProps {
|
||||||
|
body: string;
|
||||||
|
created_at: string;
|
||||||
|
draft: boolean;
|
||||||
|
html_url: string;
|
||||||
|
id: number;
|
||||||
|
mentions_count: number;
|
||||||
|
name: string;
|
||||||
|
node_id: number;
|
||||||
|
prerelease: boolean;
|
||||||
|
published_at: string;
|
||||||
|
tag_name: number;
|
||||||
|
tarball_url: string;
|
||||||
|
target_commitish: string;
|
||||||
|
upload_url: string;
|
||||||
|
url: string;
|
||||||
|
zipball_url: string;
|
||||||
|
}
|
@ -2,4 +2,8 @@ export default interface AppReducer {
|
|||||||
isDarkMode: boolean;
|
isDarkMode: boolean;
|
||||||
isLoggedIn: boolean;
|
isLoggedIn: boolean;
|
||||||
isSideBarCollapsed: boolean;
|
isSideBarCollapsed: boolean;
|
||||||
|
currentVersion: string;
|
||||||
|
latestVersion: string;
|
||||||
|
isCurrentVersionError: boolean;
|
||||||
|
isLatestVersionError: boolean;
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,10 @@ export interface TraceReducer {
|
|||||||
filter: Map<TraceFilterEnum, Record<string, string>>;
|
filter: Map<TraceFilterEnum, Record<string, string>>;
|
||||||
filterToFetchData: TraceFilterEnum[];
|
filterToFetchData: TraceFilterEnum[];
|
||||||
filterLoading: boolean;
|
filterLoading: boolean;
|
||||||
|
|
||||||
selectedFilter: Map<TraceFilterEnum, string[]>;
|
selectedFilter: Map<TraceFilterEnum, string[]>;
|
||||||
userSelectedFilter: Map<TraceFilterEnum, string[]>;
|
userSelectedFilter: Map<TraceFilterEnum, string[]>;
|
||||||
|
|
||||||
isFilterExclude: Map<TraceFilterEnum, boolean>;
|
isFilterExclude: Map<TraceFilterEnum, boolean>;
|
||||||
selectedTags: Tags[];
|
selectedTags: Tags[];
|
||||||
isTagModalOpen: boolean;
|
isTagModalOpen: boolean;
|
||||||
@ -18,6 +20,7 @@ export interface TraceReducer {
|
|||||||
error: boolean;
|
error: boolean;
|
||||||
total: number;
|
total: number;
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
|
order: string;
|
||||||
};
|
};
|
||||||
selectedGroupBy: string;
|
selectedGroupBy: string;
|
||||||
selectedFunction: string;
|
selectedFunction: string;
|
||||||
|
@ -25,8 +25,7 @@ export const getSpanTreeMetadata = (
|
|||||||
globalEnd = Math.max(globalEnd, endTime);
|
globalEnd = Math.max(globalEnd, endTime);
|
||||||
if (treeNode.hasError) {
|
if (treeNode.hasError) {
|
||||||
treeNode.serviceColour = errorColor;
|
treeNode.serviceColour = errorColor;
|
||||||
}
|
} else treeNode.serviceColour = spanServiceColours[treeNode.serviceName];
|
||||||
treeNode.serviceColour = spanServiceColours[treeNode.serviceName];
|
|
||||||
treeNode.children.forEach((childNode) => {
|
treeNode.children.forEach((childNode) => {
|
||||||
traverse(childNode, level + 1);
|
traverse(childNode, level + 1);
|
||||||
});
|
});
|
||||||
|
@ -22,5 +22,5 @@
|
|||||||
"plugins": [{ "name": "typescript-plugin-css-modules" }]
|
"plugins": [{ "name": "typescript-plugin-css-modules" }]
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules"],
|
"exclude": ["node_modules"],
|
||||||
"include": ["./src"]
|
"include": ["./src", "./babel.config.js", "./jest.config.ts"]
|
||||||
}
|
}
|
||||||
|
1188
frontend/yarn.lock
1188
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user