mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-13 19:35:56 +08:00
fix: merge conflit resolved
This commit is contained in:
commit
cbaf9b009c
@ -106,32 +106,70 @@ Need to update [https://github.com/SigNoz/charts](https://github.com/SigNoz/char
|
|||||||
- [k3d](https://k3d.io/#installation)
|
- [k3d](https://k3d.io/#installation)
|
||||||
- [minikube](https://minikube.sigs.k8s.io/docs/start/)
|
- [minikube](https://minikube.sigs.k8s.io/docs/start/)
|
||||||
- create a k8s cluster and make sure `kubectl` points to the locally created k8s cluster
|
- create a k8s cluster and make sure `kubectl` points to the locally created k8s cluster
|
||||||
- run `helm install -n platform --create-namespace my-release charts/signoz` to install SigNoz chart
|
- run `make dev-install` to install SigNoz chart with `my-release` release name in `platform` namespace.
|
||||||
- run `kubectl -n platform port-forward svc/my-release-frontend 3301:3301` to make SigNoz UI available at [localhost:3301](http://localhost:3301)
|
- run `kubectl -n platform port-forward svc/my-release-signoz-frontend 3301:3301` to make SigNoz UI available at [localhost:3301](http://localhost:3301)
|
||||||
|
|
||||||
|
**To install HotROD sample app:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sL https://github.com/SigNoz/signoz/raw/main/sample-apps/hotrod/hotrod-install.sh \
|
||||||
|
| HELM_RELEASE=my-release SIGNOZ_NAMESPACE=platform bash
|
||||||
|
```
|
||||||
|
|
||||||
**To load data with HotROD sample app:**
|
**To load data with HotROD sample app:**
|
||||||
|
|
||||||
```sh
|
```bash
|
||||||
kubectl create ns sample-application
|
|
||||||
|
|
||||||
kubectl -n sample-application apply -f https://raw.githubusercontent.com/SigNoz/signoz/main/sample-apps/hotrod/hotrod.yaml
|
|
||||||
|
|
||||||
kubectl -n sample-application run strzal --image=djbingham/curl \
|
kubectl -n sample-application run strzal --image=djbingham/curl \
|
||||||
--restart='OnFailure' -i --tty --rm --command -- curl -X POST -F \
|
--restart='OnFailure' -i --tty --rm --command -- curl -X POST -F \
|
||||||
'locust_count=6' -F 'hatch_rate=2' http://locust-master:8089/swarm
|
'locust_count=6' -F 'hatch_rate=2' http://locust-master:8089/swarm
|
||||||
```
|
```
|
||||||
|
|
||||||
**To stop the load generation:**
|
**To stop the load generation:**
|
||||||
|
|
||||||
```sh
|
```bash
|
||||||
kubectl -n sample-application run strzal --image=djbingham/curl \
|
kubectl -n sample-application run strzal --image=djbingham/curl \
|
||||||
--restart='OnFailure' -i --tty --rm --command -- curl \
|
--restart='OnFailure' -i --tty --rm --command -- curl \
|
||||||
http://locust-master:8089/stop
|
http://locust-master:8089/stop
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**To delete HotROD sample app:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sL https://github.com/SigNoz/signoz/raw/main/sample-apps/hotrod/hotrod-delete.sh \
|
||||||
|
| HOTROD_NAMESPACE=sample-application bash
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## General Instructions
|
## General Instructions
|
||||||
|
|
||||||
|
**Before making any significant changes, please open an issue**. Each issue
|
||||||
|
should describe the following:
|
||||||
|
|
||||||
|
* Requirement - what kind of use case are you trying to solve?
|
||||||
|
* Proposal - what do you suggest to solve the problem or improve the existing
|
||||||
|
situation?
|
||||||
|
* Any open questions to address
|
||||||
|
|
||||||
|
Discussing your proposed changes ahead of time will make the contribution
|
||||||
|
process smooth for everyone. Once the approach is agreed upon, make your changes
|
||||||
|
and open a pull request(s). Unless your change is small, Please consider submitting different PRs:
|
||||||
|
|
||||||
|
* First PR should include the overall structure of the new component:
|
||||||
|
* Readme, configuration, interfaces or base classes etc...
|
||||||
|
* This PR is usually trivial to review, so the size limit does not apply to
|
||||||
|
it.
|
||||||
|
* Second PR should include the concrete implementation of the component. If the
|
||||||
|
size of this PR is larger than the recommended size consider splitting it in
|
||||||
|
multiple PRs.
|
||||||
|
* If there are multiple sub-component then ideally each one should be implemented as
|
||||||
|
a separate pull request.
|
||||||
|
* Last PR should include changes to any user facing documentation. And should include
|
||||||
|
end to end tests if applicable. The component must be enabled
|
||||||
|
only after sufficient testing, and there is enough confidence in the
|
||||||
|
stability and quality of the component.
|
||||||
|
|
||||||
|
|
||||||
You can always reach out to `ankit@signoz.io` to understand more about the repo and product. We are very responsive over email and [slack](https://signoz.io/slack).
|
You can always reach out to `ankit@signoz.io` to understand more about the repo and product. We are very responsive over email and [slack](https://signoz.io/slack).
|
||||||
|
|
||||||
- If you find any bugs, please create an issue
|
- If you find any bugs, please create an issue
|
||||||
|
@ -39,8 +39,9 @@ services:
|
|||||||
query-service:
|
query-service:
|
||||||
image: signoz/query-service:0.8.0
|
image: signoz/query-service:0.8.0
|
||||||
command: ["-config=/root/config/prometheus.yml"]
|
command: ["-config=/root/config/prometheus.yml"]
|
||||||
ports:
|
# ports:
|
||||||
- "8080:8080"
|
# - "6060:6060" # pprof port
|
||||||
|
# - "8080:8080" # query-service port
|
||||||
volumes:
|
volumes:
|
||||||
- ./prometheus.yml:/root/config/prometheus.yml
|
- ./prometheus.yml:/root/config/prometheus.yml
|
||||||
- ../dashboards:/root/config/dashboards
|
- ../dashboards:/root/config/dashboards
|
||||||
@ -85,7 +86,7 @@ services:
|
|||||||
- "4317:4317" # OTLP gRPC receiver
|
- "4317:4317" # OTLP gRPC receiver
|
||||||
- "4318:4318" # OTLP HTTP receiver
|
- "4318:4318" # OTLP HTTP receiver
|
||||||
# - "8889:8889" # Prometheus metrics exposed by the agent
|
# - "8889:8889" # Prometheus metrics exposed by the agent
|
||||||
# - "13133" # health_check
|
# - "13133:13133" # health_check
|
||||||
# - "14268:14268" # Jaeger receiver
|
# - "14268:14268" # Jaeger receiver
|
||||||
# - "55678:55678" # OpenCensus receiver
|
# - "55678:55678" # OpenCensus receiver
|
||||||
# - "55679:55679" # zpages extension
|
# - "55679:55679" # zpages extension
|
||||||
|
@ -12,13 +12,18 @@ server {
|
|||||||
gzip_http_version 1.1;
|
gzip_http_version 1.1;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0";
|
if ( $uri = '/index.html' ) {
|
||||||
add_header Last-Modified $date_gmt;
|
add_header Cache-Control no-store always;
|
||||||
|
}
|
||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
index index.html index.htm;
|
index index.html index.htm;
|
||||||
try_files $uri $uri/ /index.html;
|
try_files $uri $uri/ /index.html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /api/alertmanager {
|
||||||
|
proxy_pass http://alertmanager:9093/api/v2;
|
||||||
|
}
|
||||||
|
|
||||||
location /api {
|
location /api {
|
||||||
proxy_pass http://query-service:8080/api;
|
proxy_pass http://query-service:8080/api;
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,9 @@ services:
|
|||||||
image: signoz/query-service:0.8.0
|
image: signoz/query-service:0.8.0
|
||||||
container_name: query-service
|
container_name: query-service
|
||||||
command: ["-config=/root/config/prometheus.yml"]
|
command: ["-config=/root/config/prometheus.yml"]
|
||||||
|
# ports:
|
||||||
|
# - "6060:6060" # pprof port
|
||||||
|
# - "8080:8080" # query-service port
|
||||||
volumes:
|
volumes:
|
||||||
- ./prometheus.yml:/root/config/prometheus.yml
|
- ./prometheus.yml:/root/config/prometheus.yml
|
||||||
- ../dashboards:/root/config/dashboards
|
- ../dashboards:/root/config/dashboards
|
||||||
@ -82,7 +85,7 @@ services:
|
|||||||
- "4317:4317" # OTLP gRPC receiver
|
- "4317:4317" # OTLP gRPC receiver
|
||||||
- "4318:4318" # OTLP HTTP receiver
|
- "4318:4318" # OTLP HTTP receiver
|
||||||
# - "8889:8889" # Prometheus metrics exposed by the agent
|
# - "8889:8889" # Prometheus metrics exposed by the agent
|
||||||
# - "13133" # health_check
|
# - "13133:13133" # health_check
|
||||||
# - "14268:14268" # Jaeger receiver
|
# - "14268:14268" # Jaeger receiver
|
||||||
# - "55678:55678" # OpenCensus receiver
|
# - "55678:55678" # OpenCensus receiver
|
||||||
# - "55679:55679" # zpages extension
|
# - "55679:55679" # zpages extension
|
||||||
|
@ -39,6 +39,9 @@ services:
|
|||||||
image: signoz/query-service:0.8.0
|
image: signoz/query-service:0.8.0
|
||||||
container_name: query-service
|
container_name: query-service
|
||||||
command: ["-config=/root/config/prometheus.yml"]
|
command: ["-config=/root/config/prometheus.yml"]
|
||||||
|
# ports:
|
||||||
|
# - "6060:6060" # pprof port
|
||||||
|
# - "8080:8080" # query-service port
|
||||||
volumes:
|
volumes:
|
||||||
- ./prometheus.yml:/root/config/prometheus.yml
|
- ./prometheus.yml:/root/config/prometheus.yml
|
||||||
- ../dashboards:/root/config/dashboards
|
- ../dashboards:/root/config/dashboards
|
||||||
@ -80,7 +83,7 @@ services:
|
|||||||
- "4317:4317" # OTLP gRPC receiver
|
- "4317:4317" # OTLP gRPC receiver
|
||||||
- "4318:4318" # OTLP HTTP receiver
|
- "4318:4318" # OTLP HTTP receiver
|
||||||
# - "8889:8889" # Prometheus metrics exposed by the agent
|
# - "8889:8889" # Prometheus metrics exposed by the agent
|
||||||
# - "13133" # health_check
|
# - "13133:13133" # health_check
|
||||||
# - "14268:14268" # Jaeger receiver
|
# - "14268:14268" # Jaeger receiver
|
||||||
# - "55678:55678" # OpenCensus receiver
|
# - "55678:55678" # OpenCensus receiver
|
||||||
# - "55679:55679" # zpages extension
|
# - "55679:55679" # zpages extension
|
||||||
|
@ -12,14 +12,15 @@ server {
|
|||||||
gzip_http_version 1.1;
|
gzip_http_version 1.1;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0";
|
if ( $uri = '/index.html' ) {
|
||||||
add_header Last-Modified $date_gmt;
|
add_header Cache-Control no-store always;
|
||||||
|
}
|
||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
index index.html index.htm;
|
index index.html index.htm;
|
||||||
try_files $uri $uri/ /index.html;
|
try_files $uri $uri/ /index.html;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /api/alertmanager{
|
location /api/alertmanager {
|
||||||
proxy_pass http://alertmanager:9093/api/v2;
|
proxy_pass http://alertmanager:9093/api/v2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
frontend/.husky/commit-msg
Executable file
4
frontend/.husky/commit-msg
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
. "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
cd frontend && npm run commitlint
|
@ -1 +1 @@
|
|||||||
12.13.0
|
16.15.0
|
@ -12,6 +12,9 @@ WORKDIR /frontend
|
|||||||
# copy the package.json to install dependencies
|
# copy the package.json to install dependencies
|
||||||
COPY package.json ./
|
COPY package.json ./
|
||||||
|
|
||||||
|
# configure node_env as production
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
# Install the dependencies and make the folder
|
# Install the dependencies and make the folder
|
||||||
RUN yarn install
|
RUN yarn install
|
||||||
|
|
||||||
|
1
frontend/commitlint.config.js
Normal file
1
frontend/commitlint.config.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = { extends: ['@commitlint/config-conventional'] };
|
@ -13,12 +13,13 @@
|
|||||||
"jest:coverage": "jest --coverage",
|
"jest:coverage": "jest --coverage",
|
||||||
"jest:watch": "jest --watch",
|
"jest:watch": "jest --watch",
|
||||||
"postinstall": "yarn husky:configure",
|
"postinstall": "yarn husky:configure",
|
||||||
"husky:configure": "cd .. && husky install frontend/.husky",
|
|
||||||
"playwright": "playwright test --config=./playwright.config.ts",
|
"playwright": "playwright test --config=./playwright.config.ts",
|
||||||
"playwright:local:debug": "PWDEBUG=console yarn playwright --headed --browser=chromium"
|
"playwright:local:debug": "PWDEBUG=console yarn playwright --headed --browser=chromium",
|
||||||
|
"husky:configure": "cd .. && husky install frontend/.husky && cd frontend && chmod ug+x .husky/*",
|
||||||
|
"commitlint": "commitlint --edit $1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.13.0"
|
"node": ">=16.15.0"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
@ -108,6 +109,8 @@
|
|||||||
"@babel/preset-env": "^7.12.17",
|
"@babel/preset-env": "^7.12.17",
|
||||||
"@babel/preset-react": "^7.12.13",
|
"@babel/preset-react": "^7.12.13",
|
||||||
"@babel/preset-typescript": "^7.12.17",
|
"@babel/preset-typescript": "^7.12.17",
|
||||||
|
"@commitlint/cli": "^16.2.4",
|
||||||
|
"@commitlint/config-conventional": "^16.2.4",
|
||||||
"@jest/globals": "^27.5.1",
|
"@jest/globals": "^27.5.1",
|
||||||
"@playwright/test": "^1.22.0",
|
"@playwright/test": "^1.22.0",
|
||||||
"@testing-library/react-hooks": "^7.0.2",
|
"@testing-library/react-hooks": "^7.0.2",
|
||||||
@ -135,7 +138,7 @@
|
|||||||
"@typescript-eslint/parser": "^4.28.2",
|
"@typescript-eslint/parser": "^4.28.2",
|
||||||
"autoprefixer": "^9.0.0",
|
"autoprefixer": "^9.0.0",
|
||||||
"babel-plugin-styled-components": "^1.12.0",
|
"babel-plugin-styled-components": "^1.12.0",
|
||||||
"compression-webpack-plugin": "^9.0.0",
|
"compression-webpack-plugin": "9.0.0",
|
||||||
"copy-webpack-plugin": "^8.1.0",
|
"copy-webpack-plugin": "^8.1.0",
|
||||||
"critters-webpack-plugin": "^3.0.1",
|
"critters-webpack-plugin": "^3.0.1",
|
||||||
"eslint": "^7.30.0",
|
"eslint": "^7.30.0",
|
||||||
|
@ -2,5 +2,8 @@
|
|||||||
"general": "General",
|
"general": "General",
|
||||||
"alert_channels": "Alert Channels",
|
"alert_channels": "Alert Channels",
|
||||||
"organization_settings": "Organization Settings",
|
"organization_settings": "Organization Settings",
|
||||||
"my_settings": "My Settings"
|
"my_settings": "My Settings",
|
||||||
|
"overview_metrics": "Overview Metrics",
|
||||||
|
"dbcall_metrics": "Database Calls",
|
||||||
|
"external_metrics": "External Calls"
|
||||||
}
|
}
|
||||||
|
@ -2,5 +2,8 @@
|
|||||||
"general": "General",
|
"general": "General",
|
||||||
"alert_channels": "Alert Channels",
|
"alert_channels": "Alert Channels",
|
||||||
"organization_settings": "Organization Settings",
|
"organization_settings": "Organization Settings",
|
||||||
"my_settings": "My Settings"
|
"my_settings": "My Settings",
|
||||||
|
"overview_metrics": "Overview Metrics",
|
||||||
|
"dbcall_metrics": "Database Calls",
|
||||||
|
"external_metrics": "External Calls"
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import { notification } from 'antd';
|
import { notification } from 'antd';
|
||||||
import getLocalStorageApi from 'api/browser/localstorage/get';
|
import getLocalStorageApi from 'api/browser/localstorage/get';
|
||||||
import loginApi from 'api/user/login';
|
import loginApi from 'api/user/login';
|
||||||
|
import { Logout } from 'api/utils';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
@ -103,7 +104,7 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
|||||||
history.push(ROUTES.UN_AUTHORIZED);
|
history.push(ROUTES.UN_AUTHORIZED);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
history.push(ROUTES.SOMETHING_WENT_WRONG);
|
Logout();
|
||||||
|
|
||||||
notification.error({
|
notification.error({
|
||||||
message: response.error || t('something_went_wrong'),
|
message: response.error || t('something_went_wrong'),
|
||||||
|
@ -5,6 +5,7 @@ import history from 'lib/history';
|
|||||||
import store from 'store';
|
import store from 'store';
|
||||||
import {
|
import {
|
||||||
LOGGED_IN,
|
LOGGED_IN,
|
||||||
|
UPDATE_ORG,
|
||||||
UPDATE_USER,
|
UPDATE_USER,
|
||||||
UPDATE_USER_ACCESS_REFRESH_ACCESS_TOKEN,
|
UPDATE_USER_ACCESS_REFRESH_ACCESS_TOKEN,
|
||||||
UPDATE_USER_ORG_ROLE,
|
UPDATE_USER_ORG_ROLE,
|
||||||
@ -51,5 +52,12 @@ export const Logout = (): void => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
store.dispatch({
|
||||||
|
type: UPDATE_ORG,
|
||||||
|
payload: {
|
||||||
|
org: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
history.push(ROUTES.LOGIN);
|
history.push(ROUTES.LOGIN);
|
||||||
};
|
};
|
||||||
|
@ -27,12 +27,19 @@ function RouteTab({
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
destroyInactiveTabPane
|
destroyInactiveTabPane
|
||||||
activeKey={activeKey}
|
activeKey={activeKey}
|
||||||
|
animated
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
{routes.map(
|
{routes.map(
|
||||||
({ Component, name }): JSX.Element => (
|
({ Component, name, route }): JSX.Element => (
|
||||||
<TabPane tab={name} key={name}>
|
<TabPane
|
||||||
|
tabKey={route}
|
||||||
|
animated
|
||||||
|
destroyInactiveTabPane
|
||||||
|
tab={name}
|
||||||
|
key={name}
|
||||||
|
>
|
||||||
<Component />
|
<Component />
|
||||||
</TabPane>
|
</TabPane>
|
||||||
),
|
),
|
||||||
|
@ -3,4 +3,5 @@ export const ENVIRONMENT = {
|
|||||||
process?.env?.FRONTEND_API_ENDPOINT ||
|
process?.env?.FRONTEND_API_ENDPOINT ||
|
||||||
process?.env?.GITPOD_WORKSPACE_URL?.replace('://', '://8080-') ||
|
process?.env?.GITPOD_WORKSPACE_URL?.replace('://', '://8080-') ||
|
||||||
'',
|
'',
|
||||||
|
NODE_ENV: process?.env?.NODE_ENV,
|
||||||
};
|
};
|
||||||
|
@ -18,7 +18,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',
|
||||||
ALL_ERROR: '/errors',
|
ALL_ERROR: '/exceptions',
|
||||||
ERROR_DETAIL: '/error-detail',
|
ERROR_DETAIL: '/error-detail',
|
||||||
VERSION: '/status',
|
VERSION: '/status',
|
||||||
MY_SETTINGS: '/my-settings',
|
MY_SETTINGS: '/my-settings',
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { notification, Table, Typography } from 'antd';
|
import { notification, Table, Tooltip, Typography } from 'antd';
|
||||||
import { ColumnsType } from 'antd/lib/table';
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
import getAll from 'api/errors/getAll';
|
import getAll from 'api/errors/getAll';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
@ -47,11 +47,13 @@ function AllErrors(): JSX.Element {
|
|||||||
dataIndex: 'exceptionType',
|
dataIndex: 'exceptionType',
|
||||||
key: 'exceptionType',
|
key: 'exceptionType',
|
||||||
render: (value, record): JSX.Element => (
|
render: (value, record): JSX.Element => (
|
||||||
<Link
|
<Tooltip overlay={(): JSX.Element => value}>
|
||||||
to={`${ROUTES.ERROR_DETAIL}?serviceName=${record.serviceName}&errorType=${record.exceptionType}`}
|
<Link
|
||||||
>
|
to={`${ROUTES.ERROR_DETAIL}?serviceName=${record.serviceName}&errorType=${record.exceptionType}`}
|
||||||
{value}
|
>
|
||||||
</Link>
|
{value}
|
||||||
|
</Link>
|
||||||
|
</Tooltip>
|
||||||
),
|
),
|
||||||
sorter: (a, b): number =>
|
sorter: (a, b): number =>
|
||||||
a.exceptionType.charCodeAt(0) - b.exceptionType.charCodeAt(0),
|
a.exceptionType.charCodeAt(0) - b.exceptionType.charCodeAt(0),
|
||||||
@ -61,13 +63,15 @@ function AllErrors(): JSX.Element {
|
|||||||
dataIndex: 'exceptionMessage',
|
dataIndex: 'exceptionMessage',
|
||||||
key: 'exceptionMessage',
|
key: 'exceptionMessage',
|
||||||
render: (value): JSX.Element => (
|
render: (value): JSX.Element => (
|
||||||
<Typography.Paragraph
|
<Tooltip overlay={(): JSX.Element => value}>
|
||||||
ellipsis={{
|
<Typography.Paragraph
|
||||||
rows: 2,
|
ellipsis={{
|
||||||
}}
|
rows: 2,
|
||||||
>
|
}}
|
||||||
{value}
|
>
|
||||||
</Typography.Paragraph>
|
{value}
|
||||||
|
</Typography.Paragraph>
|
||||||
|
</Tooltip>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { Button, Typography } from 'antd';
|
import { Button, Typography } from 'antd';
|
||||||
import getQueryResult from 'api/widgets/getQuery';
|
import getQueryResult from 'api/widgets/getQuery';
|
||||||
import { AxiosError } from 'axios';
|
|
||||||
import { ChartData } from 'chart.js';
|
|
||||||
import { GraphOnClickHandler } from 'components/Graph';
|
import { GraphOnClickHandler } from 'components/Graph';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import TimePreference from 'components/TimePreferenceDropDown';
|
import TimePreference from 'components/TimePreferenceDropDown';
|
||||||
@ -17,7 +15,8 @@ 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 getStep from 'lib/getStep';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
|
import { useQueries } from 'react-query';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
@ -37,13 +36,6 @@ function FullView({
|
|||||||
GlobalReducer
|
GlobalReducer
|
||||||
>((state) => state.globalTime);
|
>((state) => state.globalTime);
|
||||||
|
|
||||||
const [state, setState] = useState<FullViewState>({
|
|
||||||
error: false,
|
|
||||||
errorMessage: '',
|
|
||||||
loading: true,
|
|
||||||
payload: undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
const getSelectedTime = useCallback(
|
const getSelectedTime = useCallback(
|
||||||
() =>
|
() =>
|
||||||
timeItems.find((e) => e.enum === (widget?.timePreferance || 'GLOBAL_TIME')),
|
timeItems.find((e) => e.enum === (widget?.timePreferance || 'GLOBAL_TIME')),
|
||||||
@ -55,107 +47,82 @@ function FullView({
|
|||||||
enum: widget?.timePreferance || 'GLOBAL_TIME',
|
enum: widget?.timePreferance || 'GLOBAL_TIME',
|
||||||
});
|
});
|
||||||
|
|
||||||
const onFetchDataHandler = useCallback(async () => {
|
const maxMinTime = GetMaxMinTime({
|
||||||
try {
|
graphType: widget.panelTypes,
|
||||||
const maxMinTime = GetMaxMinTime({
|
maxTime,
|
||||||
graphType: widget.panelTypes,
|
minTime,
|
||||||
maxTime,
|
});
|
||||||
minTime,
|
|
||||||
});
|
|
||||||
|
|
||||||
const getMinMax = (
|
const getMinMax = (
|
||||||
time: timePreferenceType,
|
time: timePreferenceType,
|
||||||
): { min: string | number; max: string | number } => {
|
): { min: string | number; max: string | number } => {
|
||||||
if (time === 'GLOBAL_TIME') {
|
if (time === 'GLOBAL_TIME') {
|
||||||
const minMax = GetMinMax(globalSelectedTime);
|
const minMax = GetMinMax(globalSelectedTime);
|
||||||
return {
|
return {
|
||||||
min: convertToNanoSecondsToSecond(minMax.minTime / 1000),
|
min: convertToNanoSecondsToSecond(minMax.minTime / 1000),
|
||||||
max: convertToNanoSecondsToSecond(minMax.maxTime / 1000),
|
max: convertToNanoSecondsToSecond(minMax.maxTime / 1000),
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const minMax = getStartAndEndTime({
|
|
||||||
type: selectedTime.enum,
|
|
||||||
maxTime: maxMinTime.maxTime,
|
|
||||||
minTime: maxMinTime.minTime,
|
|
||||||
});
|
|
||||||
return { min: parseInt(minMax.start, 10), max: parseInt(minMax.end, 10) };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const queryMinMax = getMinMax(selectedTime.enum);
|
|
||||||
const response = await Promise.all(
|
|
||||||
widget.query
|
|
||||||
.filter((e) => e.query.length !== 0)
|
|
||||||
.map(async (query) => {
|
|
||||||
const result = await getQueryResult({
|
|
||||||
end: queryMinMax.max.toString(),
|
|
||||||
query: query.query,
|
|
||||||
start: queryMinMax.min.toString(),
|
|
||||||
step: `${getStep({
|
|
||||||
start: queryMinMax.min,
|
|
||||||
end: queryMinMax.max,
|
|
||||||
inputFormat: 's',
|
|
||||||
})}`,
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
query: query.query,
|
|
||||||
queryData: result,
|
|
||||||
legend: query.legend,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const isError = response.find((e) => e.queryData.statusCode !== 200);
|
|
||||||
|
|
||||||
if (isError !== undefined) {
|
|
||||||
setState((state) => ({
|
|
||||||
...state,
|
|
||||||
error: true,
|
|
||||||
errorMessage: isError.queryData.error || 'Something went wrong',
|
|
||||||
loading: false,
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
const chartDataSet = getChartData({
|
|
||||||
queryData: response.map((e) => ({
|
|
||||||
query: e.query,
|
|
||||||
legend: e.legend,
|
|
||||||
queryData: e.queryData.payload?.result || [],
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
|
|
||||||
setState((state) => ({
|
|
||||||
...state,
|
|
||||||
loading: false,
|
|
||||||
payload: chartDataSet,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
setState((state) => ({
|
|
||||||
...state,
|
|
||||||
error: true,
|
|
||||||
errorMessage: (error as AxiosError).toString(),
|
|
||||||
loading: false,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}, [widget, maxTime, minTime, selectedTime.enum, globalSelectedTime]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
const minMax = getStartAndEndTime({
|
||||||
onFetchDataHandler();
|
type: selectedTime.enum,
|
||||||
}, [onFetchDataHandler]);
|
maxTime: maxMinTime.maxTime,
|
||||||
|
minTime: maxMinTime.minTime,
|
||||||
|
});
|
||||||
|
return { min: parseInt(minMax.start, 10), max: parseInt(minMax.end, 10) };
|
||||||
|
};
|
||||||
|
|
||||||
if (state.error && !state.loading) {
|
const queryMinMax = getMinMax(selectedTime.enum);
|
||||||
return (
|
|
||||||
<NotFoundContainer>
|
const queryLength = widget.query.filter((e) => e.query.length !== 0);
|
||||||
<Typography>{state.errorMessage}</Typography>
|
|
||||||
</NotFoundContainer>
|
const response = useQueries(
|
||||||
);
|
queryLength.map((query) => {
|
||||||
|
return {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||||
|
queryFn: () => {
|
||||||
|
return getQueryResult({
|
||||||
|
end: queryMinMax.max.toString(),
|
||||||
|
query: query.query,
|
||||||
|
start: queryMinMax.min.toString(),
|
||||||
|
step: `${getStep({
|
||||||
|
start: queryMinMax.min,
|
||||||
|
end: queryMinMax.max,
|
||||||
|
inputFormat: 's',
|
||||||
|
})}`,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
queryHash: `${query.query}-${query.legend}-${selectedTime.enum}`,
|
||||||
|
retryOnMount: false,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const isError =
|
||||||
|
response.find((e) => e?.data?.statusCode !== 200) !== undefined ||
|
||||||
|
response.some((e) => e.isError === true);
|
||||||
|
|
||||||
|
const isLoading = response.some((e) => e.isLoading === true);
|
||||||
|
|
||||||
|
const errorMessage = response.find((e) => e.data?.error !== null)?.data?.error;
|
||||||
|
|
||||||
|
const data = response.map((responseOfQuery) =>
|
||||||
|
responseOfQuery?.data?.payload?.result.map((e, index) => ({
|
||||||
|
query: queryLength[index]?.query,
|
||||||
|
queryData: e,
|
||||||
|
legend: queryLength[index]?.legend,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <Spinner height="100%" size="large" tip="Loading..." />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.loading || state.payload === undefined) {
|
if (isError || data === undefined || data[0] === undefined) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<NotFoundContainer>
|
||||||
<Spinner height="80vh" size="large" tip="Loading..." />
|
<Typography>{errorMessage}</Typography>
|
||||||
</div>
|
</NotFoundContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,17 +136,27 @@ function FullView({
|
|||||||
setSelectedTime,
|
setSelectedTime,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button onClick={onFetchDataHandler} type="primary">
|
<Button
|
||||||
|
onClick={(): void => {
|
||||||
|
response.forEach((e) => e.refetch());
|
||||||
|
}}
|
||||||
|
type="primary"
|
||||||
|
>
|
||||||
Refresh
|
Refresh
|
||||||
</Button>
|
</Button>
|
||||||
</TimeContainer>
|
</TimeContainer>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* <GraphContainer> */}
|
|
||||||
<GridGraphComponent
|
<GridGraphComponent
|
||||||
{...{
|
{...{
|
||||||
GRAPH_TYPES: widget.panelTypes,
|
GRAPH_TYPES: widget.panelTypes,
|
||||||
data: state.payload,
|
data: getChartData({
|
||||||
|
queryData: data.map((e) => ({
|
||||||
|
query: e?.map((e) => e.query).join(' ') || '',
|
||||||
|
queryData: e?.map((e) => e.queryData) || [],
|
||||||
|
legend: e?.map((e) => e.legend).join('') || '',
|
||||||
|
})),
|
||||||
|
}),
|
||||||
isStacked: widget.isStacked,
|
isStacked: widget.isStacked,
|
||||||
opacity: widget.opacity,
|
opacity: widget.opacity,
|
||||||
title: widget.title,
|
title: widget.title,
|
||||||
@ -188,18 +165,10 @@ function FullView({
|
|||||||
yAxisUnit,
|
yAxisUnit,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/* </GraphContainer> */}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FullViewState {
|
|
||||||
loading: boolean;
|
|
||||||
error: boolean;
|
|
||||||
errorMessage: string;
|
|
||||||
payload: ChartData | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FullViewProps {
|
interface FullViewProps {
|
||||||
widget: Widgets;
|
widget: Widgets;
|
||||||
fullViewOptions?: boolean;
|
fullViewOptions?: boolean;
|
||||||
|
@ -65,7 +65,7 @@ function GridCardGraph({
|
|||||||
.map(async (query) => {
|
.map(async (query) => {
|
||||||
const result = await getQueryResult({
|
const result = await getQueryResult({
|
||||||
end,
|
end,
|
||||||
query: query.query,
|
query: encodeURIComponent(query.query),
|
||||||
start,
|
start,
|
||||||
step: '60',
|
step: '60',
|
||||||
});
|
});
|
||||||
|
@ -28,6 +28,7 @@ function GridGraph(): JSX.Element {
|
|||||||
const { dashboards, loading } = useSelector<AppState, DashboardReducer>(
|
const { dashboards, loading } = useSelector<AppState, DashboardReducer>(
|
||||||
(state) => state.dashboards,
|
(state) => state.dashboards,
|
||||||
);
|
);
|
||||||
|
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
const [saveLayoutState, setSaveLayoutState] = useState<State>({
|
const [saveLayoutState, setSaveLayoutState] = useState<State>({
|
||||||
loading: false,
|
loading: false,
|
||||||
error: false,
|
error: false,
|
||||||
@ -251,8 +252,13 @@ function GridGraph(): JSX.Element {
|
|||||||
const isQueryType = type === 'VALUE';
|
const isQueryType = type === 'VALUE';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardContainer key={rest.i} data-grid={rest}>
|
<CardContainer
|
||||||
<Card isQueryType={isQueryType}>
|
isQueryType={isQueryType}
|
||||||
|
isDarkMode={isDarkMode}
|
||||||
|
key={rest.i}
|
||||||
|
data-grid={rest}
|
||||||
|
>
|
||||||
|
<Card isDarkMode={isDarkMode} isQueryType={isQueryType}>
|
||||||
<Component />
|
<Component />
|
||||||
</Card>
|
</Card>
|
||||||
</CardContainer>
|
</CardContainer>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Button as ButtonComponent, Card as CardComponent } from 'antd';
|
import { Button as ButtonComponent, Card as CardComponent } from 'antd';
|
||||||
|
import { StyledCSS } from 'container/GantChart/Trace/styles';
|
||||||
import RGL, { WidthProvider } from 'react-grid-layout';
|
import RGL, { WidthProvider } from 'react-grid-layout';
|
||||||
import styled from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
const ReactGridLayoutComponent = WidthProvider(RGL);
|
const ReactGridLayoutComponent = WidthProvider(RGL);
|
||||||
|
|
||||||
@ -18,20 +19,34 @@ export const Card = styled(CardComponent)<Props>`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const CardContainer = styled.div`
|
interface Props {
|
||||||
.react-resizable-handle {
|
isDarkMode: boolean;
|
||||||
position: absolute;
|
}
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
export const CardContainer = styled.div<Props>`
|
||||||
bottom: 0;
|
:hover {
|
||||||
right: 0;
|
.react-resizable-handle {
|
||||||
background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pg08IS0tIEdlbmVyYXRvcjogQWRvYmUgRmlyZXdvcmtzIENTNiwgRXhwb3J0IFNWRyBFeHRlbnNpb24gYnkgQWFyb24gQmVhbGwgKGh0dHA6Ly9maXJld29ya3MuYWJlYWxsLmNvbSkgLiBWZXJzaW9uOiAwLjYuMSAgLS0+DTwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DTxzdmcgaWQ9IlVudGl0bGVkLVBhZ2UlMjAxIiB2aWV3Qm94PSIwIDAgNiA2IiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojZmZmZmZmMDAiIHZlcnNpb249IjEuMSINCXhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbDpzcGFjZT0icHJlc2VydmUiDQl4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjZweCIgaGVpZ2h0PSI2cHgiDT4NCTxnIG9wYWNpdHk9IjAuMzAyIj4NCQk8cGF0aCBkPSJNIDYgNiBMIDAgNiBMIDAgNC4yIEwgNCA0LjIgTCA0LjIgNC4yIEwgNC4yIDAgTCA2IDAgTCA2IDYgTCA2IDYgWiIgZmlsbD0iIzAwMDAwMCIvPg0JPC9nPg08L3N2Zz4=');
|
position: absolute;
|
||||||
background-position: bottom right;
|
width: 20px;
|
||||||
padding: 0 3px 3px 0;
|
height: 20px;
|
||||||
background-repeat: no-repeat;
|
bottom: 0;
|
||||||
background-origin: content-box;
|
right: 0;
|
||||||
box-sizing: border-box;
|
background-position: bottom right;
|
||||||
cursor: se-resize;
|
padding: 0 3px 3px 0;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-origin: content-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: se-resize;
|
||||||
|
|
||||||
|
${({ isDarkMode }): StyledCSS => {
|
||||||
|
const uri = `data:image/svg+xml,%3Csvg viewBox='0 0 6 6' style='background-color:%23ffffff00' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' xml:space='preserve' x='0px' y='0px' width='6px' height='6px'%0A%3E%3Cg opacity='0.302'%3E%3Cpath d='M 6 6 L 0 6 L 0 4.2 L 4 4.2 L 4.2 4.2 L 4.2 0 L 6 0 L 6 6 L 6 6 Z' fill='${
|
||||||
|
isDarkMode ? 'white' : 'grey'
|
||||||
|
}'/%3E%3C/g%3E%3C/svg%3E`;
|
||||||
|
|
||||||
|
return css`
|
||||||
|
background-image: ${(): string => `url("${uri}")`};
|
||||||
|
`;
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
function IsRouteAccessible(): JSX.Element {
|
|
||||||
return <div>asd</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default IsRouteAccessible;
|
|
@ -1,6 +1,7 @@
|
|||||||
|
import { notification } from 'antd';
|
||||||
import getAll from 'api/alerts/getAll';
|
import getAll from 'api/alerts/getAll';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
|
|
||||||
@ -8,15 +9,37 @@ import ListAlert from './ListAlert';
|
|||||||
|
|
||||||
function ListAlertRules(): JSX.Element {
|
function ListAlertRules(): JSX.Element {
|
||||||
const { t } = useTranslation('common');
|
const { t } = useTranslation('common');
|
||||||
const { data, isError, isLoading, refetch } = useQuery('allAlerts', {
|
const { data, isError, isLoading, refetch, status } = useQuery('allAlerts', {
|
||||||
queryFn: getAll,
|
queryFn: getAll,
|
||||||
cacheTime: 0,
|
cacheTime: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (status === 'error' || (status === 'success' && data.statusCode >= 400)) {
|
||||||
|
notification.error({
|
||||||
|
message: data?.error || t('something_went_wrong'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [data?.error, data?.statusCode, status, t]);
|
||||||
|
|
||||||
|
// api failed to load the data
|
||||||
if (isError) {
|
if (isError) {
|
||||||
return <div>{data?.error || t('something_went_wrong')}</div>;
|
return <div>{data?.error || t('something_went_wrong')}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// api is successful but error is present
|
||||||
|
if (status === 'success' && data.statusCode >= 400) {
|
||||||
|
return (
|
||||||
|
<ListAlert
|
||||||
|
{...{
|
||||||
|
allAlertRules: [],
|
||||||
|
refetch,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// in case of loading
|
||||||
if (isLoading || !data?.payload) {
|
if (isLoading || !data?.payload) {
|
||||||
return <Spinner height="75vh" tip="Loading Rules..." />;
|
return <Spinner height="75vh" tip="Loading Rules..." />;
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,9 @@ function ImportJSON({
|
|||||||
...queryData,
|
...queryData,
|
||||||
queryData: [],
|
queryData: [],
|
||||||
})),
|
})),
|
||||||
|
error: false,
|
||||||
|
errorMessage: '',
|
||||||
|
loading: false,
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
@ -16,7 +16,7 @@ import MetricReducer from 'types/reducer/metrics';
|
|||||||
|
|
||||||
import { Card, Col, GraphContainer, GraphTitle, Row } from '../styles';
|
import { Card, Col, GraphContainer, GraphTitle, Row } from '../styles';
|
||||||
import TopEndpointsTable from '../TopEndpointsTable';
|
import TopEndpointsTable from '../TopEndpointsTable';
|
||||||
import { Button, TableContainerCard } from './styles';
|
import { Button } from './styles';
|
||||||
|
|
||||||
function Application({ getWidget }: DashboardProps): JSX.Element {
|
function Application({ getWidget }: DashboardProps): JSX.Element {
|
||||||
const { servicename } = useParams<{ servicename?: string }>();
|
const { servicename } = useParams<{ servicename?: string }>();
|
||||||
@ -48,7 +48,7 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClickhandler = async (
|
const onClickHandler = async (
|
||||||
event: ChartEvent,
|
event: ChartEvent,
|
||||||
elements: ActiveElement[],
|
elements: ActiveElement[],
|
||||||
chart: Chart,
|
chart: Chart,
|
||||||
@ -119,7 +119,7 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
<GraphContainer>
|
<GraphContainer>
|
||||||
<Graph
|
<Graph
|
||||||
onClickHandler={(ChartEvent, activeElements, chart, data): void => {
|
onClickHandler={(ChartEvent, activeElements, chart, data): void => {
|
||||||
onClickhandler(ChartEvent, activeElements, chart, data, 'Application');
|
onClickHandler(ChartEvent, activeElements, chart, data, 'Application');
|
||||||
}}
|
}}
|
||||||
name="application_latency"
|
name="application_latency"
|
||||||
type="line"
|
type="line"
|
||||||
@ -189,7 +189,7 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
name="request_per_sec"
|
name="request_per_sec"
|
||||||
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');
|
||||||
}}
|
}}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
@ -223,7 +223,7 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
name="error_percentage_%"
|
name="error_percentage_%"
|
||||||
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');
|
||||||
}}
|
}}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
@ -238,9 +238,9 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<TableContainerCard>
|
<Card>
|
||||||
<TopEndpointsTable data={topEndPoints} />
|
<TopEndpointsTable data={topEndPoints} />
|
||||||
</TableContainerCard>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</>
|
</>
|
@ -1,8 +1,6 @@
|
|||||||
import { Button as ButtonComponent } from 'antd';
|
import { Button as ButtonComponent } from 'antd';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { Card } from '../styles';
|
|
||||||
|
|
||||||
export const Button = styled(ButtonComponent)`
|
export const Button = styled(ButtonComponent)`
|
||||||
&&& {
|
&&& {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -10,6 +8,3 @@ export const Button = styled(ButtonComponent)`
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const TableContainerCard = styled(Card)`
|
|
||||||
overflow-x: auto;
|
|
||||||
`;
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Button, Table, Tooltip } from 'antd';
|
import { Table, Tooltip, Typography } from 'antd';
|
||||||
import { ColumnsType } from 'antd/lib/table';
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
import { METRICS_PAGE_QUERY_PARAM } from 'constants/query';
|
import { METRICS_PAGE_QUERY_PARAM } from 'constants/query';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
@ -51,17 +51,12 @@ function TopEndpointsTable(props: TopEndpointsTableProps): JSX.Element {
|
|||||||
title: 'Name',
|
title: 'Name',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
key: 'name',
|
key: 'name',
|
||||||
|
ellipsis: true,
|
||||||
// eslint-disable-next-line react/display-name
|
|
||||||
render: (text: string): JSX.Element => (
|
render: (text: string): JSX.Element => (
|
||||||
<Tooltip placement="topLeft" title={text}>
|
<Tooltip placement="topLeft" title={text}>
|
||||||
<Button
|
<Typography.Link onClick={(): void => handleOnClick(text)}>
|
||||||
className="topEndpointsButton"
|
|
||||||
type="link"
|
|
||||||
onClick={(): void => handleOnClick(text)}
|
|
||||||
>
|
|
||||||
{text}
|
{text}
|
||||||
</Button>
|
</Typography.Link>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -101,9 +96,9 @@ function TopEndpointsTable(props: TopEndpointsTableProps): JSX.Element {
|
|||||||
title={(): string => {
|
title={(): string => {
|
||||||
return 'Top Endpoints';
|
return 'Top Endpoints';
|
||||||
}}
|
}}
|
||||||
|
tableLayout="fixed"
|
||||||
dataSource={data}
|
dataSource={data}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
pagination={false}
|
|
||||||
rowKey="name"
|
rowKey="name"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -1,54 +1,108 @@
|
|||||||
import { Tabs } from 'antd';
|
import RouteTab from 'components/RouteTab';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { generatePath, useParams } from 'react-router-dom';
|
||||||
|
import { useLocation } from 'react-use';
|
||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
|
|
||||||
import ResourceAttributesFilter from './ResourceAttributesFilter';
|
import ResourceAttributesFilter from './ResourceAttributesFilter';
|
||||||
import Application from './Tabs/Application';
|
|
||||||
import DBCall from './Tabs/DBCall';
|
import DBCall from './Tabs/DBCall';
|
||||||
import External from './Tabs/External';
|
import External from './Tabs/External';
|
||||||
|
import Overview from './Tabs/Overview';
|
||||||
|
|
||||||
const { TabPane } = Tabs;
|
const getWidget = (query: Widgets['query']): Widgets => {
|
||||||
|
return {
|
||||||
|
description: '',
|
||||||
|
id: '',
|
||||||
|
isStacked: false,
|
||||||
|
nullZeroValues: '',
|
||||||
|
opacity: '0',
|
||||||
|
panelTypes: 'TIME_SERIES',
|
||||||
|
query,
|
||||||
|
queryData: {
|
||||||
|
data: [],
|
||||||
|
error: false,
|
||||||
|
errorMessage: '',
|
||||||
|
loading: false,
|
||||||
|
},
|
||||||
|
timePreferance: 'GLOBAL_TIME',
|
||||||
|
title: '',
|
||||||
|
stepSize: 60,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function OverViewTab(): JSX.Element {
|
||||||
|
return <Overview getWidget={getWidget} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DbCallTab(): JSX.Element {
|
||||||
|
return <DBCall getWidget={getWidget} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ExternalTab(): JSX.Element {
|
||||||
|
return <External getWidget={getWidget} />;
|
||||||
|
}
|
||||||
|
|
||||||
function ServiceMetrics(): JSX.Element {
|
function ServiceMetrics(): JSX.Element {
|
||||||
const getWidget = (query: Widgets['query']): Widgets => {
|
const { search } = useLocation();
|
||||||
return {
|
const { servicename } = useParams<{ servicename: string }>();
|
||||||
description: '',
|
|
||||||
id: '',
|
const searchParams = new URLSearchParams(search);
|
||||||
isStacked: false,
|
const tab = searchParams.get('tab');
|
||||||
nullZeroValues: '',
|
|
||||||
opacity: '0',
|
const overMetrics = 'Overview Metrics';
|
||||||
panelTypes: 'TIME_SERIES',
|
const dbCallMetrics = 'Database Calls';
|
||||||
query,
|
const externalMetrics = 'External Calls';
|
||||||
queryData: {
|
|
||||||
data: [],
|
const getActiveKey = (): string => {
|
||||||
error: false,
|
if (tab === null) {
|
||||||
errorMessage: '',
|
return overMetrics;
|
||||||
loading: false,
|
}
|
||||||
},
|
|
||||||
timePreferance: 'GLOBAL_TIME',
|
if (tab === dbCallMetrics) {
|
||||||
title: '',
|
return dbCallMetrics;
|
||||||
stepSize: 60,
|
}
|
||||||
};
|
|
||||||
|
if (tab === externalMetrics) {
|
||||||
|
return externalMetrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
return overMetrics;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const activeKey = getActiveKey();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ResourceAttributesFilter />
|
<ResourceAttributesFilter />
|
||||||
<Tabs defaultActiveKey="1">
|
<RouteTab
|
||||||
<TabPane animated destroyInactiveTabPane tab="Application Metrics" key="1">
|
routes={[
|
||||||
<Application getWidget={getWidget} />
|
{
|
||||||
</TabPane>
|
Component: OverViewTab,
|
||||||
|
name: overMetrics,
|
||||||
<TabPane animated destroyInactiveTabPane tab="External Calls" key="2">
|
route: `${generatePath(ROUTES.SERVICE_METRICS, {
|
||||||
<External getWidget={getWidget} />
|
servicename,
|
||||||
</TabPane>
|
})}?tab=${overMetrics}`,
|
||||||
|
},
|
||||||
<TabPane animated destroyInactiveTabPane tab="Database Calls" key="3">
|
{
|
||||||
<DBCall getWidget={getWidget} />
|
Component: DbCallTab,
|
||||||
</TabPane>
|
name: dbCallMetrics,
|
||||||
</Tabs>
|
route: `${generatePath(ROUTES.SERVICE_METRICS, {
|
||||||
|
servicename,
|
||||||
|
})}?tab=${dbCallMetrics}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Component: ExternalTab,
|
||||||
|
name: externalMetrics,
|
||||||
|
route: `${generatePath(ROUTES.SERVICE_METRICS, {
|
||||||
|
servicename,
|
||||||
|
})}?tab=${externalMetrics}`,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
activeKey={activeKey}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ServiceMetrics;
|
export default React.memo(ServiceMetrics);
|
||||||
|
@ -77,7 +77,6 @@ function Metrics(): JSX.Element {
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
dataSource={services}
|
dataSource={services}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
pagination={false}
|
|
||||||
rowKey="serviceName"
|
rowKey="serviceName"
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
|
@ -12,9 +12,23 @@ function ShareModal({
|
|||||||
onToggleHandler,
|
onToggleHandler,
|
||||||
selectedData,
|
selectedData,
|
||||||
}: ShareModalProps): JSX.Element {
|
}: ShareModalProps): JSX.Element {
|
||||||
const [jsonValue, setJSONValue] = useState<string>(
|
const getParsedValue = (): string => {
|
||||||
JSON.stringify(selectedData, null, 2),
|
const updatedData: DashboardData = {
|
||||||
);
|
...selectedData,
|
||||||
|
widgets: selectedData.widgets?.map((widget) => ({
|
||||||
|
...widget,
|
||||||
|
queryData: {
|
||||||
|
...widget.queryData,
|
||||||
|
loading: false,
|
||||||
|
error: false,
|
||||||
|
errorMessage: '',
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
return JSON.stringify(updatedData, null, 2);
|
||||||
|
};
|
||||||
|
|
||||||
|
const [jsonValue, setJSONValue] = useState<string>(getParsedValue());
|
||||||
const [isViewJSON, setIsViewJSON] = useState<boolean>(false);
|
const [isViewJSON, setIsViewJSON] = useState<boolean>(false);
|
||||||
const { t } = useTranslation(['dashboard', 'common']);
|
const { t } = useTranslation(['dashboard', 'common']);
|
||||||
const [state, setCopy] = useCopyToClipboard();
|
const [state, setCopy] = useCopyToClipboard();
|
||||||
|
@ -5,6 +5,7 @@ import React, { memo } from 'react';
|
|||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
|
import AppReducer from 'types/reducer/app';
|
||||||
import DashboardReducer from 'types/reducer/dashboards';
|
import DashboardReducer from 'types/reducer/dashboards';
|
||||||
|
|
||||||
import { NewWidgetProps } from '../../index';
|
import { NewWidgetProps } from '../../index';
|
||||||
@ -18,6 +19,7 @@ function WidgetGraph({
|
|||||||
const { dashboards, isQueryFired } = useSelector<AppState, DashboardReducer>(
|
const { dashboards, isQueryFired } = useSelector<AppState, DashboardReducer>(
|
||||||
(state) => state.dashboards,
|
(state) => state.dashboards,
|
||||||
);
|
);
|
||||||
|
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
const [selectedDashboard] = dashboards;
|
const [selectedDashboard] = dashboards;
|
||||||
const { search } = useLocation();
|
const { search } = useLocation();
|
||||||
|
|
||||||
@ -31,7 +33,11 @@ function WidgetGraph({
|
|||||||
const selectedWidget = widgets.find((e) => e.id === widgetId);
|
const selectedWidget = widgets.find((e) => e.id === widgetId);
|
||||||
|
|
||||||
if (selectedWidget === undefined) {
|
if (selectedWidget === undefined) {
|
||||||
return <Card isQueryType={false}>Invalid widget</Card>;
|
return (
|
||||||
|
<Card isDarkMode={isDarkMode} isQueryType={false}>
|
||||||
|
Invalid widget
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { queryData } = selectedWidget;
|
const { queryData } = selectedWidget;
|
||||||
|
@ -38,7 +38,7 @@ function DisplayName({
|
|||||||
dispatch({
|
dispatch({
|
||||||
type: UPDATE_ORG_NAME,
|
type: UPDATE_ORG_NAME,
|
||||||
payload: {
|
payload: {
|
||||||
index,
|
orgId,
|
||||||
name: orgName,
|
name: orgName,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { CheckCircleTwoTone, WarningOutlined } from '@ant-design/icons';
|
import { CheckCircleTwoTone, WarningOutlined } from '@ant-design/icons';
|
||||||
import { Menu, Typography } from 'antd';
|
import { Menu, Space, 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';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
@ -19,6 +19,7 @@ import {
|
|||||||
Sider,
|
Sider,
|
||||||
SlackButton,
|
SlackButton,
|
||||||
SlackMenuItemContainer,
|
SlackMenuItemContainer,
|
||||||
|
Tags,
|
||||||
VersionContainer,
|
VersionContainer,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
|
|
||||||
@ -96,13 +97,21 @@ function SideNav(): JSX.Element {
|
|||||||
selectedKeys={[pathname]}
|
selectedKeys={[pathname]}
|
||||||
mode="inline"
|
mode="inline"
|
||||||
>
|
>
|
||||||
{menus.map(({ to, Icon, name }) => (
|
{menus.map(({ to, Icon, name, tags }) => (
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
key={to}
|
key={to}
|
||||||
icon={<Icon />}
|
icon={<Icon />}
|
||||||
onClick={(): void => onClickHandler(to)}
|
onClick={(): void => onClickHandler(to)}
|
||||||
>
|
>
|
||||||
<Typography>{name}</Typography>
|
<Space style={{ position: 'relative' }}>
|
||||||
|
<Typography>{name}</Typography>
|
||||||
|
{tags &&
|
||||||
|
tags.map((e) => (
|
||||||
|
<Tags color="#177DDC" key={e}>
|
||||||
|
<Typography.Text strong>{e}</Typography.Text>
|
||||||
|
</Tags>
|
||||||
|
))}
|
||||||
|
</Space>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
))}
|
))}
|
||||||
{sidebar.map((props, index) => (
|
{sidebar.map((props, index) => (
|
||||||
|
@ -41,6 +41,7 @@ const menus: SidebarMenu[] = [
|
|||||||
to: ROUTES.SERVICE_MAP,
|
to: ROUTES.SERVICE_MAP,
|
||||||
name: 'Service Map',
|
name: 'Service Map',
|
||||||
Icon: DeploymentUnitOutlined,
|
Icon: DeploymentUnitOutlined,
|
||||||
|
tags: ['Beta'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Icon: LineChartOutlined,
|
Icon: LineChartOutlined,
|
||||||
@ -63,6 +64,7 @@ interface SidebarMenu {
|
|||||||
to: string;
|
to: string;
|
||||||
name: string;
|
name: string;
|
||||||
Icon: typeof ApiOutlined;
|
Icon: typeof ApiOutlined;
|
||||||
|
tags?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default menus;
|
export default menus;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Layout, Typography } from 'antd';
|
import { Layout, Tag, Typography } from 'antd';
|
||||||
import { StyledCSS } from 'container/GantChart/Trace/styles';
|
import { StyledCSS } from 'container/GantChart/Trace/styles';
|
||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
@ -75,3 +75,11 @@ export const VersionContainer = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const Tags = styled(Tag)`
|
||||||
|
&&& {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Checkbox, notification, Typography } from 'antd';
|
import { Checkbox, notification, Tooltip, Typography } from 'antd';
|
||||||
import getFilters from 'api/trace/getFilters';
|
import getFilters from 'api/trace/getFilters';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import React, { useState } from 'react';
|
import React, { useMemo, 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';
|
||||||
@ -11,7 +11,7 @@ import { UPDATE_ALL_FILTERS } from 'types/actions/trace';
|
|||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import { TraceFilterEnum, TraceReducer } from 'types/reducer/trace';
|
import { TraceFilterEnum, TraceReducer } from 'types/reducer/trace';
|
||||||
|
|
||||||
import { CheckBoxContainer } from './styles';
|
import { CheckBoxContainer, ParaGraph } from './styles';
|
||||||
|
|
||||||
function CheckBoxComponent(props: CheckBoxProps): JSX.Element {
|
function CheckBoxComponent(props: CheckBoxProps): JSX.Element {
|
||||||
const {
|
const {
|
||||||
@ -155,6 +155,11 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element {
|
|||||||
|
|
||||||
const isCheckBoxSelected = isUserSelected;
|
const isCheckBoxSelected = isUserSelected;
|
||||||
|
|
||||||
|
const TooTipOverLay = useMemo(
|
||||||
|
(): JSX.Element => <Typography>{keyValue}</Typography>,
|
||||||
|
[keyValue],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CheckBoxContainer>
|
<CheckBoxContainer>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@ -164,7 +169,9 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element {
|
|||||||
defaultChecked
|
defaultChecked
|
||||||
key={keyValue}
|
key={keyValue}
|
||||||
>
|
>
|
||||||
{keyValue}
|
<Tooltip overlay={TooTipOverLay}>
|
||||||
|
<ParaGraph ellipsis>{keyValue}</ParaGraph>
|
||||||
|
</Tooltip>
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
{isCheckBoxSelected ? (
|
{isCheckBoxSelected ? (
|
||||||
<Typography>{value}</Typography>
|
<Typography>{value}</Typography>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { Typography } from 'antd';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const CheckBoxContainer = styled.div`
|
export const CheckBoxContainer = styled.div`
|
||||||
@ -9,3 +10,10 @@ export const CheckBoxContainer = styled.div`
|
|||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const ParaGraph = styled(Typography.Paragraph)`
|
||||||
|
&&& {
|
||||||
|
margin: 0;
|
||||||
|
max-width: 8rem;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
@ -34,7 +34,7 @@ function TraceGraph(): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loading || payload === undefined) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Spinner height="20vh" size="small" tip="Loading..." />
|
<Spinner height="20vh" size="small" tip="Loading..." />
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
import { notification } from 'antd';
|
||||||
import getTriggeredApi from 'api/alerts/getTriggered';
|
import getTriggeredApi from 'api/alerts/getTriggered';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import { State } from 'hooks/useFetch';
|
import { State } from 'hooks/useFetch';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PayloadProps } from 'types/api/alerts/getTriggered';
|
import { PayloadProps } from 'types/api/alerts/getTriggered';
|
||||||
|
|
||||||
import TriggerComponent from './TriggeredAlert';
|
import TriggerComponent from './TriggeredAlert';
|
||||||
@ -14,6 +16,7 @@ function TriggeredAlerts(): JSX.Element {
|
|||||||
success: false,
|
success: false,
|
||||||
payload: [],
|
payload: [],
|
||||||
});
|
});
|
||||||
|
const { t } = useTranslation(['common']);
|
||||||
|
|
||||||
const fetchData = useCallback(async () => {
|
const fetchData = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
@ -56,8 +59,16 @@ function TriggeredAlerts(): JSX.Element {
|
|||||||
fetchData();
|
fetchData();
|
||||||
}, [fetchData]);
|
}, [fetchData]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (groupState.error) {
|
||||||
|
notification.error({
|
||||||
|
message: groupState.errorMessage || t('something_went_wrong'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [groupState.error, groupState.errorMessage, t]);
|
||||||
|
|
||||||
if (groupState.error) {
|
if (groupState.error) {
|
||||||
return <div>{groupState.errorMessage}</div>;
|
return <TriggerComponent allAlerts={[]} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groupState.loading || groupState.payload === undefined) {
|
if (groupState.loading || groupState.payload === undefined) {
|
||||||
|
@ -5,12 +5,13 @@ 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, useSelector } from 'react-redux';
|
||||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||||
import { getDetailedServiceMapItems, ServiceMapStore } 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';
|
||||||
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
import SelectService from './SelectService';
|
import SelectService from './SelectService';
|
||||||
import { getGraphData, getTooltip, getZoomPx, transformLabel } from './utils';
|
import { getGraphData, getTooltip, getZoomPx, transformLabel } from './utils';
|
||||||
@ -53,6 +54,8 @@ export interface graphDataType {
|
|||||||
function ServiceMap(props: ServiceMapProps): JSX.Element {
|
function ServiceMap(props: ServiceMapProps): JSX.Element {
|
||||||
const fgRef = useRef();
|
const fgRef = useRef();
|
||||||
|
|
||||||
|
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
|
|
||||||
const { getDetailedServiceMapItems, globalTime, serviceMap } = props;
|
const { getDetailedServiceMapItems, globalTime, serviceMap } = props;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -115,10 +118,11 @@ function ServiceMap(props: ServiceMapProps): JSX.Element {
|
|||||||
ctx.fillStyle = node.color;
|
ctx.fillStyle = node.color;
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(node.x, node.y, width, 0, 2 * Math.PI, false);
|
ctx.arc(node.x, node.y, width, 0, 2 * Math.PI, false);
|
||||||
|
ctx.fillStyle = isDarkMode ? '#3d0b00' : '#ffbcad';
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.textAlign = 'center';
|
ctx.textAlign = 'center';
|
||||||
ctx.textBaseline = 'middle';
|
ctx.textBaseline = 'middle';
|
||||||
ctx.fillStyle = '#646464';
|
ctx.fillStyle = isDarkMode ? '#ffffff' : '#000000';
|
||||||
ctx.fillText(label, node.x, node.y);
|
ctx.fillText(label, node.x, node.y);
|
||||||
}}
|
}}
|
||||||
onNodeClick={(node) => {
|
onNodeClick={(node) => {
|
||||||
|
@ -5,6 +5,7 @@ import Spinner from 'components/Spinner';
|
|||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import ErrorDetailsContainer from 'container/ErrorDetails';
|
import ErrorDetailsContainer from 'container/ErrorDetails';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { Redirect, useLocation } from 'react-router-dom';
|
import { Redirect, useLocation } from 'react-router-dom';
|
||||||
@ -13,6 +14,7 @@ import { PayloadProps } from 'types/api/errors/getById';
|
|||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
function ErrorDetails(): JSX.Element {
|
function ErrorDetails(): JSX.Element {
|
||||||
|
const { t } = useTranslation(['common']);
|
||||||
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
);
|
);
|
||||||
@ -22,6 +24,7 @@ function ErrorDetails(): JSX.Element {
|
|||||||
const errorId = params.get('errorId');
|
const errorId = params.get('errorId');
|
||||||
const errorType = params.get('errorType');
|
const errorType = params.get('errorType');
|
||||||
const serviceName = params.get('serviceName');
|
const serviceName = params.get('serviceName');
|
||||||
|
const defaultError = t('something_went_wrong');
|
||||||
|
|
||||||
const { data, status } = useQuery(
|
const { data, status } = useQuery(
|
||||||
[
|
[
|
||||||
@ -72,16 +75,31 @@ function ErrorDetails(): JSX.Element {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// if errorType and serviceName is null redirecting to the ALL_ERROR page not now
|
||||||
if (errorType === null || serviceName === null) {
|
if (errorType === null || serviceName === null) {
|
||||||
return <Redirect to={ROUTES.ALL_ERROR} />;
|
return <Redirect to={ROUTES.ALL_ERROR} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// when the api is in loading state
|
||||||
if (status === 'loading' || ErrorIdStatus === 'loading') {
|
if (status === 'loading' || ErrorIdStatus === 'loading') {
|
||||||
return <Spinner tip="Loading.." />;
|
return <Spinner tip="Loading.." />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if any error occurred while loading
|
||||||
if (status === 'error' || ErrorIdStatus === 'error') {
|
if (status === 'error' || ErrorIdStatus === 'error') {
|
||||||
return <Typography>{data?.error || errorIdPayload?.error}</Typography>;
|
return (
|
||||||
|
<Typography>
|
||||||
|
{data?.error || errorIdPayload?.error || defaultError}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if API is successfully but there is an error
|
||||||
|
if (
|
||||||
|
(status === 'success' && data?.statusCode >= 400) ||
|
||||||
|
(ErrorIdStatus === 'success' && errorIdPayload.statusCode >= 400)
|
||||||
|
) {
|
||||||
|
return <Typography>{data?.error || defaultError}</Typography>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -20,7 +20,10 @@ function Login(): JSX.Element {
|
|||||||
enabled: !isLoggedIn,
|
enabled: !isLoggedIn,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (versionResult.status === 'error') {
|
if (
|
||||||
|
versionResult.status === 'error' ||
|
||||||
|
(versionResult.status === 'success' && versionResult?.data.statusCode !== 200)
|
||||||
|
) {
|
||||||
return (
|
return (
|
||||||
<Typography>
|
<Typography>
|
||||||
{versionResult.data?.error || t('something_went_wrong')}
|
{versionResult.data?.error || t('something_went_wrong')}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { notification } from 'antd';
|
||||||
import getLocalStorageKey from 'api/browser/localstorage/get';
|
import getLocalStorageKey from 'api/browser/localstorage/get';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import { SKIP_ONBOARDING } from 'constants/onboarding';
|
import { SKIP_ONBOARDING } from 'constants/onboarding';
|
||||||
@ -20,10 +21,20 @@ function Metrics({ getService }: MetricsProps): JSX.Element {
|
|||||||
AppState,
|
AppState,
|
||||||
GlobalReducer
|
GlobalReducer
|
||||||
>((state) => state.globalTime);
|
>((state) => state.globalTime);
|
||||||
const { services, resourceAttributeQueries } = useSelector<
|
const {
|
||||||
AppState,
|
services,
|
||||||
MetricReducer
|
resourceAttributeQueries,
|
||||||
>((state) => state.metrics);
|
error,
|
||||||
|
errorMessage,
|
||||||
|
} = useSelector<AppState, MetricReducer>((state) => state.metrics);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (error) {
|
||||||
|
notification.error({
|
||||||
|
message: errorMessage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [error, errorMessage]);
|
||||||
|
|
||||||
const selectedTags = useMemo(
|
const selectedTags = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
@ -10,8 +10,8 @@ export const Container = styled.div`
|
|||||||
|
|
||||||
export const LeftContainer = styled(Card)`
|
export const LeftContainer = styled(Card)`
|
||||||
flex: 0.5;
|
flex: 0.5;
|
||||||
width: 95%;
|
margin-right: 0.5rem;
|
||||||
padding-right: 0.5rem;
|
width: 15rem;
|
||||||
|
|
||||||
.ant-card-body {
|
.ant-card-body {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -7,6 +7,7 @@ import AppActions from 'types/actions';
|
|||||||
import {
|
import {
|
||||||
UPDATE_ALL_FILTERS,
|
UPDATE_ALL_FILTERS,
|
||||||
UPDATE_TRACE_FILTER_LOADING,
|
UPDATE_TRACE_FILTER_LOADING,
|
||||||
|
UPDATE_TRACE_GRAPH_LOADING,
|
||||||
} from 'types/actions/trace';
|
} from 'types/actions/trace';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import { TraceFilterEnum, TraceReducer } from 'types/reducer/trace';
|
import { TraceFilterEnum, TraceReducer } from 'types/reducer/trace';
|
||||||
@ -183,6 +184,12 @@ export const GetInitialTraceFilter = (
|
|||||||
filterLoading: false,
|
filterLoading: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
dispatch({
|
||||||
|
type: UPDATE_TRACE_GRAPH_LOADING,
|
||||||
|
payload: {
|
||||||
|
loading: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
dispatch({
|
dispatch({
|
||||||
@ -191,6 +198,12 @@ export const GetInitialTraceFilter = (
|
|||||||
filterLoading: false,
|
filterLoading: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
dispatch({
|
||||||
|
type: UPDATE_TRACE_GRAPH_LOADING,
|
||||||
|
payload: {
|
||||||
|
loading: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { applyMiddleware, compose, createStore } from 'redux';
|
import { applyMiddleware, compose, createStore } from 'redux';
|
||||||
import thunk, { ThunkMiddleware } from 'redux-thunk';
|
import thunk, { ThunkMiddleware } from 'redux-thunk';
|
||||||
|
import AppActions from 'types/actions';
|
||||||
|
|
||||||
import reducers, { AppState } from './reducers';
|
import reducers, { AppState } from './reducers';
|
||||||
|
|
||||||
@ -8,8 +9,9 @@ const composeEnhancers =
|
|||||||
|
|
||||||
const store = createStore(
|
const store = createStore(
|
||||||
reducers,
|
reducers,
|
||||||
// @TODO Add Type for AppActions also
|
composeEnhancers(
|
||||||
composeEnhancers(applyMiddleware(thunk as ThunkMiddleware<AppState>)),
|
applyMiddleware(thunk as ThunkMiddleware<AppState, AppActions>),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
export default store;
|
export default store;
|
||||||
|
@ -12,6 +12,7 @@ import {
|
|||||||
UPDATE_CURRENT_VERSION,
|
UPDATE_CURRENT_VERSION,
|
||||||
UPDATE_LATEST_VERSION,
|
UPDATE_LATEST_VERSION,
|
||||||
UPDATE_LATEST_VERSION_ERROR,
|
UPDATE_LATEST_VERSION_ERROR,
|
||||||
|
UPDATE_ORG,
|
||||||
UPDATE_ORG_NAME,
|
UPDATE_ORG_NAME,
|
||||||
UPDATE_USER,
|
UPDATE_USER,
|
||||||
UPDATE_USER_ACCESS_REFRESH_ACCESS_TOKEN,
|
UPDATE_USER_ACCESS_REFRESH_ACCESS_TOKEN,
|
||||||
@ -172,16 +173,19 @@ const appReducer = (
|
|||||||
|
|
||||||
case UPDATE_ORG_NAME: {
|
case UPDATE_ORG_NAME: {
|
||||||
const stateOrg = state.org || ({} as OrgPayload);
|
const stateOrg = state.org || ({} as OrgPayload);
|
||||||
const { index, name: updatedName } = action.payload;
|
const { orgId, name: updatedName } = action.payload;
|
||||||
const current = stateOrg[index];
|
|
||||||
|
const orgIndex = stateOrg.findIndex((e) => e.id === orgId);
|
||||||
|
|
||||||
|
const current = stateOrg[orgIndex];
|
||||||
|
|
||||||
const updatedOrg: OrgPayload = [
|
const updatedOrg: OrgPayload = [
|
||||||
...stateOrg.slice(0, index),
|
...stateOrg.slice(0, orgIndex),
|
||||||
{
|
{
|
||||||
...current,
|
...current,
|
||||||
name: updatedName,
|
name: updatedName,
|
||||||
},
|
},
|
||||||
...stateOrg.slice(index + 1, stateOrg.length),
|
...stateOrg.slice(orgIndex + 1, stateOrg.length),
|
||||||
];
|
];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -190,6 +194,13 @@ const appReducer = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case UPDATE_ORG: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
org: action.payload.org,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ export const UPDATE_USER_IS_FETCH = 'UPDATE_USER_IS_FETCH';
|
|||||||
export const UPDATE_USER_ORG_ROLE = 'UPDATE_USER_ORG_ROLE';
|
export const UPDATE_USER_ORG_ROLE = 'UPDATE_USER_ORG_ROLE';
|
||||||
export const UPDATE_USER = 'UPDATE_USER';
|
export const UPDATE_USER = 'UPDATE_USER';
|
||||||
export const UPDATE_ORG_NAME = 'UPDATE_ORG_NAME';
|
export const UPDATE_ORG_NAME = 'UPDATE_ORG_NAME';
|
||||||
|
export const UPDATE_ORG = 'UPDATE_ORG';
|
||||||
|
|
||||||
export interface SwitchDarkMode {
|
export interface SwitchDarkMode {
|
||||||
type: typeof SWITCH_DARK_MODE;
|
type: typeof SWITCH_DARK_MODE;
|
||||||
@ -98,7 +99,14 @@ export interface UpdateOrgName {
|
|||||||
type: typeof UPDATE_ORG_NAME;
|
type: typeof UPDATE_ORG_NAME;
|
||||||
payload: {
|
payload: {
|
||||||
name: string;
|
name: string;
|
||||||
index: number;
|
orgId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateOrg {
|
||||||
|
type: typeof UPDATE_ORG;
|
||||||
|
payload: {
|
||||||
|
org: AppReducer['org'];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,4 +121,5 @@ export type AppAction =
|
|||||||
| UpdateUserIsFetched
|
| UpdateUserIsFetched
|
||||||
| UpdateUserOrgRole
|
| UpdateUserOrgRole
|
||||||
| UpdateUser
|
| UpdateUser
|
||||||
| UpdateOrgName;
|
| UpdateOrgName
|
||||||
|
| UpdateOrg;
|
||||||
|
@ -3,6 +3,7 @@ declare global {
|
|||||||
namespace NodeJS {
|
namespace NodeJS {
|
||||||
interface ProcessEnv {
|
interface ProcessEnv {
|
||||||
FRONTEND_API_ENDPOINT: string | undefined;
|
FRONTEND_API_ENDPOINT: string | undefined;
|
||||||
|
NODE_ENV: 'development' | 'production' | 'test';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"baseUrl": "./src",
|
"baseUrl": "./src",
|
||||||
"downlevelIteration": true,
|
"downlevelIteration": true,
|
||||||
"plugins": [{ "name": "typescript-plugin-css-modules" }]
|
"plugins": [{ "name": "typescript-plugin-css-modules" }],
|
||||||
|
"types": ["cypress", "@testing-library/cypress", "node"]
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules"],
|
"exclude": ["node_modules"],
|
||||||
"include": [
|
"include": [
|
||||||
@ -31,6 +32,9 @@
|
|||||||
"./conf/default.conf",
|
"./conf/default.conf",
|
||||||
"./public",
|
"./public",
|
||||||
"./tests",
|
"./tests",
|
||||||
"playwright.config.ts"
|
"playwright.config.ts",
|
||||||
|
"./commitlint.config.js",
|
||||||
|
"./webpack.config.js",
|
||||||
|
"./webpack.config.prod.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
// shared config (dev and prod)
|
// shared config (dev and prod)
|
||||||
const { resolve } = require('path');
|
const { resolve } = require('path');
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
@ -5,8 +6,7 @@ const portFinderSync = require('portfinder-sync');
|
|||||||
const dotenv = require('dotenv');
|
const dotenv = require('dotenv');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
||||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
|
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
|
||||||
.BundleAnalyzerPlugin;
|
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ const config = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
plugins: plugins,
|
plugins,
|
||||||
performance: {
|
performance: {
|
||||||
hints: false,
|
hints: false,
|
||||||
},
|
},
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
// shared config (dev and prod)
|
// shared config (dev and prod)
|
||||||
const { resolve } = require('path');
|
const { resolve } = require('path');
|
||||||
@ -9,8 +10,7 @@ const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
|||||||
const TerserPlugin = require('terser-webpack-plugin');
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
|
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
|
||||||
.BundleAnalyzerPlugin;
|
|
||||||
const Critters = require('critters-webpack-plugin');
|
const Critters = require('critters-webpack-plugin');
|
||||||
|
|
||||||
const plugins = [
|
const plugins = [
|
||||||
@ -119,13 +119,13 @@ const config = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
plugins: plugins,
|
plugins,
|
||||||
optimization: {
|
optimization: {
|
||||||
chunkIds: 'named',
|
chunkIds: 'named',
|
||||||
concatenateModules: false,
|
concatenateModules: false,
|
||||||
emitOnErrors: true,
|
emitOnErrors: true,
|
||||||
flagIncludedChunks: true,
|
flagIncludedChunks: true,
|
||||||
innerGraph: true, //tells webpack whether to conduct inner graph analysis for unused exports.
|
innerGraph: true, // tells webpack whether to conduct inner graph analysis for unused exports.
|
||||||
mangleWasmImports: true,
|
mangleWasmImports: true,
|
||||||
mergeDuplicateChunks: true,
|
mergeDuplicateChunks: true,
|
||||||
minimize: true,
|
minimize: true,
|
||||||
|
3385
frontend/yarn.lock
3385
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -2333,6 +2333,40 @@ func (r *ClickHouseReader) setColdStorage(ctx context.Context, tableName string,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ClickHouseReader) RemoveTTL(ctx context.Context,
|
||||||
|
params *model.RemoveTTLParams) (*model.RemoveTTLResponseItem, *model.ApiError) {
|
||||||
|
|
||||||
|
var reqs []string
|
||||||
|
templateQuery := `ALTER TABLE %v REMOVE TTL`
|
||||||
|
tracesTables := []string{signozTraceDBName + "." + signozTraceTableName, signozTraceDBName + "." + signozDurationMVTable, signozTraceDBName + "." + signozSpansTable, signozTraceDBName + "." + signozErrorIndexTable}
|
||||||
|
metricsTables := []string{signozMetricDBName + "." + signozSampleName}
|
||||||
|
|
||||||
|
switch params.Type {
|
||||||
|
case constants.TraceTTL:
|
||||||
|
for _, tableName := range tracesTables {
|
||||||
|
reqs = append(reqs, fmt.Sprintf(templateQuery, tableName))
|
||||||
|
}
|
||||||
|
case constants.MetricsTTL:
|
||||||
|
for _, tableName := range metricsTables {
|
||||||
|
reqs = append(reqs, fmt.Sprintf(templateQuery, tableName))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
for _, tableName := range append(append([]string{}, tracesTables...), metricsTables...) {
|
||||||
|
reqs = append(reqs, fmt.Sprintf(templateQuery, tableName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zap.S().Debugf("Executing remove TTL requests: %s\n", reqs)
|
||||||
|
for _, req := range reqs {
|
||||||
|
if err := r.db.Exec(ctx, req); err != nil {
|
||||||
|
zap.S().Error(fmt.Errorf("error while removing ttl. Err=%v", err))
|
||||||
|
return nil, &model.ApiError{Typ: model.ErrorExec,
|
||||||
|
Err: fmt.Errorf("error while removing ttl. Err=%v", err)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &model.RemoveTTLResponseItem{Message: "ttl has been successfully removed"}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetDisks returns a list of disks {name, type} configured in clickhouse DB.
|
// GetDisks returns a list of disks {name, type} configured in clickhouse DB.
|
||||||
func (r *ClickHouseReader) GetDisks(ctx context.Context) (*[]model.DiskItem, *model.ApiError) {
|
func (r *ClickHouseReader) GetDisks(ctx context.Context) (*[]model.DiskItem, *model.ApiError) {
|
||||||
diskItems := []model.DiskItem{}
|
diskItems := []model.DiskItem{}
|
||||||
|
@ -310,6 +310,7 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router) {
|
|||||||
router.HandleFunc("/api/v1/serviceMapDependencies", ViewAccess(aH.serviceMapDependencies)).Methods(http.MethodPost)
|
router.HandleFunc("/api/v1/serviceMapDependencies", ViewAccess(aH.serviceMapDependencies)).Methods(http.MethodPost)
|
||||||
router.HandleFunc("/api/v1/settings/ttl", AdminAccess(aH.setTTL)).Methods(http.MethodPost)
|
router.HandleFunc("/api/v1/settings/ttl", AdminAccess(aH.setTTL)).Methods(http.MethodPost)
|
||||||
router.HandleFunc("/api/v1/settings/ttl", ViewAccess(aH.getTTL)).Methods(http.MethodGet)
|
router.HandleFunc("/api/v1/settings/ttl", ViewAccess(aH.getTTL)).Methods(http.MethodGet)
|
||||||
|
router.HandleFunc("/api/v1/settings/ttl", AdminAccess(aH.removeTTL)).Methods(http.MethodDelete)
|
||||||
|
|
||||||
router.HandleFunc("/api/v1/version", OpenAccess(aH.getVersion)).Methods(http.MethodGet)
|
router.HandleFunc("/api/v1/version", OpenAccess(aH.getVersion)).Methods(http.MethodGet)
|
||||||
|
|
||||||
@ -1162,6 +1163,47 @@ func (aH *APIHandler) getTTL(w http.ResponseWriter, r *http.Request) {
|
|||||||
aH.writeJSON(w, r, result)
|
aH.writeJSON(w, r, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (aH *APIHandler) removeTTL(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ttlParams, err := parseRemoveTTL(r)
|
||||||
|
if aH.handleError(w, err, http.StatusBadRequest) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
existingTTL, apiErr := (*aH.reader).GetTTL(context.Background(), &model.GetTTLParams{GetAllTTL: true})
|
||||||
|
if apiErr != nil && aH.handleError(w, apiErr.Err, http.StatusInternalServerError) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ttlParams.Type == constants.TraceTTL && existingTTL.TracesTime == -1 &&
|
||||||
|
aH.handleError(w, fmt.Errorf("traces doesn't have any TTL set, cannot remove"), http.StatusBadRequest) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ttlParams.Type == constants.MetricsTTL && existingTTL.MetricsTime == -1 &&
|
||||||
|
aH.handleError(w, fmt.Errorf("metrics doesn't have any TTL set, cannot remove"), http.StatusBadRequest) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ttlParams.RemoveAllTTL {
|
||||||
|
if existingTTL.TracesTime == -1 && existingTTL.MetricsTime != -1 {
|
||||||
|
ttlParams.Type = constants.MetricsTTL
|
||||||
|
ttlParams.RemoveAllTTL = false
|
||||||
|
} else if existingTTL.TracesTime != -1 && existingTTL.MetricsTime == -1 {
|
||||||
|
ttlParams.Type = constants.TraceTTL
|
||||||
|
ttlParams.RemoveAllTTL = false
|
||||||
|
} else if aH.handleError(w, fmt.Errorf("no TTL set, cannot remove"), http.StatusBadRequest) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result, apiErr := (*aH.reader).RemoveTTL(context.Background(), ttlParams)
|
||||||
|
if apiErr != nil && aH.handleError(w, apiErr.Err, http.StatusInternalServerError) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
aH.writeJSON(w, r, result)
|
||||||
|
}
|
||||||
|
|
||||||
func (aH *APIHandler) getDisks(w http.ResponseWriter, r *http.Request) {
|
func (aH *APIHandler) getDisks(w http.ResponseWriter, r *http.Request) {
|
||||||
result, apiErr := (*aH.reader).GetDisks(context.Background())
|
result, apiErr := (*aH.reader).GetDisks(context.Background())
|
||||||
if apiErr != nil && aH.handleError(w, apiErr.Err, http.StatusInternalServerError) {
|
if apiErr != nil && aH.handleError(w, apiErr.Err, http.StatusInternalServerError) {
|
||||||
|
@ -50,6 +50,7 @@ type Reader interface {
|
|||||||
|
|
||||||
// Setter Interfaces
|
// Setter Interfaces
|
||||||
SetTTL(ctx context.Context, ttlParams *model.TTLParams) (*model.SetTTLResponseItem, *model.ApiError)
|
SetTTL(ctx context.Context, ttlParams *model.TTLParams) (*model.SetTTLResponseItem, *model.ApiError)
|
||||||
|
RemoveTTL(ctx context.Context, ttlParams *model.RemoveTTLParams) (*model.RemoveTTLResponseItem, *model.ApiError)
|
||||||
|
|
||||||
GetMetricAutocompleteMetricNames(ctx context.Context, matchText string) (*[]string, *model.ApiError)
|
GetMetricAutocompleteMetricNames(ctx context.Context, matchText string) (*[]string, *model.ApiError)
|
||||||
GetMetricAutocompleteTagKey(ctx context.Context, params *model.MetricAutocompleteTagParams) (*[]string, *model.ApiError)
|
GetMetricAutocompleteTagKey(ctx context.Context, params *model.MetricAutocompleteTagParams) (*[]string, *model.ApiError)
|
||||||
|
@ -545,7 +545,7 @@ func parseTTLParams(r *http.Request) (*model.TTLParams, error) {
|
|||||||
|
|
||||||
// Validate the TTL duration.
|
// Validate the TTL duration.
|
||||||
durationParsed, err := time.ParseDuration(delDuration)
|
durationParsed, err := time.ParseDuration(delDuration)
|
||||||
if err != nil {
|
if err != nil || durationParsed.Seconds() <= 0 {
|
||||||
return nil, fmt.Errorf("Not a valid TTL duration %v", delDuration)
|
return nil, fmt.Errorf("Not a valid TTL duration %v", delDuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -554,7 +554,7 @@ func parseTTLParams(r *http.Request) (*model.TTLParams, error) {
|
|||||||
// If some cold storage is provided, validate the cold storage move TTL.
|
// If some cold storage is provided, validate the cold storage move TTL.
|
||||||
if len(coldStorage) > 0 {
|
if len(coldStorage) > 0 {
|
||||||
toColdParsed, err = time.ParseDuration(toColdDuration)
|
toColdParsed, err = time.ParseDuration(toColdDuration)
|
||||||
if err != nil {
|
if err != nil || toColdParsed.Seconds() <= 0 {
|
||||||
return nil, fmt.Errorf("Not a valid toCold TTL duration %v", toColdDuration)
|
return nil, fmt.Errorf("Not a valid toCold TTL duration %v", toColdDuration)
|
||||||
}
|
}
|
||||||
if toColdParsed.Seconds() != 0 && toColdParsed.Seconds() >= durationParsed.Seconds() {
|
if toColdParsed.Seconds() != 0 && toColdParsed.Seconds() >= durationParsed.Seconds() {
|
||||||
@ -587,6 +587,23 @@ func parseGetTTL(r *http.Request) (*model.GetTTLParams, error) {
|
|||||||
return &model.GetTTLParams{Type: typeTTL, GetAllTTL: getAllTTL}, nil
|
return &model.GetTTLParams{Type: typeTTL, GetAllTTL: getAllTTL}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseRemoveTTL(r *http.Request) (*model.RemoveTTLParams, error) {
|
||||||
|
|
||||||
|
typeTTL := r.URL.Query().Get("type")
|
||||||
|
removeAllTTL := false
|
||||||
|
|
||||||
|
if len(typeTTL) == 0 {
|
||||||
|
removeAllTTL = true
|
||||||
|
} else {
|
||||||
|
// Validate the type parameter
|
||||||
|
if typeTTL != constants.TraceTTL && typeTTL != constants.MetricsTTL {
|
||||||
|
return nil, fmt.Errorf("type param should be <metrics|traces>, got %v", typeTTL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &model.RemoveTTLParams{Type: typeTTL, RemoveAllTTL: removeAllTTL}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func parseUserRequest(r *http.Request) (*model.User, error) {
|
func parseUserRequest(r *http.Request) (*model.User, error) {
|
||||||
var req model.User
|
var req model.User
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
_ "net/http/pprof" // http profiler
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -257,7 +258,15 @@ func (s *Server) Start() error {
|
|||||||
zap.S().Error("Could not start HTTP server", zap.Error(err))
|
zap.S().Error("Could not start HTTP server", zap.Error(err))
|
||||||
}
|
}
|
||||||
s.unavailableChannel <- healthcheck.Unavailable
|
s.unavailableChannel <- healthcheck.Unavailable
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
zap.S().Info("Starting pprof server", zap.String("addr", constants.DebugHttpPort))
|
||||||
|
|
||||||
|
err = http.ListenAndServe(constants.DebugHttpPort, nil)
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Error("Could not start pprof server", zap.Error(err))
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -6,7 +6,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
HTTPHostPort = "0.0.0.0:8080"
|
HTTPHostPort = "0.0.0.0:8080" // Address to serve http (query service)
|
||||||
|
DebugHttpPort = "0.0.0.0:6060" // Address to serve http (pprof)
|
||||||
)
|
)
|
||||||
|
|
||||||
var DEFAULT_TELEMETRY_ANONYMOUS = false
|
var DEFAULT_TELEMETRY_ANONYMOUS = false
|
||||||
|
@ -206,3 +206,8 @@ type GetErrorParams struct {
|
|||||||
ErrorID string
|
ErrorID string
|
||||||
ServiceName string
|
ServiceName string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RemoveTTLParams struct {
|
||||||
|
Type string
|
||||||
|
RemoveAllTTL bool
|
||||||
|
}
|
||||||
|
@ -246,6 +246,10 @@ type SetTTLResponseItem struct {
|
|||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RemoveTTLResponseItem struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
type DiskItem struct {
|
type DiskItem struct {
|
||||||
Name string `json:"name,omitempty" ch:"name"`
|
Name string `json:"name,omitempty" ch:"name"`
|
||||||
Type string `json:"type,omitempty" ch:"type"`
|
Type string `json:"type,omitempty" ch:"type"`
|
||||||
|
Loading…
x
Reference in New Issue
Block a user