mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-14 12:05:53 +08:00
commit
1ded475b37
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@ -2,6 +2,6 @@
|
|||||||
# Owners are automatically requested for review for PRs that changes code
|
# Owners are automatically requested for review for PRs that changes code
|
||||||
# that they own.
|
# that they own.
|
||||||
* @ankitnayan
|
* @ankitnayan
|
||||||
/frontend/ @palashgdev @pranshuchittora
|
/frontend/ @palashgdev
|
||||||
/deploy/ @prashant-shahi
|
/deploy/ @prashant-shahi
|
||||||
**/query-service/ @srikanthccv
|
**/query-service/ @srikanthccv
|
||||||
|
9
.github/workflows/commitlint.yml
vendored
9
.github/workflows/commitlint.yml
vendored
@ -7,12 +7,7 @@ jobs:
|
|||||||
lint-commits:
|
lint-commits:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.3.1
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
# we actually need "github.event.pull_request.commits + 1" commit
|
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: actions/setup-node@v2.1.0
|
- uses: wagoid/commitlint-github-action@v5
|
||||||
# or just "yarn" if you depend on "@commitlint/cli" already
|
|
||||||
- run: yarn add @commitlint/cli
|
|
||||||
- run: yarn add @commitlint/config-conventional
|
|
||||||
- run: yarn run commitlint --config ./node_modules/@commitlint/config-conventional/index.js --from HEAD~${{ github.event.pull_request.commits }} --to HEAD
|
|
||||||
|
4
.github/workflows/pr_verify_linked_issue.yml
vendored
4
.github/workflows/pr_verify_linked_issue.yml
vendored
@ -5,7 +5,7 @@ name: VerifyIssue
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [edited, synchronize, opened, reopened]
|
types: [edited, opened]
|
||||||
check_run:
|
check_run:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@ -14,6 +14,6 @@ jobs:
|
|||||||
name: Ensure Pull Request has a linked issue.
|
name: Ensure Pull Request has a linked issue.
|
||||||
steps:
|
steps:
|
||||||
- name: Verify Linked Issue
|
- name: Verify Linked Issue
|
||||||
uses: hattan/verify-linked-issue-action@v1.1.0
|
uses: srikanthccv/verify-linked-issue-action@v0.70
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,7 +1,5 @@
|
|||||||
|
|
||||||
node_modules
|
node_modules
|
||||||
yarn.lock
|
|
||||||
package.json
|
|
||||||
|
|
||||||
deploy/docker/environment_tiny/common_test
|
deploy/docker/environment_tiny/common_test
|
||||||
frontend/node_modules
|
frontend/node_modules
|
||||||
|
@ -85,9 +85,9 @@ Hier findest du die vollständige Liste von unterstützten Programmiersprachen -
|
|||||||
|
|
||||||
### Bereitstellung mit Docker
|
### Bereitstellung mit Docker
|
||||||
|
|
||||||
Bitte folge den [hier](https://signoz.io/docs/deployment/docker/) aufgelisteten Schritten um deine Anwendung mit Docker bereitzustellen.
|
Bitte folge den [hier](https://signoz.io/docs/install/docker/) aufgelisteten Schritten um deine Anwendung mit Docker bereitzustellen.
|
||||||
|
|
||||||
Die [Anleitungen zur Fehlerbehebung](https://signoz.io/docs/deployment/troubleshooting) könnten hilfreich sein, falls du auf irgendwelche Schwierigkeiten stößt.
|
Die [Anleitungen zur Fehlerbehebung](https://signoz.io/docs/install/troubleshooting/) könnten hilfreich sein, falls du auf irgendwelche Schwierigkeiten stößt.
|
||||||
|
|
||||||
<p>  </p>
|
<p>  </p>
|
||||||
|
|
||||||
|
14
README.md
14
README.md
@ -70,7 +70,6 @@ SigNoz helps developers monitor applications and troubleshoot problems in their
|
|||||||
|
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
<img align="left" src="https://signoz-public.s3.us-east-2.amazonaws.com/Contributing.svg" width="50px" />
|
|
||||||
|
|
||||||
## Join our Slack community
|
## Join our Slack community
|
||||||
|
|
||||||
@ -78,7 +77,6 @@ Come say Hi to us on [Slack](https://signoz.io/slack) 👋
|
|||||||
|
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
<img align="left" src="https://signoz-public.s3.us-east-2.amazonaws.com/Features.svg" width="50px" />
|
|
||||||
|
|
||||||
## Features:
|
## Features:
|
||||||
|
|
||||||
@ -89,13 +87,12 @@ Come say Hi to us on [Slack](https://signoz.io/slack) 👋
|
|||||||
- Filter traces by service name, operation, latency, error, tags/annotations.
|
- Filter traces by service name, operation, latency, error, tags/annotations.
|
||||||
- Run aggregates on trace data (events/spans) to get business relevant metrics. e.g. You can get error rate and 99th percentile latency of `customer_type: gold` or `deployment_version: v2` or `external_call: paypal`
|
- Run aggregates on trace data (events/spans) to get business relevant metrics. e.g. You can get error rate and 99th percentile latency of `customer_type: gold` or `deployment_version: v2` or `external_call: paypal`
|
||||||
- Native support for OpenTelemetry Logs, advanced log query builder, and automatic log collection from k8s cluster
|
- Native support for OpenTelemetry Logs, advanced log query builder, and automatic log collection from k8s cluster
|
||||||
- Lightening quick log analytics ([Logs Perf. Benchmark](https://signoz.io/blog/logs-performance-benchmark/))
|
- Lightning quick log analytics ([Logs Perf. Benchmark](https://signoz.io/blog/logs-performance-benchmark/))
|
||||||
- End-to-End visibility into infrastructure performance, ingest metrics from all kinds of host environments
|
- End-to-End visibility into infrastructure performance, ingest metrics from all kinds of host environments
|
||||||
- Easy to set alerts with DIY query builder
|
- Easy to set alerts with DIY query builder
|
||||||
|
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
<img align="left" src="https://signoz-public.s3.us-east-2.amazonaws.com/WhatsCool.svg" width="50px" />
|
|
||||||
|
|
||||||
## Why SigNoz?
|
## Why SigNoz?
|
||||||
|
|
||||||
@ -124,15 +121,14 @@ You can find the complete list of languages here - https://opentelemetry.io/docs
|
|||||||
|
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
<img align="left" src="https://signoz-public.s3.us-east-2.amazonaws.com/Philosophy.svg" width="50px" />
|
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
### Deploy using Docker
|
### Deploy using Docker
|
||||||
|
|
||||||
Please follow the steps listed [here](https://signoz.io/docs/deployment/docker/) to install using docker
|
Please follow the steps listed [here](https://signoz.io/docs/install/docker/) to install using docker
|
||||||
|
|
||||||
The [troubleshooting instructions](https://signoz.io/docs/deployment/troubleshooting) may be helpful if you face any issues.
|
The [troubleshooting instructions](https://signoz.io/docs/install/troubleshooting/) may be helpful if you face any issues.
|
||||||
|
|
||||||
<p>  </p>
|
<p>  </p>
|
||||||
|
|
||||||
@ -143,7 +139,6 @@ Please follow the steps listed [here](https://signoz.io/docs/deployment/helm_cha
|
|||||||
|
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
<img align="left" src="https://signoz-public.s3.us-east-2.amazonaws.com/UseSigNoz.svg" width="50px" />
|
|
||||||
|
|
||||||
## Comparisons to Familiar Tools
|
## Comparisons to Familiar Tools
|
||||||
|
|
||||||
@ -185,7 +180,6 @@ We have published benchmarks comparing Loki with SigNoz. Check it out [here](htt
|
|||||||
|
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
<img align="left" src="https://signoz-public.s3.us-east-2.amazonaws.com/Contributors.svg" width="50px" />
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
@ -212,7 +206,6 @@ Not sure how to get started? Just ping us on `#contributing` in our [slack commu
|
|||||||
|
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
<img align="left" src="https://signoz-public.s3.us-east-2.amazonaws.com/DevelopingLocally.svg" width="50px" />
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
@ -220,7 +213,6 @@ You can find docs at https://signoz.io/docs/. If you need any clarification or f
|
|||||||
|
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
<img align="left" src="https://signoz-public.s3.us-east-2.amazonaws.com/Contributing.svg" width="50px" />
|
|
||||||
|
|
||||||
## Community
|
## Community
|
||||||
|
|
||||||
|
@ -84,9 +84,9 @@ Você pode encontrar a lista completa de linguagens aqui - https://opentelemetry
|
|||||||
|
|
||||||
### Implantar usando Docker
|
### Implantar usando Docker
|
||||||
|
|
||||||
Siga as etapas listadas [aqui](https://signoz.io/docs/deployment/docker/) para instalar usando o Docker.
|
Siga as etapas listadas [aqui](https://signoz.io/docs/install/docker/) para instalar usando o Docker.
|
||||||
|
|
||||||
Esse [guia para solução de problemas](https://signoz.io/docs/deployment/troubleshooting) pode ser útil se você enfrentar quaisquer problemas.
|
Esse [guia para solução de problemas](https://signoz.io/docs/install/troubleshooting/) pode ser útil se você enfrentar quaisquer problemas.
|
||||||
|
|
||||||
<p>  </p>
|
<p>  </p>
|
||||||
|
|
||||||
|
@ -80,9 +80,9 @@ SigNoz帮助开发人员监控应用并排查已部署应用中的问题。SigNo
|
|||||||
|
|
||||||
### 使用Docker部署
|
### 使用Docker部署
|
||||||
|
|
||||||
请按照[这里](https://signoz.io/docs/deployment/docker/)列出的步骤使用Docker来安装
|
请按照[这里](https://signoz.io/docs/install/docker/)列出的步骤使用Docker来安装
|
||||||
|
|
||||||
如果你遇到任何问题,这个[排查指南](https://signoz.io/docs/deployment/troubleshooting)会对你有帮助。
|
如果你遇到任何问题,这个[排查指南](https://signoz.io/docs/install/troubleshooting/)会对你有帮助。
|
||||||
|
|
||||||
<p>  </p>
|
<p>  </p>
|
||||||
|
|
||||||
|
@ -7,9 +7,21 @@
|
|||||||
</default>
|
</default>
|
||||||
<s3>
|
<s3>
|
||||||
<type>s3</type>
|
<type>s3</type>
|
||||||
<endpoint>https://BUCKET-NAME.s3.amazonaws.com/data/</endpoint>
|
<!-- For S3 cold storage,
|
||||||
|
if region is us-east-1, endpoint can be https://<bucket-name>.s3.amazonaws.com
|
||||||
|
if region is not us-east-1, endpoint should be https://<bucket-name>.s3-<region>.amazonaws.com
|
||||||
|
For GCS cold storage,
|
||||||
|
endpoint should be https://storage.googleapis.com/<bucket-name>/data/
|
||||||
|
-->
|
||||||
|
<endpoint>https://BUCKET-NAME.s3-REGION-NAME.amazonaws.com/data/</endpoint>
|
||||||
<access_key_id>ACCESS-KEY-ID</access_key_id>
|
<access_key_id>ACCESS-KEY-ID</access_key_id>
|
||||||
<secret_access_key>SECRET-ACCESS-KEY</secret_access_key>
|
<secret_access_key>SECRET-ACCESS-KEY</secret_access_key>
|
||||||
|
<!-- In case of S3, uncomment the below configuration in case you want to read
|
||||||
|
AWS credentials from the Environment variables if they exist. -->
|
||||||
|
<!-- <use_environment_credentials>true</use_environment_credentials> -->
|
||||||
|
<!-- In case of GCS, uncomment the below configuration, since GCS does
|
||||||
|
not support batch deletion and result in error messages in logs. -->
|
||||||
|
<!-- <support_batch_delete>false</support_batch_delete> -->
|
||||||
</s3>
|
</s3>
|
||||||
</disks>
|
</disks>
|
||||||
<policies>
|
<policies>
|
||||||
|
@ -34,7 +34,7 @@ x-clickhouse-depend: &clickhouse-depend
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
zookeeper-1:
|
zookeeper-1:
|
||||||
image: bitnami/zookeeper:3.7.0
|
image: bitnami/zookeeper:3.7.1
|
||||||
hostname: zookeeper-1
|
hostname: zookeeper-1
|
||||||
user: root
|
user: root
|
||||||
ports:
|
ports:
|
||||||
@ -124,7 +124,7 @@ services:
|
|||||||
# - ./data/clickhouse-3/:/var/lib/clickhouse/
|
# - ./data/clickhouse-3/:/var/lib/clickhouse/
|
||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
image: signoz/alertmanager:0.23.0-0.2
|
image: signoz/alertmanager:0.23.1
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
command:
|
command:
|
||||||
@ -137,7 +137,7 @@ services:
|
|||||||
condition: on-failure
|
condition: on-failure
|
||||||
|
|
||||||
query-service:
|
query-service:
|
||||||
image: signoz/query-service:0.18.3
|
image: signoz/query-service:0.19.0
|
||||||
command: ["-config=/root/config/prometheus.yml"]
|
command: ["-config=/root/config/prometheus.yml"]
|
||||||
# ports:
|
# ports:
|
||||||
# - "6060:6060" # pprof port
|
# - "6060:6060" # pprof port
|
||||||
@ -166,7 +166,7 @@ services:
|
|||||||
<<: *clickhouse-depend
|
<<: *clickhouse-depend
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:0.18.3
|
image: signoz/frontend:0.19.0
|
||||||
deploy:
|
deploy:
|
||||||
restart_policy:
|
restart_policy:
|
||||||
condition: on-failure
|
condition: on-failure
|
||||||
@ -179,8 +179,8 @@ services:
|
|||||||
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
otel-collector:
|
otel-collector:
|
||||||
image: signoz/signoz-otel-collector:0.66.7
|
image: signoz/signoz-otel-collector:0.76.1
|
||||||
command: ["--config=/etc/otel-collector-config.yaml"]
|
command: ["--config=/etc/otel-collector-config.yaml", "--feature-gates=-pkg.translator.prometheus.NormalizeName"]
|
||||||
user: root # required for reading docker container logs
|
user: root # required for reading docker container logs
|
||||||
volumes:
|
volumes:
|
||||||
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
||||||
@ -208,8 +208,8 @@ services:
|
|||||||
<<: *clickhouse-depend
|
<<: *clickhouse-depend
|
||||||
|
|
||||||
otel-collector-metrics:
|
otel-collector-metrics:
|
||||||
image: signoz/signoz-otel-collector:0.66.7
|
image: signoz/signoz-otel-collector:0.76.1
|
||||||
command: ["--config=/etc/otel-collector-metrics-config.yaml"]
|
command: ["--config=/etc/otel-collector-metrics-config.yaml", "--feature-gates=-pkg.translator.prometheus.NormalizeName"]
|
||||||
volumes:
|
volumes:
|
||||||
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
|
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
|
||||||
# ports:
|
# ports:
|
||||||
|
@ -75,7 +75,7 @@ processors:
|
|||||||
timeout: 10s
|
timeout: 10s
|
||||||
resourcedetection:
|
resourcedetection:
|
||||||
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
|
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
|
||||||
detectors: [env, system] # include ec2 for AWS, gce for GCP and azure for Azure.
|
detectors: [env, system] # include ec2 for AWS, gcp for GCP and azure for Azure.
|
||||||
timeout: 2s
|
timeout: 2s
|
||||||
signozspanmetrics/prometheus:
|
signozspanmetrics/prometheus:
|
||||||
metrics_exporter: prometheus
|
metrics_exporter: prometheus
|
||||||
|
@ -7,9 +7,21 @@
|
|||||||
</default>
|
</default>
|
||||||
<s3>
|
<s3>
|
||||||
<type>s3</type>
|
<type>s3</type>
|
||||||
<endpoint>https://BUCKET-NAME.s3.amazonaws.com/data/</endpoint>
|
<!-- For S3 cold storage,
|
||||||
|
if region is us-east-1, endpoint can be https://<bucket-name>.s3.amazonaws.com
|
||||||
|
if region is not us-east-1, endpoint should be https://<bucket-name>.s3-<region>.amazonaws.com
|
||||||
|
For GCS cold storage,
|
||||||
|
endpoint should be https://storage.googleapis.com/<bucket-name>/data/
|
||||||
|
-->
|
||||||
|
<endpoint>https://BUCKET-NAME.s3-REGION-NAME.amazonaws.com/data/</endpoint>
|
||||||
<access_key_id>ACCESS-KEY-ID</access_key_id>
|
<access_key_id>ACCESS-KEY-ID</access_key_id>
|
||||||
<secret_access_key>SECRET-ACCESS-KEY</secret_access_key>
|
<secret_access_key>SECRET-ACCESS-KEY</secret_access_key>
|
||||||
|
<!-- In case of S3, uncomment the below configuration in case you want to read
|
||||||
|
AWS credentials from the Environment variables if they exist. -->
|
||||||
|
<!-- <use_environment_credentials>true</use_environment_credentials> -->
|
||||||
|
<!-- In case of GCS, uncomment the below configuration, since GCS does
|
||||||
|
not support batch deletion and result in error messages in logs. -->
|
||||||
|
<!-- <support_batch_delete>false</support_batch_delete> -->
|
||||||
</s3>
|
</s3>
|
||||||
</disks>
|
</disks>
|
||||||
<policies>
|
<policies>
|
||||||
|
@ -27,7 +27,7 @@ services:
|
|||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
container_name: alertmanager
|
container_name: alertmanager
|
||||||
image: signoz/alertmanager:0.23.0-0.2
|
image: signoz/alertmanager:0.23.1
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
depends_on:
|
depends_on:
|
||||||
@ -41,8 +41,8 @@ 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`
|
||||||
otel-collector:
|
otel-collector:
|
||||||
container_name: otel-collector
|
container_name: otel-collector
|
||||||
image: signoz/signoz-otel-collector:0.66.7
|
image: signoz/signoz-otel-collector:0.76.1
|
||||||
command: ["--config=/etc/otel-collector-config.yaml"]
|
command: ["--config=/etc/otel-collector-config.yaml", "--feature-gates=-pkg.translator.prometheus.NormalizeName"]
|
||||||
# user: root # required for reading docker container logs
|
# user: root # required for reading docker container logs
|
||||||
volumes:
|
volumes:
|
||||||
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
||||||
@ -67,8 +67,8 @@ services:
|
|||||||
|
|
||||||
otel-collector-metrics:
|
otel-collector-metrics:
|
||||||
container_name: otel-collector-metrics
|
container_name: otel-collector-metrics
|
||||||
image: signoz/signoz-otel-collector:0.66.7
|
image: signoz/signoz-otel-collector:0.76.1
|
||||||
command: ["--config=/etc/otel-collector-metrics-config.yaml"]
|
command: ["--config=/etc/otel-collector-metrics-config.yaml", "--feature-gates=-pkg.translator.prometheus.NormalizeName"]
|
||||||
volumes:
|
volumes:
|
||||||
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
|
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
|
||||||
# ports:
|
# ports:
|
||||||
|
@ -34,9 +34,9 @@ x-clickhouse-depend: &clickhouse-depend
|
|||||||
# condition: service_healthy
|
# condition: service_healthy
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
|
||||||
zookeeper-1:
|
zookeeper-1:
|
||||||
image: bitnami/zookeeper:3.7.0
|
image: bitnami/zookeeper:3.7.1
|
||||||
container_name: zookeeper-1
|
container_name: zookeeper-1
|
||||||
hostname: zookeeper-1
|
hostname: zookeeper-1
|
||||||
user: root
|
user: root
|
||||||
@ -120,7 +120,7 @@ services:
|
|||||||
# - ./data/clickhouse-2/:/var/lib/clickhouse/
|
# - ./data/clickhouse-2/:/var/lib/clickhouse/
|
||||||
# - ./user_scripts:/var/lib/clickhouse/user_scripts/
|
# - ./user_scripts:/var/lib/clickhouse/user_scripts/
|
||||||
|
|
||||||
|
|
||||||
# clickhouse-3:
|
# clickhouse-3:
|
||||||
# <<: *clickhouse-defaults
|
# <<: *clickhouse-defaults
|
||||||
# container_name: clickhouse-3
|
# container_name: clickhouse-3
|
||||||
@ -139,7 +139,7 @@ services:
|
|||||||
# - ./user_scripts:/var/lib/clickhouse/user_scripts/
|
# - ./user_scripts:/var/lib/clickhouse/user_scripts/
|
||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.0-0.2}
|
image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.1}
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
depends_on:
|
depends_on:
|
||||||
@ -150,10 +150,10 @@ services:
|
|||||||
- --queryService.url=http://query-service:8085
|
- --queryService.url=http://query-service:8085
|
||||||
- --storage.path=/data
|
- --storage.path=/data
|
||||||
|
|
||||||
# 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:${DOCKER_TAG:-0.18.3}
|
image: signoz/query-service:${DOCKER_TAG:-0.19.0}
|
||||||
container_name: query-service
|
container_name: query-service
|
||||||
command: ["-config=/root/config/prometheus.yml"]
|
command: ["-config=/root/config/prometheus.yml"]
|
||||||
# ports:
|
# ports:
|
||||||
@ -181,7 +181,7 @@ services:
|
|||||||
<<: *clickhouse-depend
|
<<: *clickhouse-depend
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:${DOCKER_TAG:-0.18.3}
|
image: signoz/frontend:${DOCKER_TAG:-0.19.0}
|
||||||
container_name: frontend
|
container_name: frontend
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
depends_on:
|
depends_on:
|
||||||
@ -193,8 +193,8 @@ services:
|
|||||||
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
otel-collector:
|
otel-collector:
|
||||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.66.7}
|
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.76.1}
|
||||||
command: ["--config=/etc/otel-collector-config.yaml"]
|
command: ["--config=/etc/otel-collector-config.yaml", "--feature-gates=-pkg.translator.prometheus.NormalizeName"]
|
||||||
user: root # required for reading docker container logs
|
user: root # required for reading docker container logs
|
||||||
volumes:
|
volumes:
|
||||||
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
||||||
@ -219,8 +219,8 @@ services:
|
|||||||
<<: *clickhouse-depend
|
<<: *clickhouse-depend
|
||||||
|
|
||||||
otel-collector-metrics:
|
otel-collector-metrics:
|
||||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.66.7}
|
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.76.1}
|
||||||
command: ["--config=/etc/otel-collector-metrics-config.yaml"]
|
command: ["--config=/etc/otel-collector-metrics-config.yaml", "--feature-gates=-pkg.translator.prometheus.NormalizeName"]
|
||||||
volumes:
|
volumes:
|
||||||
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
|
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
|
||||||
# ports:
|
# ports:
|
||||||
|
@ -70,6 +70,40 @@ receivers:
|
|||||||
|
|
||||||
|
|
||||||
processors:
|
processors:
|
||||||
|
logstransform/internal:
|
||||||
|
operators:
|
||||||
|
- type: trace_parser
|
||||||
|
if: '"trace_id" in attributes or "span_id" in attributes'
|
||||||
|
trace_id:
|
||||||
|
parse_from: attributes.trace_id
|
||||||
|
span_id:
|
||||||
|
parse_from: attributes.span_id
|
||||||
|
output: remove_trace_id
|
||||||
|
- type: trace_parser
|
||||||
|
if: '"traceId" in attributes or "spanId" in attributes'
|
||||||
|
trace_id:
|
||||||
|
parse_from: attributes.traceId
|
||||||
|
span_id:
|
||||||
|
parse_from: attributes.spanId
|
||||||
|
output: remove_traceId
|
||||||
|
- id: remove_traceId
|
||||||
|
type: remove
|
||||||
|
if: '"traceId" in attributes'
|
||||||
|
field: attributes.traceId
|
||||||
|
output: remove_spanId
|
||||||
|
- id: remove_spanId
|
||||||
|
type: remove
|
||||||
|
if: '"spanId" in attributes'
|
||||||
|
field: attributes.spanId
|
||||||
|
- id: remove_trace_id
|
||||||
|
type: remove
|
||||||
|
if: '"trace_id" in attributes'
|
||||||
|
field: attributes.trace_id
|
||||||
|
output: remove_span_id
|
||||||
|
- id: remove_span_id
|
||||||
|
type: remove
|
||||||
|
if: '"span_id" in attributes'
|
||||||
|
field: attributes.span_id
|
||||||
batch:
|
batch:
|
||||||
send_batch_size: 10000
|
send_batch_size: 10000
|
||||||
send_batch_max_size: 11000
|
send_batch_max_size: 11000
|
||||||
@ -104,7 +138,7 @@ processors:
|
|||||||
# retry_on_failure: true
|
# retry_on_failure: true
|
||||||
resourcedetection:
|
resourcedetection:
|
||||||
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
|
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
|
||||||
detectors: [env, system] # include ec2 for AWS, gce for GCP and azure for Azure.
|
detectors: [env, system] # include ec2 for AWS, gcp for GCP and azure for Azure.
|
||||||
timeout: 2s
|
timeout: 2s
|
||||||
|
|
||||||
extensions:
|
extensions:
|
||||||
@ -172,5 +206,5 @@ service:
|
|||||||
exporters: [prometheus]
|
exporters: [prometheus]
|
||||||
logs:
|
logs:
|
||||||
receivers: [otlp, filelog/dockercontainers]
|
receivers: [otlp, filelog/dockercontainers]
|
||||||
processors: [batch]
|
processors: [logstransform/internal, batch]
|
||||||
exporters: [clickhouselogsexporter]
|
exporters: [clickhouselogsexporter]
|
@ -125,7 +125,7 @@ check_ports_occupied() {
|
|||||||
|
|
||||||
echo "+++++++++++ ERROR ++++++++++++++++++++++"
|
echo "+++++++++++ ERROR ++++++++++++++++++++++"
|
||||||
echo "SigNoz requires ports 3301 & 4317 to be open. Please shut down any other service(s) that may be running on these ports."
|
echo "SigNoz requires ports 3301 & 4317 to be open. Please shut down any other service(s) that may be running on these ports."
|
||||||
echo "You can run SigNoz on another port following this guide https://signoz.io/docs/deployment/docker#troubleshooting"
|
echo "You can run SigNoz on another port following this guide https://signoz.io/docs/install/troubleshooting/"
|
||||||
echo "++++++++++++++++++++++++++++++++++++++++"
|
echo "++++++++++++++++++++++++++++++++++++++++"
|
||||||
echo ""
|
echo ""
|
||||||
exit 1
|
exit 1
|
||||||
@ -249,7 +249,7 @@ bye() { # Prints a friendly good bye message and exits the script.
|
|||||||
echo ""
|
echo ""
|
||||||
echo -e "$sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml ps -a"
|
echo -e "$sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml ps -a"
|
||||||
|
|
||||||
# echo "Please read our troubleshooting guide https://signoz.io/docs/deployment/docker#troubleshooting"
|
echo "Please read our troubleshooting guide https://signoz.io/docs/install/troubleshooting/"
|
||||||
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 "++++++++++++++++++++++++++++++++++++++++"
|
||||||
|
|
||||||
@ -500,7 +500,7 @@ if [[ $status_code -ne 200 ]]; then
|
|||||||
|
|
||||||
echo -e "$sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml ps -a"
|
echo -e "$sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml ps -a"
|
||||||
|
|
||||||
echo "Please read our troubleshooting guide https://signoz.io/docs/deployment/docker/#troubleshooting-of-common-issues"
|
echo "Please read our troubleshooting guide https://signoz.io/docs/install/troubleshooting/"
|
||||||
echo "or reach us on SigNoz for support https://signoz.io/slack"
|
echo "or reach us on SigNoz for support https://signoz.io/slack"
|
||||||
echo "++++++++++++++++++++++++++++++++++++++++"
|
echo "++++++++++++++++++++++++++++++++++++++++"
|
||||||
|
|
||||||
|
@ -5,6 +5,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
|
func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
|
||||||
featureSet := ah.FF().GetFeatureFlags()
|
featureSet, err := ah.FF().GetFeatureFlags()
|
||||||
|
if err != nil {
|
||||||
|
ah.HandleError(w, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
ah.Respond(w, featureSet)
|
ah.Respond(w, featureSet)
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ import (
|
|||||||
"go.signoz.io/signoz/ee/query-service/app/db"
|
"go.signoz.io/signoz/ee/query-service/app/db"
|
||||||
"go.signoz.io/signoz/ee/query-service/dao"
|
"go.signoz.io/signoz/ee/query-service/dao"
|
||||||
"go.signoz.io/signoz/ee/query-service/interfaces"
|
"go.signoz.io/signoz/ee/query-service/interfaces"
|
||||||
|
baseInterface "go.signoz.io/signoz/pkg/query-service/interfaces"
|
||||||
|
|
||||||
licensepkg "go.signoz.io/signoz/ee/query-service/license"
|
licensepkg "go.signoz.io/signoz/ee/query-service/license"
|
||||||
"go.signoz.io/signoz/ee/query-service/usage"
|
"go.signoz.io/signoz/ee/query-service/usage"
|
||||||
|
|
||||||
@ -126,7 +128,8 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
|
|||||||
serverOptions.RuleRepoURL,
|
serverOptions.RuleRepoURL,
|
||||||
localDB,
|
localDB,
|
||||||
reader,
|
reader,
|
||||||
serverOptions.DisableRules)
|
serverOptions.DisableRules,
|
||||||
|
lm)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -402,7 +405,7 @@ func setTimeoutMiddleware(next http.Handler) http.Handler {
|
|||||||
// check if route is not excluded
|
// check if route is not excluded
|
||||||
url := r.URL.Path
|
url := r.URL.Path
|
||||||
if _, ok := baseconst.TimeoutExcludedRoutes[url]; !ok {
|
if _, ok := baseconst.TimeoutExcludedRoutes[url]; !ok {
|
||||||
ctx, cancel = context.WithTimeout(r.Context(), baseconst.ContextTimeout*time.Second)
|
ctx, cancel = context.WithTimeout(r.Context(), baseconst.ContextTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -544,7 +547,8 @@ func makeRulesManager(
|
|||||||
ruleRepoURL string,
|
ruleRepoURL string,
|
||||||
db *sqlx.DB,
|
db *sqlx.DB,
|
||||||
ch baseint.Reader,
|
ch baseint.Reader,
|
||||||
disableRules bool) (*rules.Manager, error) {
|
disableRules bool,
|
||||||
|
fm baseInterface.FeatureLookup) (*rules.Manager, error) {
|
||||||
|
|
||||||
// create engine
|
// create engine
|
||||||
pqle, err := pqle.FromConfigPath(promConfigPath)
|
pqle, err := pqle.FromConfigPath(promConfigPath)
|
||||||
@ -571,6 +575,7 @@ func makeRulesManager(
|
|||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
Logger: nil,
|
Logger: nil,
|
||||||
DisableRules: disableRules,
|
DisableRules: disableRules,
|
||||||
|
FeatureFlags: fm,
|
||||||
}
|
}
|
||||||
|
|
||||||
// create Manager
|
// create Manager
|
||||||
|
@ -2,6 +2,7 @@ package license
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -9,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"go.signoz.io/signoz/ee/query-service/license/sqlite"
|
"go.signoz.io/signoz/ee/query-service/license/sqlite"
|
||||||
"go.signoz.io/signoz/ee/query-service/model"
|
"go.signoz.io/signoz/ee/query-service/model"
|
||||||
|
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -125,3 +127,79 @@ func (r *Repo) UpdatePlanDetails(ctx context.Context,
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Repo) CreateFeature(req *basemodel.Feature) *basemodel.ApiError {
|
||||||
|
|
||||||
|
_, err := r.db.Exec(
|
||||||
|
`INSERT INTO feature_status (name, active, usage, usage_limit, route)
|
||||||
|
VALUES (?, ?, ?, ?, ?);`,
|
||||||
|
req.Name, req.Active, req.Usage, req.UsageLimit, req.Route)
|
||||||
|
if err != nil {
|
||||||
|
return &basemodel.ApiError{Typ: basemodel.ErrorInternal, Err: err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Repo) GetFeature(featureName string) (basemodel.Feature, error) {
|
||||||
|
|
||||||
|
var feature basemodel.Feature
|
||||||
|
|
||||||
|
err := r.db.Get(&feature,
|
||||||
|
`SELECT * FROM feature_status WHERE name = ?;`, featureName)
|
||||||
|
if err != nil {
|
||||||
|
return feature, err
|
||||||
|
}
|
||||||
|
if feature.Name == "" {
|
||||||
|
return feature, basemodel.ErrFeatureUnavailable{Key: featureName}
|
||||||
|
}
|
||||||
|
return feature, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Repo) GetAllFeatures() ([]basemodel.Feature, error) {
|
||||||
|
|
||||||
|
var feature []basemodel.Feature
|
||||||
|
|
||||||
|
err := r.db.Select(&feature,
|
||||||
|
`SELECT * FROM feature_status;`)
|
||||||
|
if err != nil {
|
||||||
|
return feature, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return feature, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Repo) UpdateFeature(req basemodel.Feature) error {
|
||||||
|
|
||||||
|
_, err := r.db.Exec(
|
||||||
|
`UPDATE feature_status SET active = ?, usage = ?, usage_limit = ?, route = ? WHERE name = ?;`,
|
||||||
|
req.Active, req.Usage, req.UsageLimit, req.Route, req.Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Repo) InitFeatures(req basemodel.FeatureSet) error {
|
||||||
|
// get a feature by name, if it doesn't exist, create it. If it does exist, update it.
|
||||||
|
for _, feature := range req {
|
||||||
|
currentFeature, err := r.GetFeature(feature.Name)
|
||||||
|
if err != nil && err == sql.ErrNoRows {
|
||||||
|
err := r.CreateFeature(&feature)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
feature.Usage = currentFeature.Usage
|
||||||
|
if feature.Usage >= feature.UsageLimit && feature.UsageLimit != -1 {
|
||||||
|
feature.Active = false
|
||||||
|
}
|
||||||
|
err = r.UpdateFeature(feature)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -96,6 +96,11 @@ func (lm *Manager) SetActive(l *model.License) {
|
|||||||
lm.activeFeatures = l.FeatureSet
|
lm.activeFeatures = l.FeatureSet
|
||||||
// set default features
|
// set default features
|
||||||
setDefaultFeatures(lm)
|
setDefaultFeatures(lm)
|
||||||
|
|
||||||
|
err := lm.InitFeatures(lm.activeFeatures)
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Panicf("Couldn't activate features: %v", err)
|
||||||
|
}
|
||||||
if !lm.validatorRunning {
|
if !lm.validatorRunning {
|
||||||
// we want to make sure only one validator runs,
|
// we want to make sure only one validator runs,
|
||||||
// we already have lock() so good to go
|
// we already have lock() so good to go
|
||||||
@ -106,9 +111,7 @@ func (lm *Manager) SetActive(l *model.License) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setDefaultFeatures(lm *Manager) {
|
func setDefaultFeatures(lm *Manager) {
|
||||||
for k, v := range baseconstants.DEFAULT_FEATURE_SET {
|
lm.activeFeatures = append(lm.activeFeatures, baseconstants.DEFAULT_FEATURE_SET...)
|
||||||
lm.activeFeatures[k] = v
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadActiveLicense loads the most recent active license
|
// LoadActiveLicense loads the most recent active license
|
||||||
@ -123,8 +126,13 @@ func (lm *Manager) LoadActiveLicense() error {
|
|||||||
} else {
|
} else {
|
||||||
zap.S().Info("No active license found, defaulting to basic plan")
|
zap.S().Info("No active license found, defaulting to basic plan")
|
||||||
// if no active license is found, we default to basic(free) plan with all default features
|
// if no active license is found, we default to basic(free) plan with all default features
|
||||||
lm.activeFeatures = basemodel.BasicPlan
|
lm.activeFeatures = model.BasicPlan
|
||||||
setDefaultFeatures(lm)
|
setDefaultFeatures(lm)
|
||||||
|
err := lm.InitFeatures(lm.activeFeatures)
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Error("Couldn't initialize features: ", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -291,18 +299,31 @@ func (lm *Manager) Activate(ctx context.Context, key string) (licenseResponse *m
|
|||||||
// CheckFeature will be internally used by backend routines
|
// CheckFeature will be internally used by backend routines
|
||||||
// for feature gating
|
// for feature gating
|
||||||
func (lm *Manager) CheckFeature(featureKey string) error {
|
func (lm *Manager) CheckFeature(featureKey string) error {
|
||||||
if value, ok := lm.activeFeatures[featureKey]; ok {
|
feature, err := lm.repo.GetFeature(featureKey)
|
||||||
if value {
|
if err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
return basemodel.ErrFeatureUnavailable{Key: featureKey}
|
if feature.Active {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return basemodel.ErrFeatureUnavailable{Key: featureKey}
|
return basemodel.ErrFeatureUnavailable{Key: featureKey}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFeatureFlags returns current active features
|
// GetFeatureFlags returns current active features
|
||||||
func (lm *Manager) GetFeatureFlags() basemodel.FeatureSet {
|
func (lm *Manager) GetFeatureFlags() (basemodel.FeatureSet, error) {
|
||||||
return lm.activeFeatures
|
return lm.repo.GetAllFeatures()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lm *Manager) InitFeatures(features basemodel.FeatureSet) error {
|
||||||
|
return lm.repo.InitFeatures(features)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lm *Manager) UpdateFeatureFlag(feature basemodel.Feature) error {
|
||||||
|
return lm.repo.UpdateFeature(feature)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lm *Manager) GetFeatureFlag(key string) (basemodel.Feature, error) {
|
||||||
|
return lm.repo.GetFeature(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRepo return the license repo
|
// GetRepo return the license repo
|
||||||
|
@ -2,6 +2,7 @@ package sqlite
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,5 +34,19 @@ func InitDB(db *sqlx.DB) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error in creating licenses table: %s", err.Error())
|
return fmt.Errorf("Error in creating licenses table: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table_schema = `CREATE TABLE IF NOT EXISTS feature_status (
|
||||||
|
name TEXT PRIMARY KEY,
|
||||||
|
active bool,
|
||||||
|
usage INTEGER DEFAULT 0,
|
||||||
|
usage_limit INTEGER DEFAULT 0,
|
||||||
|
route TEXT
|
||||||
|
);`
|
||||||
|
|
||||||
|
_, err = db.Exec(table_schema)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error in creating feature_status table: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -11,21 +11,143 @@ const Enterprise = "ENTERPRISE_PLAN"
|
|||||||
const DisableUpsell = "DISABLE_UPSELL"
|
const DisableUpsell = "DISABLE_UPSELL"
|
||||||
|
|
||||||
var BasicPlan = basemodel.FeatureSet{
|
var BasicPlan = basemodel.FeatureSet{
|
||||||
Basic: true,
|
basemodel.Feature{
|
||||||
SSO: false,
|
Name: SSO,
|
||||||
DisableUpsell: false,
|
Active: false,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.OSS,
|
||||||
|
Active: false,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: DisableUpsell,
|
||||||
|
Active: false,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.SmartTraceDetail,
|
||||||
|
Active: false,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.CustomMetricsFunction,
|
||||||
|
Active: false,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.QueryBuilderPanels,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: 5,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.QueryBuilderAlerts,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: 5,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProPlan = basemodel.FeatureSet{
|
var ProPlan = basemodel.FeatureSet{
|
||||||
Pro: true,
|
basemodel.Feature{
|
||||||
SSO: true,
|
Name: SSO,
|
||||||
basemodel.SmartTraceDetail: true,
|
Active: true,
|
||||||
basemodel.CustomMetricsFunction: true,
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.OSS,
|
||||||
|
Active: false,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.SmartTraceDetail,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.CustomMetricsFunction,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.QueryBuilderPanels,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.QueryBuilderAlerts,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var EnterprisePlan = basemodel.FeatureSet{
|
var EnterprisePlan = basemodel.FeatureSet{
|
||||||
Enterprise: true,
|
basemodel.Feature{
|
||||||
SSO: true,
|
Name: SSO,
|
||||||
basemodel.SmartTraceDetail: true,
|
Active: true,
|
||||||
basemodel.CustomMetricsFunction: true,
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.OSS,
|
||||||
|
Active: false,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.SmartTraceDetail,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.CustomMetricsFunction,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.QueryBuilderPanels,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.QueryBuilderAlerts,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"presets": [
|
"presets": [
|
||||||
"@babel/preset-env",
|
"@babel/preset-env",
|
||||||
"@babel/preset-react",
|
["@babel/preset-react", { "runtime": "automatic" }],
|
||||||
"@babel/preset-typescript"
|
"@babel/preset-typescript"
|
||||||
],
|
],
|
||||||
"plugins": [
|
"plugins": [
|
||||||
|
@ -16,6 +16,7 @@ module.exports = {
|
|||||||
'plugin:sonarjs/recommended',
|
'plugin:sonarjs/recommended',
|
||||||
'plugin:import/errors',
|
'plugin:import/errors',
|
||||||
'plugin:import/warnings',
|
'plugin:import/warnings',
|
||||||
|
'plugin:react/jsx-runtime',
|
||||||
],
|
],
|
||||||
parser: '@typescript-eslint/parser',
|
parser: '@typescript-eslint/parser',
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
|
@ -1 +1,2 @@
|
|||||||
network-timeout 600000
|
network-timeout 600000
|
||||||
|
save-prefix ""
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Builder stage
|
# Builder stage
|
||||||
FROM node:16.15.0-slim as builder
|
FROM node:16.15.0 as builder
|
||||||
|
|
||||||
# Add Maintainer Info
|
# Add Maintainer Info
|
||||||
LABEL maintainer="signoz"
|
LABEL maintainer="signoz"
|
||||||
@ -11,6 +11,8 @@ WORKDIR /frontend
|
|||||||
|
|
||||||
# Copy the package.json and .yarnrc files prior to install dependencies
|
# Copy the package.json and .yarnrc files prior to install dependencies
|
||||||
COPY package.json ./
|
COPY package.json ./
|
||||||
|
# Copy lock file
|
||||||
|
COPY yarn.lock ./
|
||||||
COPY .yarnrc ./
|
COPY .yarnrc ./
|
||||||
|
|
||||||
# Install the dependencies and make the folder
|
# Install the dependencies and make the folder
|
||||||
|
@ -1,5 +1,20 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
|
/* eslint-disable object-shorthand */
|
||||||
|
/* eslint-disable func-names */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds custom matchers from the react testing library to all tests
|
* Adds custom matchers from the react testing library to all tests
|
||||||
*/
|
*/
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import 'jest-styled-components';
|
import 'jest-styled-components';
|
||||||
|
|
||||||
|
// Mock window.matchMedia
|
||||||
|
window.matchMedia =
|
||||||
|
window.matchMedia ||
|
||||||
|
function (): any {
|
||||||
|
return {
|
||||||
|
matches: false,
|
||||||
|
addListener: function () {},
|
||||||
|
removeListener: function () {},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -48,15 +48,11 @@
|
|||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"css-loader": "4.3.0",
|
"css-loader": "4.3.0",
|
||||||
"css-minimizer-webpack-plugin": "^3.2.0",
|
"css-minimizer-webpack-plugin": "^3.2.0",
|
||||||
"d3": "^6.2.0",
|
|
||||||
"d3-flame-graph": "^3.1.1",
|
|
||||||
"d3-tip": "^0.9.1",
|
|
||||||
"dayjs": "^1.10.7",
|
"dayjs": "^1.10.7",
|
||||||
"dompurify": "3.0.0",
|
"dompurify": "3.0.0",
|
||||||
"dotenv": "8.2.0",
|
"dotenv": "8.2.0",
|
||||||
"event-source-polyfill": "1.0.31",
|
"event-source-polyfill": "1.0.31",
|
||||||
"file-loader": "6.1.1",
|
"file-loader": "6.1.1",
|
||||||
"flat": "^5.0.2",
|
|
||||||
"fontfaceobserver": "2.3.0",
|
"fontfaceobserver": "2.3.0",
|
||||||
"history": "4.10.1",
|
"history": "4.10.1",
|
||||||
"html-webpack-plugin": "5.1.0",
|
"html-webpack-plugin": "5.1.0",
|
||||||
@ -69,10 +65,10 @@
|
|||||||
"less-loader": "^10.2.0",
|
"less-loader": "^10.2.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mini-css-extract-plugin": "2.4.5",
|
"mini-css-extract-plugin": "2.4.5",
|
||||||
|
"papaparse": "5.4.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-force-graph": "^1.41.0",
|
"react-force-graph": "^1.41.0",
|
||||||
"react-graph-vis": "^1.0.5",
|
|
||||||
"react-grid-layout": "^1.3.4",
|
"react-grid-layout": "^1.3.4",
|
||||||
"react-i18next": "^11.16.1",
|
"react-i18next": "^11.16.1",
|
||||||
"react-intersection-observer": "9.4.1",
|
"react-intersection-observer": "9.4.1",
|
||||||
@ -126,16 +122,14 @@
|
|||||||
"@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",
|
||||||
"@types/d3": "^6.2.0",
|
|
||||||
"@types/d3-tip": "^3.5.5",
|
|
||||||
"@types/dompurify": "^2.4.0",
|
"@types/dompurify": "^2.4.0",
|
||||||
"@types/event-source-polyfill": "^1.0.0",
|
"@types/event-source-polyfill": "^1.0.0",
|
||||||
"@types/flat": "^5.0.2",
|
|
||||||
"@types/fontfaceobserver": "2.1.0",
|
"@types/fontfaceobserver": "2.1.0",
|
||||||
"@types/jest": "^27.5.1",
|
"@types/jest": "^27.5.1",
|
||||||
"@types/lodash-es": "^4.17.4",
|
"@types/lodash-es": "^4.17.4",
|
||||||
"@types/mini-css-extract-plugin": "^2.5.1",
|
"@types/mini-css-extract-plugin": "^2.5.1",
|
||||||
"@types/node": "^16.10.3",
|
"@types/node": "^16.10.3",
|
||||||
|
"@types/papaparse": "5.3.7",
|
||||||
"@types/react": "18.0.26",
|
"@types/react": "18.0.26",
|
||||||
"@types/react-dom": "18.0.10",
|
"@types/react-dom": "18.0.10",
|
||||||
"@types/react-grid-layout": "^1.1.2",
|
"@types/react-grid-layout": "^1.1.2",
|
||||||
@ -144,7 +138,6 @@
|
|||||||
"@types/react-router-dom": "^5.1.6",
|
"@types/react-router-dom": "^5.1.6",
|
||||||
"@types/styled-components": "^5.1.4",
|
"@types/styled-components": "^5.1.4",
|
||||||
"@types/uuid": "^8.3.1",
|
"@types/uuid": "^8.3.1",
|
||||||
"@types/vis": "^4.21.21",
|
|
||||||
"@types/webpack": "^5.28.0",
|
"@types/webpack": "^5.28.0",
|
||||||
"@types/webpack-dev-server": "^4.3.0",
|
"@types/webpack-dev-server": "^4.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.28.2",
|
"@typescript-eslint/eslint-plugin": "^4.28.2",
|
||||||
@ -174,7 +167,6 @@
|
|||||||
"is-ci": "^3.0.1",
|
"is-ci": "^3.0.1",
|
||||||
"jest-playwright-preset": "^1.7.0",
|
"jest-playwright-preset": "^1.7.0",
|
||||||
"jest-styled-components": "^7.0.8",
|
"jest-styled-components": "^7.0.8",
|
||||||
"less-plugin-npm-import": "^2.1.0",
|
|
||||||
"lint-staged": "^12.3.7",
|
"lint-staged": "^12.3.7",
|
||||||
"portfinder-sync": "^0.0.2",
|
"portfinder-sync": "^0.0.2",
|
||||||
"prettier": "2.2.1",
|
"prettier": "2.2.1",
|
||||||
|
@ -7,7 +7,7 @@ import { LOCALSTORAGE } from 'constants/localStorage';
|
|||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React, { useEffect, useMemo } from 'react';
|
import { ReactChild, useEffect, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { matchPath, Redirect, useLocation } from 'react-router-dom';
|
import { matchPath, Redirect, useLocation } from 'react-router-dom';
|
||||||
@ -161,7 +161,7 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface PrivateRouteProps {
|
interface PrivateRouteProps {
|
||||||
children: React.ReactChild;
|
children: ReactChild;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PrivateRoute;
|
export default PrivateRoute;
|
||||||
|
@ -7,7 +7,7 @@ import { NotificationProvider } from 'hooks/useNotifications';
|
|||||||
import { ResourceProvider } from 'hooks/useResourceAttribute';
|
import { ResourceProvider } from 'hooks/useResourceAttribute';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { QueryBuilderProvider } from 'providers/QueryBuilder';
|
import { QueryBuilderProvider } from 'providers/QueryBuilder';
|
||||||
import React, { Suspense } from 'react';
|
import { Suspense } from 'react';
|
||||||
import { Route, Router, Switch } from 'react-router-dom';
|
import { Route, Router, Switch } from 'react-router-dom';
|
||||||
|
|
||||||
import PrivateRoute from './Private';
|
import PrivateRoute from './Private';
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
import axios from 'api';
|
|
||||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
|
||||||
import { AxiosError } from 'axios';
|
|
||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
|
||||||
import { PayloadProps } from 'types/api/features/getFeatures';
|
|
||||||
|
|
||||||
const getFeaturesFlags = async (): Promise<
|
|
||||||
SuccessResponse<PayloadProps> | ErrorResponse
|
|
||||||
> => {
|
|
||||||
try {
|
|
||||||
const response = await axios.get(`/featureFlags`);
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
error: null,
|
|
||||||
message: response.data.status,
|
|
||||||
payload: response.data.data,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
return ErrorResponseHandler(error as AxiosError);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getFeaturesFlags;
|
|
@ -1,17 +1,17 @@
|
|||||||
import { ApiV2Instance as axios } from 'api';
|
import { ApiV3Instance as axios } from 'api';
|
||||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
import {
|
import {
|
||||||
MetricRangePayloadProps,
|
MetricRangePayloadV3,
|
||||||
MetricsRangeProps,
|
MetricsRangeProps,
|
||||||
} from 'types/api/metrics/getQueryRange';
|
} from 'types/api/metrics/getQueryRange';
|
||||||
|
|
||||||
export const getMetricsQueryRange = async (
|
export const getMetricsQueryRange = async (
|
||||||
props: MetricsRangeProps,
|
props: MetricsRangeProps,
|
||||||
): Promise<SuccessResponse<MetricRangePayloadProps> | ErrorResponse> => {
|
): Promise<SuccessResponse<MetricRangePayloadV3> | ErrorResponse> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(`/metrics/query_range`, props);
|
const response = await axios.post('/query_range', props);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
import { ApiV3Instance } from 'api';
|
import { ApiV3Instance } from 'api';
|
||||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
import { AxiosError, AxiosResponse } from 'axios';
|
import { AxiosError, AxiosResponse } from 'axios';
|
||||||
|
import createQueryParams from 'lib/createQueryParams';
|
||||||
// ** Helpers
|
// ** Helpers
|
||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
// ** Types
|
// ** Types
|
||||||
import { IGetAggregateAttributePayload } from 'types/api/queryBuilder/getAggregatorAttribute';
|
import { IGetAggregateAttributePayload } from 'types/api/queryBuilder/getAggregatorAttribute';
|
||||||
import { IQueryAutocompleteResponse } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import {
|
||||||
|
BaseAutocompleteData,
|
||||||
|
IQueryAutocompleteResponse,
|
||||||
|
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
export const getAggregateAttribute = async ({
|
export const getAggregateAttribute = async ({
|
||||||
aggregateOperator,
|
aggregateOperator,
|
||||||
@ -18,14 +23,22 @@ export const getAggregateAttribute = async ({
|
|||||||
const response: AxiosResponse<{
|
const response: AxiosResponse<{
|
||||||
data: IQueryAutocompleteResponse;
|
data: IQueryAutocompleteResponse;
|
||||||
}> = await ApiV3Instance.get(
|
}> = await ApiV3Instance.get(
|
||||||
`autocomplete/aggregate_attributes?aggregateOperator=${aggregateOperator}&dataSource=${dataSource}&searchText=${searchText}`,
|
`autocomplete/aggregate_attributes?${createQueryParams({
|
||||||
|
aggregateOperator,
|
||||||
|
searchText,
|
||||||
|
dataSource,
|
||||||
|
})}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const payload: BaseAutocompleteData[] =
|
||||||
|
response.data.data.attributeKeys?.map((item) => ({ ...item, id: uuid() })) ||
|
||||||
|
[];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
error: null,
|
error: null,
|
||||||
message: response.statusText,
|
message: response.statusText,
|
||||||
payload: response.data.data,
|
payload: { attributeKeys: payload },
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return ErrorResponseHandler(e as AxiosError);
|
return ErrorResponseHandler(e as AxiosError);
|
||||||
|
@ -1,16 +1,22 @@
|
|||||||
import { ApiV3Instance } from 'api';
|
import { ApiV3Instance } from 'api';
|
||||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
import { AxiosError, AxiosResponse } from 'axios';
|
import { AxiosError, AxiosResponse } from 'axios';
|
||||||
|
import createQueryParams from 'lib/createQueryParams';
|
||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
// ** Types
|
// ** Types
|
||||||
import { IGetAttributeKeysPayload } from 'types/api/queryBuilder/getAttributeKeys';
|
import { IGetAttributeKeysPayload } from 'types/api/queryBuilder/getAttributeKeys';
|
||||||
import { IQueryAutocompleteResponse } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import {
|
||||||
|
BaseAutocompleteData,
|
||||||
|
IQueryAutocompleteResponse,
|
||||||
|
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
export const getAggregateKeys = async ({
|
export const getAggregateKeys = async ({
|
||||||
aggregateOperator,
|
aggregateOperator,
|
||||||
searchText,
|
searchText,
|
||||||
dataSource,
|
dataSource,
|
||||||
aggregateAttribute,
|
aggregateAttribute,
|
||||||
|
tagType,
|
||||||
}: IGetAttributeKeysPayload): Promise<
|
}: IGetAttributeKeysPayload): Promise<
|
||||||
SuccessResponse<IQueryAutocompleteResponse> | ErrorResponse
|
SuccessResponse<IQueryAutocompleteResponse> | ErrorResponse
|
||||||
> => {
|
> => {
|
||||||
@ -18,14 +24,23 @@ export const getAggregateKeys = async ({
|
|||||||
const response: AxiosResponse<{
|
const response: AxiosResponse<{
|
||||||
data: IQueryAutocompleteResponse;
|
data: IQueryAutocompleteResponse;
|
||||||
}> = await ApiV3Instance.get(
|
}> = await ApiV3Instance.get(
|
||||||
`autocomplete/attribute_keys?aggregateOperator=${aggregateOperator}&dataSource=${dataSource}&aggregateAttribute=${aggregateAttribute}&searchText=${searchText}`,
|
`autocomplete/attribute_keys?${createQueryParams({
|
||||||
|
aggregateOperator,
|
||||||
|
searchText,
|
||||||
|
dataSource,
|
||||||
|
aggregateAttribute,
|
||||||
|
})}&tagType=${tagType}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const payload: BaseAutocompleteData[] =
|
||||||
|
response.data.data.attributeKeys?.map((item) => ({ ...item, id: uuid() })) ||
|
||||||
|
[];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
error: null,
|
error: null,
|
||||||
message: response.statusText,
|
message: response.statusText,
|
||||||
payload: response.data.data,
|
payload: { attributeKeys: payload },
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return ErrorResponseHandler(e as AxiosError);
|
return ErrorResponseHandler(e as AxiosError);
|
||||||
|
42
frontend/src/api/queryBuilder/getAttributesValues.ts
Normal file
42
frontend/src/api/queryBuilder/getAttributesValues.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { ApiV3Instance } from 'api';
|
||||||
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import createQueryParams from 'lib/createQueryParams';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
import {
|
||||||
|
IAttributeValuesResponse,
|
||||||
|
IGetAttributeValuesPayload,
|
||||||
|
} from 'types/api/queryBuilder/getAttributesValues';
|
||||||
|
|
||||||
|
export const getAttributesValues = async ({
|
||||||
|
aggregateOperator,
|
||||||
|
dataSource,
|
||||||
|
aggregateAttribute,
|
||||||
|
attributeKey,
|
||||||
|
filterAttributeKeyDataType,
|
||||||
|
tagType,
|
||||||
|
searchText,
|
||||||
|
}: IGetAttributeValuesPayload): Promise<
|
||||||
|
SuccessResponse<IAttributeValuesResponse> | ErrorResponse
|
||||||
|
> => {
|
||||||
|
try {
|
||||||
|
const response = await ApiV3Instance.get(
|
||||||
|
`/autocomplete/attribute_values?${createQueryParams({
|
||||||
|
aggregateOperator,
|
||||||
|
dataSource,
|
||||||
|
aggregateAttribute,
|
||||||
|
attributeKey,
|
||||||
|
searchText,
|
||||||
|
})}&filterAttributeKeyDataType=${filterAttributeKeyDataType}&tagType=${tagType}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: response.data.status,
|
||||||
|
payload: response.data.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler(error as AxiosError);
|
||||||
|
}
|
||||||
|
};
|
@ -1,5 +1,3 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
function TimeSeries(): JSX.Element {
|
function TimeSeries(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import { CSSProperties } from 'react';
|
||||||
|
|
||||||
function Value(props: ValueProps): JSX.Element {
|
function Value(props: ValueProps): JSX.Element {
|
||||||
const { fillColor } = props;
|
const { fillColor } = props;
|
||||||
@ -20,7 +20,7 @@ function Value(props: ValueProps): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ValueProps {
|
interface ValueProps {
|
||||||
fillColor: React.CSSProperties['color'];
|
fillColor: CSSProperties['color'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Value;
|
export default Value;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
function NotFound(): JSX.Element {
|
function NotFound(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
function SomethingWentWrong(): JSX.Element {
|
function SomethingWentWrong(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
function UnAuthorized(): JSX.Element {
|
function UnAuthorized(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
59
frontend/src/components/Editor/Editor.test.tsx
Normal file
59
frontend/src/components/Editor/Editor.test.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
|
|
||||||
|
import Editor from './index';
|
||||||
|
|
||||||
|
jest.mock('hooks/useDarkMode', () => ({
|
||||||
|
useIsDarkMode: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('Editor', () => {
|
||||||
|
it('renders correctly with default props', () => {
|
||||||
|
const { container } = render(<Editor value="" />);
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders correctly with custom props', () => {
|
||||||
|
const customProps = {
|
||||||
|
value: 'test',
|
||||||
|
language: 'javascript',
|
||||||
|
readOnly: true,
|
||||||
|
height: '50vh',
|
||||||
|
options: { minimap: { enabled: false } },
|
||||||
|
};
|
||||||
|
const { container } = render(
|
||||||
|
<Editor
|
||||||
|
value={customProps.value}
|
||||||
|
height={customProps.height}
|
||||||
|
language={customProps.language}
|
||||||
|
options={customProps.options}
|
||||||
|
readOnly={customProps.readOnly}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders with dark mode theme', () => {
|
||||||
|
(useIsDarkMode as jest.Mock).mockImplementation(() => true);
|
||||||
|
|
||||||
|
const { container } = render(<Editor value="dark mode text" />);
|
||||||
|
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders with light mode theme', () => {
|
||||||
|
(useIsDarkMode as jest.Mock).mockImplementation(() => false);
|
||||||
|
|
||||||
|
const { container } = render(<Editor value="light mode text" />);
|
||||||
|
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays "Loading..." message initially', () => {
|
||||||
|
const { rerender } = render(<Editor value="initial text" />);
|
||||||
|
|
||||||
|
expect(screen.getByText('Loading...')).toBeInTheDocument();
|
||||||
|
|
||||||
|
rerender(<Editor value="initial text" />);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,69 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Editor renders correctly with custom props 1`] = `
|
||||||
|
<div>
|
||||||
|
<section
|
||||||
|
style="display: flex; position: relative; text-align: initial; width: 100%; height: 50vh;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style="display: flex; height: 100%; width: 100%; justify-content: center; align-items: center;"
|
||||||
|
>
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style="width: 100%; display: none;"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Editor renders correctly with default props 1`] = `
|
||||||
|
<div>
|
||||||
|
<section
|
||||||
|
style="display: flex; position: relative; text-align: initial; width: 100%; height: 40vh;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style="display: flex; height: 100%; width: 100%; justify-content: center; align-items: center;"
|
||||||
|
>
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style="width: 100%; display: none;"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Editor renders with dark mode theme 1`] = `
|
||||||
|
<div>
|
||||||
|
<section
|
||||||
|
style="display: flex; position: relative; text-align: initial; width: 100%; height: 40vh;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style="display: flex; height: 100%; width: 100%; justify-content: center; align-items: center;"
|
||||||
|
>
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style="width: 100%; display: none;"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Editor renders with light mode theme 1`] = `
|
||||||
|
<div>
|
||||||
|
<section
|
||||||
|
style="display: flex; position: relative; text-align: initial; width: 100%; height: 40vh;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style="display: flex; height: 100%; width: 100%; justify-content: center; align-items: center;"
|
||||||
|
>
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style="width: 100%; display: none;"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
`;
|
@ -1,6 +1,6 @@
|
|||||||
import MEditor, { EditorProps } from '@monaco-editor/react';
|
import MEditor, { EditorProps } from '@monaco-editor/react';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import React, { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
function Editor({
|
function Editor({
|
||||||
value,
|
value,
|
||||||
@ -13,6 +13,8 @@ function Editor({
|
|||||||
const isDarkMode = useIsDarkMode();
|
const isDarkMode = useIsDarkMode();
|
||||||
|
|
||||||
const onChangeHandler = (newValue?: string): void => {
|
const onChangeHandler = (newValue?: string): void => {
|
||||||
|
if (readOnly) return;
|
||||||
|
|
||||||
if (typeof newValue === 'string' && onChange) onChange(newValue);
|
if (typeof newValue === 'string' && onChange) onChange(newValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -29,6 +31,7 @@ function Editor({
|
|||||||
options={editorOptions}
|
options={editorOptions}
|
||||||
height={height}
|
height={height}
|
||||||
onChange={onChangeHandler}
|
onChange={onChangeHandler}
|
||||||
|
data-testid="monaco-editor"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ import annotationPlugin from 'chartjs-plugin-annotation';
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import isEqual from 'lodash-es/isEqual';
|
import isEqual from 'lodash-es/isEqual';
|
||||||
import React, { memo, useCallback, useEffect, useRef } from 'react';
|
import { memo, useCallback, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
import { hasData } from './hasData';
|
import { hasData } from './hasData';
|
||||||
import { getAxisLabelColor } from './helpers';
|
import { getAxisLabelColor } from './helpers';
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
import { Form, Input, InputProps, InputRef } from 'antd';
|
import { Form, Input, InputProps, InputRef } from 'antd';
|
||||||
import React from 'react';
|
import {
|
||||||
|
ChangeEventHandler,
|
||||||
|
FocusEventHandler,
|
||||||
|
KeyboardEventHandler,
|
||||||
|
LegacyRef,
|
||||||
|
ReactNode,
|
||||||
|
Ref,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
function InputComponent({
|
function InputComponent({
|
||||||
value,
|
value,
|
||||||
@ -22,7 +29,7 @@ function InputComponent({
|
|||||||
type={type}
|
type={type}
|
||||||
onChange={onChangeHandler}
|
onChange={onChangeHandler}
|
||||||
value={value}
|
value={value}
|
||||||
ref={ref as React.Ref<InputRef>}
|
ref={ref as Ref<InputRef>}
|
||||||
size={size}
|
size={size}
|
||||||
addonBefore={addonBefore}
|
addonBefore={addonBefore}
|
||||||
onBlur={onBlurHandler}
|
onBlur={onBlurHandler}
|
||||||
@ -37,15 +44,15 @@ function InputComponent({
|
|||||||
interface InputComponentProps extends InputProps {
|
interface InputComponentProps extends InputProps {
|
||||||
value: InputProps['value'];
|
value: InputProps['value'];
|
||||||
type?: InputProps['type'];
|
type?: InputProps['type'];
|
||||||
onChangeHandler?: React.ChangeEventHandler<HTMLInputElement>;
|
onChangeHandler?: ChangeEventHandler<HTMLInputElement>;
|
||||||
placeholder?: InputProps['placeholder'];
|
placeholder?: InputProps['placeholder'];
|
||||||
ref?: React.LegacyRef<InputRef>;
|
ref?: LegacyRef<InputRef>;
|
||||||
size?: InputProps['size'];
|
size?: InputProps['size'];
|
||||||
onBlurHandler?: React.FocusEventHandler<HTMLInputElement>;
|
onBlurHandler?: FocusEventHandler<HTMLInputElement>;
|
||||||
onPressEnterHandler?: React.KeyboardEventHandler<HTMLInputElement>;
|
onPressEnterHandler?: KeyboardEventHandler<HTMLInputElement>;
|
||||||
label?: string;
|
label?: string;
|
||||||
labelOnTop?: boolean;
|
labelOnTop?: boolean;
|
||||||
addonBefore?: React.ReactNode;
|
addonBefore?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
InputComponent.defaultProps = {
|
InputComponent.defaultProps = {
|
||||||
|
49
frontend/src/components/Loadable/Loadable.test.tsx
Normal file
49
frontend/src/components/Loadable/Loadable.test.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import {
|
||||||
|
render,
|
||||||
|
screen,
|
||||||
|
waitForElementToBeRemoved,
|
||||||
|
} from '@testing-library/react';
|
||||||
|
import React, { ComponentType, Suspense } from 'react';
|
||||||
|
|
||||||
|
import Loadable from './index';
|
||||||
|
|
||||||
|
// Sample component to be loaded lazily
|
||||||
|
function SampleComponent(): JSX.Element {
|
||||||
|
return <div>Sample Component</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadSampleComponent = (): Promise<{
|
||||||
|
default: ComponentType;
|
||||||
|
}> =>
|
||||||
|
new Promise<{ default: ComponentType }>((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve({ default: SampleComponent });
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Loadable', () => {
|
||||||
|
it('should render the lazily loaded component', async () => {
|
||||||
|
const LoadableSampleComponent = Loadable(loadSampleComponent);
|
||||||
|
|
||||||
|
const { container } = render(
|
||||||
|
<Suspense fallback={<div>Loading...</div>}>
|
||||||
|
<LoadableSampleComponent />
|
||||||
|
</Suspense>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText('Loading...')).toBeInTheDocument();
|
||||||
|
await waitForElementToBeRemoved(() => screen.queryByText('Loading...'));
|
||||||
|
|
||||||
|
expect(container.querySelector('div')).toHaveTextContent('Sample Component');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call lazy with the provided import path', () => {
|
||||||
|
const reactLazySpy = jest.spyOn(React, 'lazy');
|
||||||
|
Loadable(loadSampleComponent);
|
||||||
|
|
||||||
|
expect(reactLazySpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(reactLazySpy).toHaveBeenCalledWith(expect.any(Function));
|
||||||
|
|
||||||
|
reactLazySpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
@ -1,8 +1,8 @@
|
|||||||
import { ComponentType, lazy } from 'react';
|
import { ComponentType, lazy, LazyExoticComponent } from 'react';
|
||||||
|
|
||||||
function Loadable(importPath: {
|
function Loadable(importPath: {
|
||||||
(): LoadableProps;
|
(): LoadableProps;
|
||||||
}): React.LazyExoticComponent<LazyComponent> {
|
}): LazyExoticComponent<LazyComponent> {
|
||||||
return lazy(() => importPath());
|
return lazy(() => importPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { Button, Popover } from 'antd';
|
import { Popover } from 'antd';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
|
import history from 'lib/history';
|
||||||
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
||||||
import React, { memo, useCallback, useMemo } from 'react';
|
import { memo, ReactNode, useCallback, useMemo } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { Dispatch } from 'redux';
|
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppActions from 'types/actions';
|
|
||||||
import { SET_SEARCH_QUERY_STRING } from 'types/actions/logs';
|
|
||||||
import { ILogsReducer } from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
|
import { ButtonContainer } from './styles';
|
||||||
|
|
||||||
function AddToQueryHOC({
|
function AddToQueryHOC({
|
||||||
fieldKey,
|
fieldKey,
|
||||||
fieldValue,
|
fieldValue,
|
||||||
@ -16,7 +17,6 @@ function AddToQueryHOC({
|
|||||||
const {
|
const {
|
||||||
searchFilter: { queryString },
|
searchFilter: { queryString },
|
||||||
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
||||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
|
||||||
|
|
||||||
const generatedQuery = useMemo(
|
const generatedQuery = useMemo(
|
||||||
() => generateFilterQuery({ fieldKey, fieldValue, type: 'IN' }),
|
() => generateFilterQuery({ fieldKey, fieldValue, type: 'IN' }),
|
||||||
@ -31,31 +31,27 @@ function AddToQueryHOC({
|
|||||||
} else {
|
} else {
|
||||||
updatedQueryString += ` AND ${generatedQuery}`;
|
updatedQueryString += ` AND ${generatedQuery}`;
|
||||||
}
|
}
|
||||||
dispatch({
|
|
||||||
type: SET_SEARCH_QUERY_STRING,
|
history.replace(`${ROUTES.LOGS}?q=${updatedQueryString}`);
|
||||||
payload: {
|
}, [generatedQuery, queryString]);
|
||||||
searchQueryString: updatedQueryString,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}, [dispatch, generatedQuery, queryString]);
|
|
||||||
|
|
||||||
const popOverContent = useMemo(() => <span>Add to query: {fieldKey}</span>, [
|
const popOverContent = useMemo(() => <span>Add to query: {fieldKey}</span>, [
|
||||||
fieldKey,
|
fieldKey,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button size="small" type="text" onClick={handleQueryAdd}>
|
<ButtonContainer size="small" type="text" onClick={handleQueryAdd}>
|
||||||
<Popover placement="top" content={popOverContent}>
|
<Popover placement="top" content={popOverContent}>
|
||||||
{children}
|
{children}
|
||||||
</Popover>
|
</Popover>
|
||||||
</Button>
|
</ButtonContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AddToQueryHOCProps {
|
interface AddToQueryHOCProps {
|
||||||
fieldKey: string;
|
fieldKey: string;
|
||||||
fieldValue: string;
|
fieldValue: string;
|
||||||
children: React.ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(AddToQueryHOC);
|
export default memo(AddToQueryHOC);
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
import { CategoryHeadingText } from './styles';
|
import { CategoryHeadingText } from './styles';
|
||||||
|
|
||||||
interface ICategoryHeadingProps {
|
interface ICategoryHeadingProps {
|
||||||
children: React.ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
function CategoryHeading({ children }: ICategoryHeadingProps): JSX.Element {
|
function CategoryHeading({ children }: ICategoryHeadingProps): JSX.Element {
|
||||||
return <CategoryHeadingText type="secondary">{children}</CategoryHeadingText>;
|
return <CategoryHeadingText type="secondary">{children}</CategoryHeadingText>;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Popover } from 'antd';
|
import { Popover } from 'antd';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import React, { useCallback, useEffect } from 'react';
|
import { ReactNode, useCallback, useEffect } from 'react';
|
||||||
import { useCopyToClipboard } from 'react-use';
|
import { useCopyToClipboard } from 'react-use';
|
||||||
|
|
||||||
function CopyClipboardHOC({
|
function CopyClipboardHOC({
|
||||||
@ -22,7 +22,7 @@ function CopyClipboardHOC({
|
|||||||
}, [setCopy, textToCopy]);
|
}, [setCopy, textToCopy]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span onClick={onClick} onKeyDown={onClick} role="button" tabIndex={0}>
|
<span onClick={onClick} role="presentation" tabIndex={-1}>
|
||||||
<Popover
|
<Popover
|
||||||
placement="top"
|
placement="top"
|
||||||
content={<span style={{ fontSize: '0.9rem' }}>Copy to clipboard</span>}
|
content={<span style={{ fontSize: '0.9rem' }}>Copy to clipboard</span>}
|
||||||
@ -35,7 +35,7 @@ function CopyClipboardHOC({
|
|||||||
|
|
||||||
interface CopyClipboardHOCProps {
|
interface CopyClipboardHOCProps {
|
||||||
textToCopy: string;
|
textToCopy: string;
|
||||||
children: React.ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CopyClipboardHOC;
|
export default CopyClipboardHOC;
|
||||||
|
@ -2,13 +2,12 @@ import { blue, grey, orange } from '@ant-design/colors';
|
|||||||
import { CopyFilled, ExpandAltOutlined } from '@ant-design/icons';
|
import { CopyFilled, ExpandAltOutlined } from '@ant-design/icons';
|
||||||
import Convert from 'ansi-to-html';
|
import Convert from 'ansi-to-html';
|
||||||
import { Button, Divider, Row, Typography } from 'antd';
|
import { Button, Divider, Row, Typography } from 'antd';
|
||||||
import { map } from 'd3';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import dompurify from 'dompurify';
|
import dompurify from 'dompurify';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
// utils
|
// utils
|
||||||
import { FlatLogData } from 'lib/logs/flatLogData';
|
import { FlatLogData } from 'lib/logs/flatLogData';
|
||||||
import React, { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { useCopyToClipboard } from 'react-use';
|
import { useCopyToClipboard } from 'react-use';
|
||||||
// interfaces
|
// interfaces
|
||||||
@ -127,7 +126,7 @@ function ListLogView({ logData }: ListLogViewProps): JSX.Element {
|
|||||||
</>
|
</>
|
||||||
</LogContainer>
|
</LogContainer>
|
||||||
<div>
|
<div>
|
||||||
{map(updatedSelecedFields, (field) =>
|
{updatedSelecedFields.map((field) =>
|
||||||
isValidLogField(flattenLogData[field.name] as never) ? (
|
isValidLogField(flattenLogData[field.name] as never) ? (
|
||||||
<LogSelectedField
|
<LogSelectedField
|
||||||
key={field.name}
|
key={field.name}
|
||||||
|
@ -1 +1,3 @@
|
|||||||
export const rawLineStyle: React.CSSProperties = {};
|
import { CSSProperties } from 'react';
|
||||||
|
|
||||||
|
export const rawLineStyle: CSSProperties = {};
|
||||||
|
@ -5,7 +5,7 @@ import dayjs from 'dayjs';
|
|||||||
import dompurify from 'dompurify';
|
import dompurify from 'dompurify';
|
||||||
// hooks
|
// hooks
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import React, { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
// interfaces
|
// interfaces
|
||||||
import { ILog } from 'types/api/logs/log';
|
import { ILog } from 'types/api/logs/log';
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { TableProps } from 'antd';
|
import { TableProps } from 'antd';
|
||||||
import React from 'react';
|
import { CSSProperties } from 'react';
|
||||||
|
|
||||||
export const defaultCellStyle: React.CSSProperties = {
|
export const defaultCellStyle: CSSProperties = {
|
||||||
paddingTop: 4,
|
paddingTop: 4,
|
||||||
paddingBottom: 6,
|
paddingBottom: 6,
|
||||||
paddingRight: 8,
|
paddingRight: 8,
|
||||||
paddingLeft: 8,
|
paddingLeft: 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultTableStyle: React.CSSProperties = {
|
export const defaultTableStyle: CSSProperties = {
|
||||||
minWidth: '40rem',
|
minWidth: '40rem',
|
||||||
maxWidth: '40rem',
|
maxWidth: '40rem',
|
||||||
};
|
};
|
||||||
|
@ -6,7 +6,7 @@ import dayjs from 'dayjs';
|
|||||||
import dompurify from 'dompurify';
|
import dompurify from 'dompurify';
|
||||||
// utils
|
// utils
|
||||||
import { FlatLogData } from 'lib/logs/flatLogData';
|
import { FlatLogData } from 'lib/logs/flatLogData';
|
||||||
import React, { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { IField } from 'types/api/logs/fields';
|
import { IField } from 'types/api/logs/fields';
|
||||||
// interfaces
|
// interfaces
|
||||||
import { ILog } from 'types/api/logs/log';
|
import { ILog } from 'types/api/logs/log';
|
||||||
|
@ -16,6 +16,4 @@ export const TableBodyContent = styled.div<TableBodyContentProps>`
|
|||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
|
|
||||||
line-height: 2rem;
|
line-height: 2rem;
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
`;
|
`;
|
||||||
|
8
frontend/src/components/Logs/styles.ts
Normal file
8
frontend/src/components/Logs/styles.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Button } from 'antd';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const ButtonContainer = styled(Button)`
|
||||||
|
&&& {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
`;
|
47
frontend/src/components/MessageTip/MessageTip.test.tsx
Normal file
47
frontend/src/components/MessageTip/MessageTip.test.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
|
||||||
|
import MessageTip from './index';
|
||||||
|
|
||||||
|
describe('MessageTip', () => {
|
||||||
|
it('should not render when show prop is false', () => {
|
||||||
|
render(
|
||||||
|
<MessageTip
|
||||||
|
show={false}
|
||||||
|
message="Test Message"
|
||||||
|
action={<button type="button">Close</button>}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const messageTip = screen.queryByRole('alert');
|
||||||
|
|
||||||
|
expect(messageTip).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render with the provided message and action', () => {
|
||||||
|
const message = 'Test Message';
|
||||||
|
const action = <button type="button">Close</button>;
|
||||||
|
|
||||||
|
render(<MessageTip show message={message} action={action} />);
|
||||||
|
|
||||||
|
const messageTip = screen.getByRole('alert');
|
||||||
|
const messageText = screen.getByText(message);
|
||||||
|
const actionButton = screen.getByRole('button', { name: 'Close' });
|
||||||
|
|
||||||
|
expect(messageTip).toBeInTheDocument();
|
||||||
|
expect(messageText).toBeInTheDocument();
|
||||||
|
expect(actionButton).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// taken from antd docs
|
||||||
|
// https://github.com/ant-design/ant-design/blob/master/components/alert/__tests__/index.test.tsx
|
||||||
|
it('custom action', () => {
|
||||||
|
const { container } = render(
|
||||||
|
<MessageTip
|
||||||
|
show
|
||||||
|
message="Success Tips"
|
||||||
|
action={<button type="button">Close</button>}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
expect(container.firstChild).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,54 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`MessageTip custom action 1`] = `
|
||||||
|
.c0 {
|
||||||
|
-webkit-align-items: center;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="ant-alert ant-alert-info ant-alert-with-description c0 css-dev-only-do-not-override-1i536d8"
|
||||||
|
data-show="true"
|
||||||
|
role="alert"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-label="info-circle"
|
||||||
|
class="anticon anticon-info-circle ant-alert-icon"
|
||||||
|
role="img"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
data-icon="info-circle"
|
||||||
|
fill="currentColor"
|
||||||
|
focusable="false"
|
||||||
|
height="1em"
|
||||||
|
viewBox="64 64 896 896"
|
||||||
|
width="1em"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm32 664c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V456c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272zm-32-344a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
class="ant-alert-content"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-alert-description"
|
||||||
|
>
|
||||||
|
Success Tips
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="ant-alert-action"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
@ -1,11 +1,11 @@
|
|||||||
import React from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
import { StyledAlert } from './styles';
|
import { StyledAlert } from './styles';
|
||||||
|
|
||||||
interface MessageTipProps {
|
interface MessageTipProps {
|
||||||
show?: boolean;
|
show?: boolean;
|
||||||
message: React.ReactNode | string;
|
message: ReactNode | string;
|
||||||
action: React.ReactNode | undefined;
|
action: ReactNode | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function MessageTip({
|
function MessageTip({
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Modal, ModalProps as Props } from 'antd';
|
import { Modal, ModalProps as Props } from 'antd';
|
||||||
import React, { ReactElement } from 'react';
|
import { ReactElement } from 'react';
|
||||||
|
|
||||||
function CustomModal({
|
function CustomModal({
|
||||||
title,
|
title,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import React from 'react';
|
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
|
@ -2,7 +2,7 @@ import getLocalStorageKey from 'api/browser/localstorage/get';
|
|||||||
import NotFoundImage from 'assets/NotFound';
|
import NotFoundImage from 'assets/NotFound';
|
||||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import React, { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Button, Space } from 'antd';
|
import { Button, Space } from 'antd';
|
||||||
import setFlags from 'api/user/setFlags';
|
import setFlags from 'api/user/setFlags';
|
||||||
import MessageTip from 'components/MessageTip';
|
import MessageTip from 'components/MessageTip';
|
||||||
import React, { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import ReleaseNoteProps from 'components/ReleaseNote/ReleaseNoteProps';
|
import ReleaseNoteProps from 'components/ReleaseNote/ReleaseNoteProps';
|
||||||
import ReleaseNote0120 from 'components/ReleaseNote/Releases/ReleaseNote0120';
|
import ReleaseNote0120 from 'components/ReleaseNote/Releases/ReleaseNote0120';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import React from 'react';
|
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { UserFlags } from 'types/api/user/setFlags';
|
import { UserFlags } from 'types/api/user/setFlags';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useMemo } from 'react';
|
import { SyntheticEvent, useMemo } from 'react';
|
||||||
import { Resizable, ResizeCallbackData } from 'react-resizable';
|
import { Resizable, ResizeCallbackData } from 'react-resizable';
|
||||||
|
|
||||||
import { enableUserSelectHack } from './config';
|
import { enableUserSelectHack } from './config';
|
||||||
@ -37,7 +37,7 @@ function ResizableHeader(props: ResizableHeaderProps): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ResizableHeaderProps {
|
interface ResizableHeaderProps {
|
||||||
onResize: (e: React.SyntheticEvent<Element>, data: ResizeCallbackData) => void;
|
onResize: (e: SyntheticEvent<Element>, data: ResizeCallbackData) => void;
|
||||||
width: number;
|
width: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Table } from 'antd';
|
import { Table } from 'antd';
|
||||||
import type { TableProps } from 'antd/es/table';
|
import type { TableProps } from 'antd/es/table';
|
||||||
import { ColumnsType } from 'antd/lib/table';
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
import React, { useCallback, useMemo, useState } from 'react';
|
import { SyntheticEvent, useCallback, useMemo, useState } from 'react';
|
||||||
import { ResizeCallbackData } from 'react-resizable';
|
import { ResizeCallbackData } from 'react-resizable';
|
||||||
|
|
||||||
import ResizableHeader from './ResizableHeader';
|
import ResizableHeader from './ResizableHeader';
|
||||||
@ -12,7 +12,7 @@ function ResizeTable({ columns, ...restprops }: TableProps<any>): JSX.Element {
|
|||||||
|
|
||||||
const handleResize = useCallback(
|
const handleResize = useCallback(
|
||||||
(index: number) => (
|
(index: number) => (
|
||||||
_e: React.SyntheticEvent<Element>,
|
_e: SyntheticEvent<Element>,
|
||||||
{ size }: ResizeCallbackData,
|
{ size }: ResizeCallbackData,
|
||||||
): void => {
|
): void => {
|
||||||
const newColumns = [...columnsData];
|
const newColumns = [...columnsData];
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { Tabs, TabsProps } from 'antd';
|
import { Tabs, TabsProps } from 'antd';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
function RouteTab({
|
function RouteTab({
|
||||||
routes,
|
routes,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { LoadingOutlined } from '@ant-design/icons';
|
import { LoadingOutlined } from '@ant-design/icons';
|
||||||
import { Spin, SpinProps } from 'antd';
|
import { Spin, SpinProps } from 'antd';
|
||||||
import React from 'react';
|
import { CSSProperties } from 'react';
|
||||||
|
|
||||||
import { SpinerStyle } from './styles';
|
import { SpinerStyle } from './styles';
|
||||||
|
|
||||||
@ -15,8 +15,8 @@ function Spinner({ size, tip, height, style }: SpinnerProps): JSX.Element {
|
|||||||
interface SpinnerProps {
|
interface SpinnerProps {
|
||||||
size?: SpinProps['size'];
|
size?: SpinProps['size'];
|
||||||
tip?: SpinProps['tip'];
|
tip?: SpinProps['tip'];
|
||||||
height?: React.CSSProperties['height'];
|
height?: CSSProperties['height'];
|
||||||
style?: React.CSSProperties;
|
style?: CSSProperties;
|
||||||
}
|
}
|
||||||
Spinner.defaultProps = {
|
Spinner.defaultProps = {
|
||||||
size: undefined,
|
size: undefined,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import { CSSProperties } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
height: React.CSSProperties['height'];
|
height: CSSProperties['height'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SpinerStyle = styled.div<Props>`
|
export const SpinerStyle = styled.div<Props>`
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import * as AntD from 'antd';
|
import * as AntD from 'antd';
|
||||||
import { TextProps } from 'antd/lib/typography/Text';
|
import { TextProps } from 'antd/lib/typography/Text';
|
||||||
import { TitleProps } from 'antd/lib/typography/Title';
|
import { TitleProps } from 'antd/lib/typography/Title';
|
||||||
import React from 'react';
|
import { HTMLAttributes } from 'react';
|
||||||
import styled, { FlattenSimpleInterpolation } from 'styled-components';
|
import styled, { FlattenSimpleInterpolation } from 'styled-components';
|
||||||
|
|
||||||
import { IStyledClass } from './types';
|
import { IStyledClass } from './types';
|
||||||
@ -51,7 +51,7 @@ const StyledTypographyTitle = styled(Title)<TStyledTypographyTitle>`
|
|||||||
${styledClass}
|
${styledClass}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type TStyledDiv = React.HTMLAttributes<HTMLDivElement> & IStyledClass;
|
type TStyledDiv = HTMLAttributes<HTMLDivElement> & IStyledClass;
|
||||||
const StyledDiv = styled.div<TStyledDiv>`
|
const StyledDiv = styled.div<TStyledDiv>`
|
||||||
${styledClass}
|
${styledClass}
|
||||||
`;
|
`;
|
||||||
|
53
frontend/src/components/TextToolTip/TextToolTip.test.tsx
Normal file
53
frontend/src/components/TextToolTip/TextToolTip.test.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { fireEvent, render, waitFor } from '@testing-library/react';
|
||||||
|
|
||||||
|
import TextToolTip from './index';
|
||||||
|
|
||||||
|
describe('TextToolTip', () => {
|
||||||
|
const tooltipText = 'Tooltip Text';
|
||||||
|
|
||||||
|
it('displays the tooltip when hovering over the icon', async () => {
|
||||||
|
const { getByRole, getByText } = render(<TextToolTip text="Tooltip Text" />);
|
||||||
|
const icon = getByRole('img');
|
||||||
|
fireEvent.mouseOver(icon);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
const tooltip = getByText(tooltipText);
|
||||||
|
expect(tooltip).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the tooltip content correctly', async () => {
|
||||||
|
const { getByRole, getByText } = render(
|
||||||
|
<TextToolTip text="Tooltip Text" url="https://example.com" />,
|
||||||
|
);
|
||||||
|
const icon = getByRole('img');
|
||||||
|
fireEvent.mouseOver(icon);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
const tooltip = getByText(tooltipText);
|
||||||
|
const link = getByText('here');
|
||||||
|
expect(tooltip).toBeInTheDocument();
|
||||||
|
expect(link).toHaveAttribute('href', 'https://example.com');
|
||||||
|
expect(link).toHaveAttribute('target', '_blank');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not display the tooltip by default', () => {
|
||||||
|
const { queryByText } = render(<TextToolTip text="Tooltip Text" />);
|
||||||
|
const tooltip = queryByText(tooltipText);
|
||||||
|
expect(tooltip).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('opens the URL in a new tab when clicked', async () => {
|
||||||
|
const { getByRole } = render(
|
||||||
|
<TextToolTip text="Tooltip Text" url="https://example.com" />,
|
||||||
|
);
|
||||||
|
const icon = getByRole('img');
|
||||||
|
fireEvent.mouseOver(icon);
|
||||||
|
await waitFor(() => {
|
||||||
|
const link = getByRole('link');
|
||||||
|
expect(link).toHaveAttribute('href', 'https://example.com');
|
||||||
|
expect(link).toHaveAttribute('target', '_blank');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -3,7 +3,7 @@ import { QuestionCircleFilled } from '@ant-design/icons';
|
|||||||
import { Tooltip } from 'antd';
|
import { Tooltip } from 'antd';
|
||||||
import { themeColors } from 'constants/theme';
|
import { themeColors } from 'constants/theme';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import React, { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { style } from './styles';
|
import { style } from './styles';
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import { timeItems } from 'container/NewWidget/RightContainer/timeItems';
|
import { timeItems } from 'container/NewWidget/RightContainer/timeItems';
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export const menuItems = timeItems.map((item) => ({
|
export const menuItems = timeItems.map((item) => ({
|
||||||
key: item.enum,
|
key: item.enum,
|
||||||
|
@ -3,7 +3,7 @@ import TimeItems, {
|
|||||||
timePreferance,
|
timePreferance,
|
||||||
timePreferenceType,
|
timePreferenceType,
|
||||||
} from 'container/NewWidget/RightContainer/timeItems';
|
} from 'container/NewWidget/RightContainer/timeItems';
|
||||||
import React, { useCallback, useMemo } from 'react';
|
import { Dispatch, SetStateAction, useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import { menuItems } from './config';
|
import { menuItems } from './config';
|
||||||
import { TextContainer } from './styles';
|
import { TextContainer } from './styles';
|
||||||
@ -44,7 +44,7 @@ interface TimeMenuItemOnChangeHandlerEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface TimePreferenceDropDownProps {
|
interface TimePreferenceDropDownProps {
|
||||||
setSelectedTime: React.Dispatch<React.SetStateAction<timePreferance>>;
|
setSelectedTime: Dispatch<SetStateAction<timePreferance>>;
|
||||||
selectedTime: timePreferance;
|
selectedTime: timePreferance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { Value } from './styles';
|
import { Value } from './styles';
|
||||||
|
|
||||||
function ValueGraph({ value }: ValueGraphProps): JSX.Element {
|
function ValueGraph({ value }: ValueGraphProps): JSX.Element {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Card, Space, Typography } from 'antd';
|
import { Card, Space, Typography } from 'antd';
|
||||||
import React from 'react';
|
import { ReactChild } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { Container, LeftContainer, Logo } from './styles';
|
import { Container, LeftContainer, Logo } from './styles';
|
||||||
@ -34,7 +34,7 @@ function WelcomeLeftContainer({
|
|||||||
|
|
||||||
interface WelcomeLeftContainerProps {
|
interface WelcomeLeftContainerProps {
|
||||||
version: string;
|
version: string;
|
||||||
children: React.ReactChild;
|
children: ReactChild;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default WelcomeLeftContainer;
|
export default WelcomeLeftContainer;
|
||||||
|
9
frontend/src/constants/alerts.ts
Normal file
9
frontend/src/constants/alerts.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||||
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
|
export const ALERTS_DATA_SOURCE_MAP: Record<AlertTypes, DataSource> = {
|
||||||
|
[AlertTypes.METRICS_BASED_ALERT]: DataSource.METRICS,
|
||||||
|
[AlertTypes.LOGS_BASED_ALERT]: DataSource.LOGS,
|
||||||
|
[AlertTypes.TRACES_BASED_ALERT]: DataSource.TRACES,
|
||||||
|
[AlertTypes.EXCEPTIONS_BASED_ALERT]: DataSource.TRACES,
|
||||||
|
};
|
@ -1,5 +1,3 @@
|
|||||||
import { EAggregateOperator, EReduceOperator } from 'types/common/dashboard';
|
|
||||||
|
|
||||||
export const PromQLQueryTemplate = {
|
export const PromQLQueryTemplate = {
|
||||||
query: '',
|
query: '',
|
||||||
legend: '',
|
legend: '',
|
||||||
@ -11,24 +9,3 @@ export const ClickHouseQueryTemplate = {
|
|||||||
legend: '',
|
legend: '',
|
||||||
disabled: false,
|
disabled: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const QueryBuilderQueryTemplate = {
|
|
||||||
metricName: null,
|
|
||||||
aggregateOperator: EAggregateOperator.NOOP,
|
|
||||||
tagFilters: {
|
|
||||||
op: 'AND',
|
|
||||||
items: [],
|
|
||||||
},
|
|
||||||
legend: '',
|
|
||||||
disabled: false,
|
|
||||||
// Specific to TIME_SERIES type graph
|
|
||||||
groupBy: [],
|
|
||||||
// Specific to VALUE type graph
|
|
||||||
reduceTo: EReduceOperator['Latest of values in timeframe'],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const QueryBuilderFormulaTemplate = {
|
|
||||||
expression: '',
|
|
||||||
disabled: false,
|
|
||||||
legend: '',
|
|
||||||
};
|
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
// keep this consistent with backend model>features.go
|
|
||||||
export enum FeatureKeys {
|
|
||||||
SSO = 'SSO',
|
|
||||||
ENTERPRISE_PLAN = 'ENTERPRISE_PLAN',
|
|
||||||
BASIC_PLAN = 'BASIC_PLAN',
|
|
||||||
DISABLE_UPSELL = 'DISABLE_UPSELL',
|
|
||||||
}
|
|
@ -1,6 +1,11 @@
|
|||||||
// keep this consistent with backend constants.go
|
// keep this consistent with backend constants.go
|
||||||
export enum FeatureKeys {
|
export enum FeatureKeys {
|
||||||
SSO = 'SSO',
|
SSO = 'SSO',
|
||||||
ENTERPRISE_PLAN = 'ENTERPRISE_PLAN',
|
DurationSort = 'DurationSort',
|
||||||
BASIC_PLAN = 'BASIC_PLAN',
|
TimestampSort = 'TimestampSort',
|
||||||
|
SMART_TRACE_DETAIL = 'SMART_TRACE_DETAIL',
|
||||||
|
CUSTOM_METRICS_FUNCTION = 'CUSTOM_METRICS_FUNCTION',
|
||||||
|
QUERY_BUILDER_PANELS = 'QUERY_BUILDER_PANELS',
|
||||||
|
QUERY_BUILDER_ALERTS = 'QUERY_BUILDER_ALERTS',
|
||||||
|
DISABLE_UPSELL = 'DISABLE_UPSELL',
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
export enum QueryParams {
|
||||||
export enum METRICS_PAGE_QUERY_PARAM {
|
|
||||||
interval = 'interval',
|
interval = 'interval',
|
||||||
startTime = 'startTime',
|
startTime = 'startTime',
|
||||||
endTime = 'endTime',
|
endTime = 'endTime',
|
||||||
@ -12,4 +11,5 @@ export enum METRICS_PAGE_QUERY_PARAM {
|
|||||||
selectedTags = 'selectedTags',
|
selectedTags = 'selectedTags',
|
||||||
aggregationOption = 'aggregationOption',
|
aggregationOption = 'aggregationOption',
|
||||||
entity = 'entity',
|
entity = 'entity',
|
||||||
|
resourceAttributes = 'resourceAttribute',
|
||||||
}
|
}
|
||||||
|
235
frontend/src/constants/queryBuilder.ts
Normal file
235
frontend/src/constants/queryBuilder.ts
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
// ** Helpers
|
||||||
|
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||||
|
import { createNewBuilderItemName } from 'lib/newQueryBuilder/createNewBuilderItemName';
|
||||||
|
import { LocalDataType } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
|
import {
|
||||||
|
HavingForm,
|
||||||
|
IBuilderFormula,
|
||||||
|
IBuilderQuery,
|
||||||
|
} from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
import {
|
||||||
|
BoolOperators,
|
||||||
|
DataSource,
|
||||||
|
MetricAggregateOperator,
|
||||||
|
NumberOperators,
|
||||||
|
PanelTypeKeys,
|
||||||
|
QueryAdditionalFilter,
|
||||||
|
ReduceOperators,
|
||||||
|
StringOperators,
|
||||||
|
} from 'types/common/queryBuilder';
|
||||||
|
import { SelectOption } from 'types/common/select';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
import {
|
||||||
|
logsAggregateOperatorOptions,
|
||||||
|
metricAggregateOperatorOptions,
|
||||||
|
tracesAggregateOperatorOptions,
|
||||||
|
} from './queryBuilderOperators';
|
||||||
|
|
||||||
|
export const MAX_FORMULAS = 20;
|
||||||
|
export const MAX_QUERIES = 26;
|
||||||
|
|
||||||
|
export const selectValueDivider = '--';
|
||||||
|
|
||||||
|
export const formulasNames: string[] = Array.from(
|
||||||
|
Array(MAX_FORMULAS),
|
||||||
|
(_, i) => `F${i + 1}`,
|
||||||
|
);
|
||||||
|
const alpha: number[] = Array.from(Array(MAX_QUERIES), (_, i) => i + 65);
|
||||||
|
export const alphabet: string[] = alpha.map((str) => String.fromCharCode(str));
|
||||||
|
|
||||||
|
export enum QueryBuilderKeys {
|
||||||
|
GET_AGGREGATE_ATTRIBUTE = 'GET_AGGREGATE_ATTRIBUTE',
|
||||||
|
GET_AGGREGATE_KEYS = 'GET_AGGREGATE_KEYS',
|
||||||
|
GET_ATTRIBUTE_KEY = 'GET_ATTRIBUTE_KEY',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mapOfOperators = {
|
||||||
|
metrics: metricAggregateOperatorOptions,
|
||||||
|
logs: logsAggregateOperatorOptions,
|
||||||
|
traces: tracesAggregateOperatorOptions,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mapOfFilters: Record<DataSource, QueryAdditionalFilter[]> = {
|
||||||
|
metrics: [
|
||||||
|
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||||
|
{ text: 'Aggregation interval', field: 'stepInterval' },
|
||||||
|
{ text: 'Having', field: 'having' },
|
||||||
|
],
|
||||||
|
logs: [
|
||||||
|
{ text: 'Order by', field: 'orderBy' },
|
||||||
|
{ text: 'Limit', field: 'limit' },
|
||||||
|
{ text: 'Having', field: 'having' },
|
||||||
|
{ text: 'Aggregation interval', field: 'stepInterval' },
|
||||||
|
],
|
||||||
|
traces: [
|
||||||
|
{ text: 'Order by', field: 'orderBy' },
|
||||||
|
{ text: 'Limit', field: 'limit' },
|
||||||
|
{ text: 'Having', field: 'having' },
|
||||||
|
{ text: 'Aggregation interval', field: 'stepInterval' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const REDUCE_TO_VALUES: SelectOption<ReduceOperators, string>[] = [
|
||||||
|
{ value: 'last', label: 'Latest of values in timeframe' },
|
||||||
|
{ value: 'sum', label: 'Sum of values in timeframe' },
|
||||||
|
{ value: 'avg', label: 'Average of values in timeframe' },
|
||||||
|
{ value: 'max', label: 'Max of values in timeframe' },
|
||||||
|
{ value: 'min', label: 'Min of values in timeframe' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const initialHavingValues: HavingForm = {
|
||||||
|
columnName: '',
|
||||||
|
op: '',
|
||||||
|
value: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initialAggregateAttribute: IBuilderQuery['aggregateAttribute'] = {
|
||||||
|
id: uuid(),
|
||||||
|
dataType: null,
|
||||||
|
key: '',
|
||||||
|
isColumn: null,
|
||||||
|
type: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initialQueryBuilderFormValues: IBuilderQuery = {
|
||||||
|
dataSource: DataSource.METRICS,
|
||||||
|
queryName: createNewBuilderItemName({ existNames: [], sourceNames: alphabet }),
|
||||||
|
aggregateOperator: MetricAggregateOperator.NOOP,
|
||||||
|
aggregateAttribute: initialAggregateAttribute,
|
||||||
|
filters: { items: [], op: 'AND' },
|
||||||
|
expression: createNewBuilderItemName({
|
||||||
|
existNames: [],
|
||||||
|
sourceNames: alphabet,
|
||||||
|
}),
|
||||||
|
disabled: false,
|
||||||
|
having: [],
|
||||||
|
stepInterval: 30,
|
||||||
|
limit: null,
|
||||||
|
orderBy: [],
|
||||||
|
groupBy: [],
|
||||||
|
legend: '',
|
||||||
|
reduceTo: 'sum',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initialFormulaBuilderFormValues: IBuilderFormula = {
|
||||||
|
queryName: createNewBuilderItemName({
|
||||||
|
existNames: [],
|
||||||
|
sourceNames: formulasNames,
|
||||||
|
}),
|
||||||
|
expression: '',
|
||||||
|
disabled: false,
|
||||||
|
legend: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const operatorsByTypes: Record<LocalDataType, string[]> = {
|
||||||
|
string: Object.values(StringOperators),
|
||||||
|
number: Object.values(NumberOperators),
|
||||||
|
bool: Object.values(BoolOperators),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PANEL_TYPES: Record<PanelTypeKeys, GRAPH_TYPES> = {
|
||||||
|
TIME_SERIES: 'graph',
|
||||||
|
VALUE: 'value',
|
||||||
|
TABLE: 'table',
|
||||||
|
LIST: 'list',
|
||||||
|
EMPTY_WIDGET: 'EMPTY_WIDGET',
|
||||||
|
};
|
||||||
|
|
||||||
|
export type IQueryBuilderState = 'search';
|
||||||
|
|
||||||
|
export const QUERY_BUILDER_SEARCH_VALUES = {
|
||||||
|
MULTIPLY: 'MULTIPLY_VALUE',
|
||||||
|
SINGLE: 'SINGLE_VALUE',
|
||||||
|
NON: 'NON_VALUE',
|
||||||
|
NOT_VALID: 'NOT_VALID',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OPERATORS = {
|
||||||
|
IN: 'IN',
|
||||||
|
NIN: 'NOT_IN',
|
||||||
|
LIKE: 'LIKE',
|
||||||
|
NLIKE: 'NOT_LIKE',
|
||||||
|
'=': '=',
|
||||||
|
'!=': '!=',
|
||||||
|
EXISTS: 'EXISTS',
|
||||||
|
NOT_EXISTS: 'NOT_EXISTS',
|
||||||
|
CONTAINS: 'CONTAINS',
|
||||||
|
NOT_CONTAINS: 'NOT_CONTAINS',
|
||||||
|
'>=': '>=',
|
||||||
|
'>': '>',
|
||||||
|
'<=': '<=',
|
||||||
|
'<': '<',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const QUERY_BUILDER_OPERATORS_BY_TYPES = {
|
||||||
|
string: [
|
||||||
|
OPERATORS['='],
|
||||||
|
OPERATORS['!='],
|
||||||
|
OPERATORS.IN,
|
||||||
|
OPERATORS.NIN,
|
||||||
|
OPERATORS.LIKE,
|
||||||
|
OPERATORS.NLIKE,
|
||||||
|
OPERATORS.CONTAINS,
|
||||||
|
OPERATORS.NOT_CONTAINS,
|
||||||
|
OPERATORS.EXISTS,
|
||||||
|
OPERATORS.NOT_EXISTS,
|
||||||
|
],
|
||||||
|
int64: [
|
||||||
|
OPERATORS['='],
|
||||||
|
OPERATORS['!='],
|
||||||
|
OPERATORS.IN,
|
||||||
|
OPERATORS.NIN,
|
||||||
|
OPERATORS.EXISTS,
|
||||||
|
OPERATORS.NOT_EXISTS,
|
||||||
|
OPERATORS['>='],
|
||||||
|
OPERATORS['>'],
|
||||||
|
OPERATORS['<='],
|
||||||
|
OPERATORS['<'],
|
||||||
|
],
|
||||||
|
float64: [
|
||||||
|
OPERATORS['='],
|
||||||
|
OPERATORS['!='],
|
||||||
|
OPERATORS.IN,
|
||||||
|
OPERATORS.NIN,
|
||||||
|
OPERATORS.EXISTS,
|
||||||
|
OPERATORS.NOT_EXISTS,
|
||||||
|
OPERATORS['>='],
|
||||||
|
OPERATORS['>'],
|
||||||
|
OPERATORS['<='],
|
||||||
|
OPERATORS['<'],
|
||||||
|
],
|
||||||
|
bool: [
|
||||||
|
OPERATORS['='],
|
||||||
|
OPERATORS['!='],
|
||||||
|
OPERATORS.EXISTS,
|
||||||
|
OPERATORS.NOT_EXISTS,
|
||||||
|
],
|
||||||
|
universal: [
|
||||||
|
OPERATORS['='],
|
||||||
|
OPERATORS['!='],
|
||||||
|
OPERATORS.IN,
|
||||||
|
OPERATORS.NIN,
|
||||||
|
OPERATORS.EXISTS,
|
||||||
|
OPERATORS.NOT_EXISTS,
|
||||||
|
OPERATORS.LIKE,
|
||||||
|
OPERATORS.NLIKE,
|
||||||
|
OPERATORS['>='],
|
||||||
|
OPERATORS['>'],
|
||||||
|
OPERATORS['<='],
|
||||||
|
OPERATORS['<'],
|
||||||
|
OPERATORS.CONTAINS,
|
||||||
|
OPERATORS.NOT_CONTAINS,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const HAVING_OPERATORS: string[] = [
|
||||||
|
OPERATORS['='],
|
||||||
|
OPERATORS['!='],
|
||||||
|
OPERATORS.IN,
|
||||||
|
OPERATORS.NIN,
|
||||||
|
OPERATORS['>='],
|
||||||
|
OPERATORS['>'],
|
||||||
|
OPERATORS['<='],
|
||||||
|
OPERATORS['<'],
|
||||||
|
];
|
304
frontend/src/constants/queryBuilderOperators.ts
Normal file
304
frontend/src/constants/queryBuilderOperators.ts
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
import {
|
||||||
|
LogsAggregatorOperator,
|
||||||
|
MetricAggregateOperator,
|
||||||
|
TracesAggregatorOperator,
|
||||||
|
} from 'types/common/queryBuilder';
|
||||||
|
import { SelectOption } from 'types/common/select';
|
||||||
|
|
||||||
|
export const metricAggregateOperatorOptions: SelectOption<string, string>[] = [
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.NOOP,
|
||||||
|
label: 'NOOP',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.COUNT,
|
||||||
|
label: 'Count',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.COUNT_DISTINCT,
|
||||||
|
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||||
|
label: 'Count Distinct',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.SUM,
|
||||||
|
label: 'Sum',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.AVG,
|
||||||
|
label: 'Avg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.MAX,
|
||||||
|
label: 'Max',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.MIN,
|
||||||
|
label: 'Min',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.P05,
|
||||||
|
label: 'P05',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.P10,
|
||||||
|
label: 'P10',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.P20,
|
||||||
|
label: 'P20',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.P25,
|
||||||
|
label: 'P25',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.P50,
|
||||||
|
label: 'P50',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.P75,
|
||||||
|
label: 'P75',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.P90,
|
||||||
|
label: 'P90',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.P95,
|
||||||
|
label: 'P95',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.P99,
|
||||||
|
label: 'P99',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.RATE,
|
||||||
|
label: 'Rate',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.SUM_RATE,
|
||||||
|
label: 'Sum_rate',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.AVG_RATE,
|
||||||
|
label: 'Avg_rate',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.MAX_RATE,
|
||||||
|
label: 'Max_rate',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.MIN_RATE,
|
||||||
|
label: 'Min_rate',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.RATE_SUM,
|
||||||
|
label: 'Rate_sum',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.RATE_AVG,
|
||||||
|
label: 'Rate_avg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.RATE_MIN,
|
||||||
|
label: 'Rate_min',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.RATE_MAX,
|
||||||
|
label: 'Rate_max',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.HIST_QUANTILE_50,
|
||||||
|
label: 'Hist_quantile_50',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.HIST_QUANTILE_75,
|
||||||
|
label: 'Hist_quantile_75',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.HIST_QUANTILE_90,
|
||||||
|
label: 'Hist_quantile_90',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.HIST_QUANTILE_95,
|
||||||
|
label: 'Hist_quantile_95',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MetricAggregateOperator.HIST_QUANTILE_99,
|
||||||
|
label: 'Hist_quantile_99',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const tracesAggregateOperatorOptions: SelectOption<string, string>[] = [
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.NOOP,
|
||||||
|
label: 'NOOP',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.COUNT,
|
||||||
|
label: 'Count',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.COUNT_DISTINCT,
|
||||||
|
label: 'Count Distinct',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.SUM,
|
||||||
|
label: 'Sum',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.AVG,
|
||||||
|
label: 'Avg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.MAX,
|
||||||
|
label: 'Max',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.MIN,
|
||||||
|
label: 'Min',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.P05,
|
||||||
|
label: 'P05',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.P10,
|
||||||
|
label: 'P10',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.P20,
|
||||||
|
label: 'P20',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.P25,
|
||||||
|
label: 'P25',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.P50,
|
||||||
|
label: 'P50',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.P75,
|
||||||
|
label: 'P75',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.P90,
|
||||||
|
label: 'P90',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.P95,
|
||||||
|
label: 'P95',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.P99,
|
||||||
|
label: 'P99',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.RATE,
|
||||||
|
label: 'Rate',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.RATE_SUM,
|
||||||
|
label: 'Rate_sum',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.RATE_AVG,
|
||||||
|
label: 'Rate_avg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.RATE_MIN,
|
||||||
|
label: 'Rate_min',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TracesAggregatorOperator.RATE_MAX,
|
||||||
|
label: 'Rate_max',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const logsAggregateOperatorOptions: SelectOption<string, string>[] = [
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.NOOP,
|
||||||
|
label: 'NOOP',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.COUNT,
|
||||||
|
label: 'Count',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.COUNT_DISTINCT,
|
||||||
|
label: 'Count Distinct',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.SUM,
|
||||||
|
label: 'Sum',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.AVG,
|
||||||
|
label: 'Avg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.MAX,
|
||||||
|
label: 'Max',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.MIN,
|
||||||
|
label: 'Min',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.P05,
|
||||||
|
label: 'P05',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.P10,
|
||||||
|
label: 'P10',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.P20,
|
||||||
|
label: 'P20',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.P25,
|
||||||
|
label: 'P25',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.P50,
|
||||||
|
label: 'P50',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.P75,
|
||||||
|
label: 'P75',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.P90,
|
||||||
|
label: 'P90',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.P95,
|
||||||
|
label: 'P95',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.P99,
|
||||||
|
label: 'P99',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.RATE,
|
||||||
|
label: 'Rate',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.RATE_SUM,
|
||||||
|
label: 'Rate_sum',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.RATE_AVG,
|
||||||
|
label: 'Rate_avg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.RATE_MIN,
|
||||||
|
label: 'Rate_min',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LogsAggregatorOperator.RATE_MAX,
|
||||||
|
label: 'Rate_max',
|
||||||
|
},
|
||||||
|
];
|
3
frontend/src/constants/reactQueryKeys.ts
Normal file
3
frontend/src/constants/reactQueryKeys.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const REACT_QUERY_KEY = {
|
||||||
|
GET_ALL_LICENCES: 'GET_ALL_LICENCES',
|
||||||
|
};
|
5
frontend/src/constants/regExp.ts
Normal file
5
frontend/src/constants/regExp.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export const FORMULA_REGEXP = /F\d+/;
|
||||||
|
|
||||||
|
export const HAVING_FILTER_REGEXP = /^[-\d.,\s]+$/;
|
||||||
|
|
||||||
|
export const TYPE_ADDON_REGEXP = /_(.+)/;
|
@ -16,8 +16,8 @@ const ROUTES = {
|
|||||||
LIST_ALL_ALERT: '/alerts',
|
LIST_ALL_ALERT: '/alerts',
|
||||||
ALERTS_NEW: '/alerts/new',
|
ALERTS_NEW: '/alerts/new',
|
||||||
ALL_CHANNELS: '/settings/channels',
|
ALL_CHANNELS: '/settings/channels',
|
||||||
CHANNELS_NEW: '/setting/channels/new',
|
CHANNELS_NEW: '/settings/channels/new',
|
||||||
CHANNELS_EDIT: '/setting/channels/edit/:id',
|
CHANNELS_EDIT: '/settings/channels/:id',
|
||||||
ALL_ERROR: '/exceptions',
|
ALL_ERROR: '/exceptions',
|
||||||
ERROR_DETAIL: '/error-detail',
|
ERROR_DETAIL: '/error-detail',
|
||||||
VERSION: '/status',
|
VERSION: '/status',
|
||||||
|
@ -38,6 +38,8 @@ const themeColors = {
|
|||||||
whiteCream: '#ffffffd5',
|
whiteCream: '#ffffffd5',
|
||||||
black: '#000000',
|
black: '#000000',
|
||||||
lightgrey: '#ddd',
|
lightgrey: '#ddd',
|
||||||
|
borderLightGrey: '#d9d9d9',
|
||||||
|
borderDarkGrey: '#424242',
|
||||||
};
|
};
|
||||||
|
|
||||||
export { themeColors };
|
export { themeColors };
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
export enum QueryBuilderKeys {
|
|
||||||
GET_AGGREGATE_ATTRIBUTE = 'GET_AGGREGATE_ATTRIBUTE',
|
|
||||||
}
|
|
@ -6,7 +6,7 @@ import ROUTES from 'constants/routes';
|
|||||||
import useComponentPermission from 'hooks/useComponentPermission';
|
import useComponentPermission from 'hooks/useComponentPermission';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React, { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { generatePath } from 'react-router-dom';
|
import { generatePath } from 'react-router-dom';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
import { NotificationInstance } from 'antd/es/notification/interface';
|
import { NotificationInstance } from 'antd/es/notification/interface';
|
||||||
import deleteChannel from 'api/channels/delete';
|
import deleteChannel from 'api/channels/delete';
|
||||||
import React, { useState } from 'react';
|
import { Dispatch, SetStateAction, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Channels } from 'types/api/channels/getAll';
|
import { Channels } from 'types/api/channels/getAll';
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ function Delete({ notifications, setChannels, id }: DeleteProps): JSX.Element {
|
|||||||
|
|
||||||
interface DeleteProps {
|
interface DeleteProps {
|
||||||
notifications: NotificationInstance;
|
notifications: NotificationInstance;
|
||||||
setChannels: React.Dispatch<React.SetStateAction<Channels[]>>;
|
setChannels: Dispatch<SetStateAction<Channels[]>>;
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import ROUTES from 'constants/routes';
|
|||||||
import useComponentPermission from 'hooks/useComponentPermission';
|
import useComponentPermission from 'hooks/useComponentPermission';
|
||||||
import useFetch from 'hooks/useFetch';
|
import useFetch from 'hooks/useFetch';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React, { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
|
@ -23,7 +23,7 @@ import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute
|
|||||||
import useUrlQuery from 'hooks/useUrlQuery';
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
import createQueryParams from 'lib/createQueryParams';
|
import createQueryParams from 'lib/createQueryParams';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useQueries } from 'react-query';
|
import { useQueries } from 'react-query';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
|
@ -6,7 +6,7 @@ import Header from 'container/Header';
|
|||||||
import SideNav from 'container/SideNav';
|
import SideNav from 'container/SideNav';
|
||||||
import TopNav from 'container/TopNav';
|
import TopNav from 'container/TopNav';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import React, { ReactNode, useEffect, useRef } from 'react';
|
import { ReactNode, useEffect, useRef } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useQueries } from 'react-query';
|
import { useQueries } from 'react-query';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
@ -18,7 +18,7 @@ import {
|
|||||||
UPDATE_CONFIGS,
|
UPDATE_CONFIGS,
|
||||||
UPDATE_CURRENT_ERROR,
|
UPDATE_CURRENT_ERROR,
|
||||||
UPDATE_CURRENT_VERSION,
|
UPDATE_CURRENT_VERSION,
|
||||||
UPDATE_FEATURE_FLAGS,
|
UPDATE_FEATURE_FLAG_RESPONSE,
|
||||||
UPDATE_LATEST_VERSION,
|
UPDATE_LATEST_VERSION,
|
||||||
UPDATE_LATEST_VERSION_ERROR,
|
UPDATE_LATEST_VERSION_ERROR,
|
||||||
} from 'types/actions/app';
|
} from 'types/actions/app';
|
||||||
@ -27,7 +27,9 @@ import AppReducer from 'types/reducer/app';
|
|||||||
import { ChildrenContainer, Layout } from './styles';
|
import { ChildrenContainer, 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, user } = useSelector<AppState, AppReducer>(
|
||||||
|
(state) => state.app,
|
||||||
|
);
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@ -39,21 +41,21 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
] = useQueries([
|
] = useQueries([
|
||||||
{
|
{
|
||||||
queryFn: getUserVersion,
|
queryFn: getUserVersion,
|
||||||
queryKey: 'getUserVersion',
|
queryKey: ['getUserVersion', user?.accessJwt],
|
||||||
enabled: isLoggedIn,
|
enabled: isLoggedIn,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
queryFn: getUserLatestVersion,
|
queryFn: getUserLatestVersion,
|
||||||
queryKey: 'getUserLatestVersion',
|
queryKey: ['getUserLatestVersion', user?.accessJwt],
|
||||||
enabled: isLoggedIn,
|
enabled: isLoggedIn,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
queryFn: getFeaturesFlags,
|
queryFn: getFeaturesFlags,
|
||||||
queryKey: 'getFeatureFlags',
|
queryKey: ['getFeatureFlags', user?.accessJwt],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
queryFn: getDynamicConfigs,
|
queryFn: getDynamicConfigs,
|
||||||
queryKey: 'getDynamicConfigs',
|
queryKey: ['getDynamicConfigs', user?.accessJwt],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -129,19 +131,6 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
message: t('oops_something_went_wrong_version'),
|
message: t('oops_something_went_wrong_version'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
getFeaturesResponse.isFetched &&
|
|
||||||
getFeaturesResponse.isSuccess &&
|
|
||||||
getFeaturesResponse.data &&
|
|
||||||
getFeaturesResponse.data.payload
|
|
||||||
) {
|
|
||||||
dispatch({
|
|
||||||
type: UPDATE_FEATURE_FLAGS,
|
|
||||||
payload: {
|
|
||||||
...getFeaturesResponse.data.payload,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
getUserVersionResponse.isFetched &&
|
getUserVersionResponse.isFetched &&
|
||||||
@ -173,20 +162,6 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
getFeaturesResponse.isFetched &&
|
|
||||||
getFeaturesResponse.isSuccess &&
|
|
||||||
getFeaturesResponse.data &&
|
|
||||||
getFeaturesResponse.data.payload
|
|
||||||
) {
|
|
||||||
dispatch({
|
|
||||||
type: UPDATE_FEATURE_FLAGS,
|
|
||||||
payload: {
|
|
||||||
...getFeaturesResponse.data.payload,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
getDynamicConfigsResponse.isFetched &&
|
getDynamicConfigsResponse.isFetched &&
|
||||||
getDynamicConfigsResponse.isSuccess &&
|
getDynamicConfigsResponse.isSuccess &&
|
||||||
@ -226,6 +201,29 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
notifications,
|
notifications,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
getFeaturesResponse.isFetched &&
|
||||||
|
getFeaturesResponse.isSuccess &&
|
||||||
|
getFeaturesResponse.data &&
|
||||||
|
getFeaturesResponse.data.payload
|
||||||
|
) {
|
||||||
|
dispatch({
|
||||||
|
type: UPDATE_FEATURE_FLAG_RESPONSE,
|
||||||
|
payload: {
|
||||||
|
featureFlag: getFeaturesResponse.data.payload,
|
||||||
|
refetch: getFeaturesResponse.refetch,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
dispatch,
|
||||||
|
getFeaturesResponse.data,
|
||||||
|
getFeaturesResponse.isFetched,
|
||||||
|
getFeaturesResponse.isSuccess,
|
||||||
|
getFeaturesResponse.refetch,
|
||||||
|
]);
|
||||||
|
|
||||||
const isToDisplayLayout = isLoggedIn;
|
const isToDisplayLayout = isLoggedIn;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
hasError: boolean;
|
hasError: boolean;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
function LinkContainer({ children, href }: LinkContainerProps): JSX.Element {
|
function LinkContainer({ children, href }: LinkContainerProps): JSX.Element {
|
||||||
@ -16,7 +16,7 @@ function LinkContainer({ children, href }: LinkContainerProps): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface LinkContainerProps {
|
interface LinkContainerProps {
|
||||||
children: React.ReactNode;
|
children: ReactNode;
|
||||||
href: string;
|
href: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Menu, Space } from 'antd';
|
import { Menu, Space } from 'antd';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import React, { Suspense, useMemo } from 'react';
|
import { lazy, Suspense, useMemo } from 'react';
|
||||||
import { ConfigProps } from 'types/api/dynamicConfigs/getDynamicConfigs';
|
import { ConfigProps } from 'types/api/dynamicConfigs/getDynamicConfigs';
|
||||||
|
|
||||||
import ErrorLink from './ErrorLink';
|
import ErrorLink from './ErrorLink';
|
||||||
@ -17,7 +17,7 @@ function HelpToolTip({ config }: HelpToolTipProps): JSX.Element {
|
|||||||
|
|
||||||
const items = sortedConfig.map((item) => {
|
const items = sortedConfig.map((item) => {
|
||||||
const iconName = `${isDarkMode ? item.darkIcon : item.lightIcon}`;
|
const iconName = `${isDarkMode ? item.darkIcon : item.lightIcon}`;
|
||||||
const Component = React.lazy(
|
const Component = lazy(
|
||||||
() => import(`@ant-design/icons/es/icons/${iconName}.js`),
|
() => import(`@ant-design/icons/es/icons/${iconName}.js`),
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { Dropdown, Space } from 'antd';
|
import { Dropdown, Space } from 'antd';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import React, { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { ConfigProps } from 'types/api/dynamicConfigs/getDynamicConfigs';
|
import { ConfigProps } from 'types/api/dynamicConfigs/getDynamicConfigs';
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
import { PagerChannel } from './config';
|
import { PagerChannel } from './config';
|
||||||
|
|
||||||
export const PagerInitialConfig: Partial<PagerChannel> = {
|
export const PagerInitialConfig: Partial<PagerChannel> = {
|
||||||
description: `{{ range .Alerts -}}
|
description: `[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}
|
||||||
*Alert:* {{ if .Annotations.title }} {{ .Annotations.title }} {{ else }} {{ .Annotations.summary }} {{end}} {{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }}
|
{{- if gt (len .CommonLabels) (len .GroupLabels) -}}
|
||||||
|
{{" "}}(
|
||||||
*Description:* {{ .Annotations.description }}
|
{{- with .CommonLabels.Remove .GroupLabels.Names }}
|
||||||
|
{{- range $index, $label := .SortedPairs -}}
|
||||||
*Details:*
|
{{ if $index }}, {{ end }}
|
||||||
{{ range .Labels.SortedPairs }} • *{{ .Name }}:* {{ .Value }}
|
{{- $label.Name }}="{{ $label.Value -}}"
|
||||||
{{ end }}
|
{{- end }}
|
||||||
{{ end }}`,
|
{{- end -}}
|
||||||
|
)
|
||||||
|
{{- end }}`,
|
||||||
severity: '{{ (index .Alerts 0).Labels.severity }}',
|
severity: '{{ (index .Alerts 0).Labels.severity }}',
|
||||||
client: 'SigNoz Alert Manager',
|
client: 'SigNoz Alert Manager',
|
||||||
client_url: 'https://enter-signoz-host-n-port-here/alerts',
|
client_url: 'https://enter-signoz-host-n-port-here/alerts',
|
||||||
|
@ -9,7 +9,7 @@ import ROUTES from 'constants/routes';
|
|||||||
import FormAlertChannels from 'container/FormAlertChannels';
|
import FormAlertChannels from 'container/FormAlertChannels';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React, { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
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