mirror of
https://github.com/SigNoz/signoz.git
synced 2026-03-20 11:40:27 +00:00
Compare commits
3 Commits
refactor/c
...
feat/cloud
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce54f2f277 | ||
|
|
9e468513c5 | ||
|
|
05062d49e9 |
14
.github/CODEOWNERS
vendored
14
.github/CODEOWNERS
vendored
@@ -1,6 +1,8 @@
|
|||||||
# CODEOWNERS info: https://help.github.com/en/articles/about-code-owners
|
# CODEOWNERS info: https://help.github.com/en/articles/about-code-owners
|
||||||
|
|
||||||
# Owners are automatically requested for review for PRs that changes code that they own.
|
# Owners are automatically requested for review for PRs that changes code
|
||||||
|
|
||||||
|
# that they own.
|
||||||
|
|
||||||
/frontend/ @SigNoz/frontend-maintainers
|
/frontend/ @SigNoz/frontend-maintainers
|
||||||
|
|
||||||
@@ -9,10 +11,8 @@
|
|||||||
/frontend/src/container/OnboardingV2Container/onboarding-configs/onboarding-config-with-links.json @makeavish
|
/frontend/src/container/OnboardingV2Container/onboarding-configs/onboarding-config-with-links.json @makeavish
|
||||||
/frontend/src/container/OnboardingV2Container/AddDataSource/AddDataSource.tsx @makeavish
|
/frontend/src/container/OnboardingV2Container/AddDataSource/AddDataSource.tsx @makeavish
|
||||||
|
|
||||||
# CI
|
/deploy/ @SigNoz/devops
|
||||||
/deploy/ @therealpandey
|
.github @SigNoz/devops
|
||||||
.github @therealpandey
|
|
||||||
go.mod @therealpandey
|
|
||||||
|
|
||||||
# Scaffold Owners
|
# Scaffold Owners
|
||||||
|
|
||||||
@@ -105,10 +105,6 @@ go.mod @therealpandey
|
|||||||
/pkg/modules/authdomain/ @vikrantgupta25
|
/pkg/modules/authdomain/ @vikrantgupta25
|
||||||
/pkg/modules/role/ @vikrantgupta25
|
/pkg/modules/role/ @vikrantgupta25
|
||||||
|
|
||||||
# IdentN Owners
|
|
||||||
/pkg/identn/ @vikrantgupta25
|
|
||||||
/pkg/http/middleware/identn.go @vikrantgupta25
|
|
||||||
|
|
||||||
# Integration tests
|
# Integration tests
|
||||||
|
|
||||||
/tests/integration/ @vikrantgupta25
|
/tests/integration/ @vikrantgupta25
|
||||||
|
|||||||
10
.github/workflows/goci.yaml
vendored
10
.github/workflows/goci.yaml
vendored
@@ -102,3 +102,13 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
go run cmd/enterprise/*.go generate openapi
|
go run cmd/enterprise/*.go generate openapi
|
||||||
git diff --compact-summary --exit-code || (echo; echo "Unexpected difference in openapi spec. Run go run cmd/enterprise/*.go generate openapi locally and commit."; exit 1)
|
git diff --compact-summary --exit-code || (echo; echo "Unexpected difference in openapi spec. Run go run cmd/enterprise/*.go generate openapi locally and commit."; exit 1)
|
||||||
|
- name: node-install
|
||||||
|
uses: actions/setup-node@v5
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
- name: install-frontend
|
||||||
|
run: cd frontend && yarn install
|
||||||
|
- name: generate-api-clients
|
||||||
|
run: |
|
||||||
|
cd frontend && yarn generate:api
|
||||||
|
git diff --compact-summary --exit-code || (echo; echo "Unexpected difference in generated api clients. Run yarn generate:api in frontend/ locally and commit."; exit 1)
|
||||||
|
|||||||
51
.github/workflows/jsci.yaml
vendored
51
.github/workflows/jsci.yaml
vendored
@@ -52,16 +52,16 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
PRIMUS_REF: main
|
PRIMUS_REF: main
|
||||||
JS_SRC: frontend
|
JS_SRC: frontend
|
||||||
languages:
|
md-languages:
|
||||||
if: |
|
if: |
|
||||||
github.event_name == 'merge_group' ||
|
github.event_name == 'merge_group' ||
|
||||||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
|
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
|
||||||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
|
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: self-checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: run
|
- name: validate md languages
|
||||||
run: bash frontend/scripts/validate-md-languages.sh
|
run: bash frontend/scripts/validate-md-languages.sh
|
||||||
authz:
|
authz:
|
||||||
if: |
|
if: |
|
||||||
@@ -70,55 +70,44 @@ jobs:
|
|||||||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
|
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: self-checkout
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v5
|
||||||
- name: node-install
|
|
||||||
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v5
|
uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: "22"
|
node-version: "22"
|
||||||
- name: deps-install
|
|
||||||
|
- name: Install frontend dependencies
|
||||||
working-directory: ./frontend
|
working-directory: ./frontend
|
||||||
run: |
|
run: |
|
||||||
yarn install
|
yarn install
|
||||||
- name: uv-install
|
|
||||||
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v5
|
uses: astral-sh/setup-uv@v5
|
||||||
- name: uv-deps
|
|
||||||
|
- name: Install Python dependencies
|
||||||
working-directory: ./tests/integration
|
working-directory: ./tests/integration
|
||||||
run: |
|
run: |
|
||||||
uv sync
|
uv sync
|
||||||
- name: setup-test
|
|
||||||
|
- name: Start test environment
|
||||||
run: |
|
run: |
|
||||||
make py-test-setup
|
make py-test-setup
|
||||||
- name: generate
|
|
||||||
|
- name: Generate permissions.type.ts
|
||||||
working-directory: ./frontend
|
working-directory: ./frontend
|
||||||
run: |
|
run: |
|
||||||
yarn generate:permissions-type
|
yarn generate:permissions-type
|
||||||
- name: teardown-test
|
|
||||||
|
- name: Teardown test environment
|
||||||
if: always()
|
if: always()
|
||||||
run: |
|
run: |
|
||||||
make py-test-teardown
|
make py-test-teardown
|
||||||
- name: validate
|
|
||||||
|
- name: Check for changes
|
||||||
run: |
|
run: |
|
||||||
if ! git diff --exit-code frontend/src/hooks/useAuthZ/permissions.type.ts; then
|
if ! git diff --exit-code frontend/src/hooks/useAuthZ/permissions.type.ts; then
|
||||||
echo "::error::frontend/src/hooks/useAuthZ/permissions.type.ts is out of date. Please run the generator locally and commit the changes: npm run generate:permissions-type (from the frontend directory)"
|
echo "::error::frontend/src/hooks/useAuthZ/permissions.type.ts is out of date. Please run the generator locally and commit the changes: npm run generate:permissions-type (from the frontend directory)"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
openapi:
|
|
||||||
if: |
|
|
||||||
github.event_name == 'merge_group' ||
|
|
||||||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
|
|
||||||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: self-checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: node-install
|
|
||||||
uses: actions/setup-node@v5
|
|
||||||
with:
|
|
||||||
node-version: "22"
|
|
||||||
- name: install-frontend
|
|
||||||
run: cd frontend && yarn install
|
|
||||||
- name: generate-api-clients
|
|
||||||
run: |
|
|
||||||
cd frontend && yarn generate:api
|
|
||||||
git diff --compact-summary --exit-code || (echo; echo "Unexpected difference in generated api clients. Run yarn generate:api in frontend/ locally and commit."; exit 1)
|
|
||||||
|
|||||||
60
.github/workflows/mergequeueci.yaml
vendored
60
.github/workflows/mergequeueci.yaml
vendored
@@ -1,60 +0,0 @@
|
|||||||
name: mergequeueci
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types:
|
|
||||||
- dequeued
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
notify:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.event.pull_request.merged == false
|
|
||||||
steps:
|
|
||||||
- name: alert
|
|
||||||
uses: slackapi/slack-github-action@v2.1.1
|
|
||||||
with:
|
|
||||||
webhook: ${{ secrets.SLACK_MERGE_QUEUE_WEBHOOK }}
|
|
||||||
webhook-type: incoming-webhook
|
|
||||||
payload: |
|
|
||||||
{
|
|
||||||
"text": ":x: PR removed from merge queue",
|
|
||||||
"blocks": [
|
|
||||||
{
|
|
||||||
"type": "header",
|
|
||||||
"text": {
|
|
||||||
"type": "plain_text",
|
|
||||||
"text": ":x: PR Removed from Merge Queue"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "section",
|
|
||||||
"text": {
|
|
||||||
"type": "mrkdwn",
|
|
||||||
"text": "*<${{ github.event.pull_request.html_url }}|PR #${{ github.event.pull_request.number }}: ${{ github.event.pull_request.title }}>*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "divider"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "section",
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"type": "mrkdwn",
|
|
||||||
"text": "*Author*\n@${{ github.event.pull_request.user.login }}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
- name: comment
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
||||||
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
|
|
||||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
|
||||||
run: |
|
|
||||||
gh api repos/${{ github.repository }}/issues/$PR_NUMBER/comments \
|
|
||||||
-f body="> :x: **PR removed from merge queue**
|
|
||||||
>
|
|
||||||
> @$PR_AUTHOR your PR was removed from the merge queue. Fix the issue and re-queue when ready."
|
|
||||||
@@ -13,8 +13,11 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/factory"
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
"github.com/SigNoz/signoz/pkg/gateway"
|
"github.com/SigNoz/signoz/pkg/gateway"
|
||||||
"github.com/SigNoz/signoz/pkg/gateway/noopgateway"
|
"github.com/SigNoz/signoz/pkg/gateway/noopgateway"
|
||||||
|
"github.com/SigNoz/signoz/pkg/global"
|
||||||
"github.com/SigNoz/signoz/pkg/licensing"
|
"github.com/SigNoz/signoz/pkg/licensing"
|
||||||
"github.com/SigNoz/signoz/pkg/licensing/nooplicensing"
|
"github.com/SigNoz/signoz/pkg/licensing/nooplicensing"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/cloudintegration"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/cloudintegration/implcloudintegration"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/dashboard"
|
"github.com/SigNoz/signoz/pkg/modules/dashboard"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
|
"github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||||
@@ -88,6 +91,9 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
|||||||
func(ps factory.ProviderSettings, q querier.Querier, a analytics.Analytics) querier.Handler {
|
func(ps factory.ProviderSettings, q querier.Querier, a analytics.Analytics) querier.Handler {
|
||||||
return querier.NewHandler(ps, q, a)
|
return querier.NewHandler(ps, q, a)
|
||||||
},
|
},
|
||||||
|
func(_ sqlstore.SQLStore, _ licensing.Licensing, _ zeus.Zeus, _ gateway.Gateway, _ global.Config) cloudintegration.Module {
|
||||||
|
return implcloudintegration.NewModule()
|
||||||
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.ErrorContext(ctx, "failed to create signoz", "error", err)
|
logger.ErrorContext(ctx, "failed to create signoz", "error", err)
|
||||||
|
|||||||
@@ -9,12 +9,13 @@ import (
|
|||||||
"github.com/SigNoz/signoz/ee/authn/callbackauthn/oidccallbackauthn"
|
"github.com/SigNoz/signoz/ee/authn/callbackauthn/oidccallbackauthn"
|
||||||
"github.com/SigNoz/signoz/ee/authn/callbackauthn/samlcallbackauthn"
|
"github.com/SigNoz/signoz/ee/authn/callbackauthn/samlcallbackauthn"
|
||||||
"github.com/SigNoz/signoz/ee/authz/openfgaauthz"
|
"github.com/SigNoz/signoz/ee/authz/openfgaauthz"
|
||||||
eequerier "github.com/SigNoz/signoz/ee/querier"
|
|
||||||
"github.com/SigNoz/signoz/ee/authz/openfgaschema"
|
"github.com/SigNoz/signoz/ee/authz/openfgaschema"
|
||||||
"github.com/SigNoz/signoz/ee/gateway/httpgateway"
|
"github.com/SigNoz/signoz/ee/gateway/httpgateway"
|
||||||
enterpriselicensing "github.com/SigNoz/signoz/ee/licensing"
|
enterpriselicensing "github.com/SigNoz/signoz/ee/licensing"
|
||||||
"github.com/SigNoz/signoz/ee/licensing/httplicensing"
|
"github.com/SigNoz/signoz/ee/licensing/httplicensing"
|
||||||
|
"github.com/SigNoz/signoz/ee/modules/cloudintegration/implcloudintegration"
|
||||||
"github.com/SigNoz/signoz/ee/modules/dashboard/impldashboard"
|
"github.com/SigNoz/signoz/ee/modules/dashboard/impldashboard"
|
||||||
|
eequerier "github.com/SigNoz/signoz/ee/querier"
|
||||||
enterpriseapp "github.com/SigNoz/signoz/ee/query-service/app"
|
enterpriseapp "github.com/SigNoz/signoz/ee/query-service/app"
|
||||||
"github.com/SigNoz/signoz/ee/sqlschema/postgressqlschema"
|
"github.com/SigNoz/signoz/ee/sqlschema/postgressqlschema"
|
||||||
"github.com/SigNoz/signoz/ee/sqlstore/postgressqlstore"
|
"github.com/SigNoz/signoz/ee/sqlstore/postgressqlstore"
|
||||||
@@ -25,7 +26,10 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/authz"
|
"github.com/SigNoz/signoz/pkg/authz"
|
||||||
"github.com/SigNoz/signoz/pkg/factory"
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
"github.com/SigNoz/signoz/pkg/gateway"
|
"github.com/SigNoz/signoz/pkg/gateway"
|
||||||
|
"github.com/SigNoz/signoz/pkg/global"
|
||||||
"github.com/SigNoz/signoz/pkg/licensing"
|
"github.com/SigNoz/signoz/pkg/licensing"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/cloudintegration"
|
||||||
|
pkgimplcloudintegration "github.com/SigNoz/signoz/pkg/modules/cloudintegration/implcloudintegration"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/dashboard"
|
"github.com/SigNoz/signoz/pkg/modules/dashboard"
|
||||||
pkgimpldashboard "github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
|
pkgimpldashboard "github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||||
@@ -129,6 +133,9 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
|||||||
communityHandler := querier.NewHandler(ps, q, a)
|
communityHandler := querier.NewHandler(ps, q, a)
|
||||||
return eequerier.NewHandler(ps, q, communityHandler)
|
return eequerier.NewHandler(ps, q, communityHandler)
|
||||||
},
|
},
|
||||||
|
func(store sqlstore.SQLStore, lic licensing.Licensing, z zeus.Zeus, gw gateway.Gateway, gc global.Config) cloudintegration.Module {
|
||||||
|
return implcloudintegration.NewModule(pkgimplcloudintegration.NewStore(store), store, lic, z, gw, gc)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/version"
|
"github.com/SigNoz/signoz/pkg/version"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"go.uber.org/zap" //nolint:depguard
|
||||||
)
|
)
|
||||||
|
|
||||||
var RootCmd = &cobra.Command{
|
var RootCmd = &cobra.Command{
|
||||||
@@ -18,6 +19,12 @@ var RootCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Execute(logger *slog.Logger) {
|
func Execute(logger *slog.Logger) {
|
||||||
|
zapLogger := newZapLogger()
|
||||||
|
zap.ReplaceGlobals(zapLogger)
|
||||||
|
defer func() {
|
||||||
|
_ = zapLogger.Sync()
|
||||||
|
}()
|
||||||
|
|
||||||
err := RootCmd.Execute()
|
err := RootCmd.Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.ErrorContext(RootCmd.Context(), "error running command", "error", err)
|
logger.ErrorContext(RootCmd.Context(), "error running command", "error", err)
|
||||||
|
|||||||
110
cmd/zap.go
Normal file
110
cmd/zap.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"go.uber.org/zap" //nolint:depguard
|
||||||
|
"go.uber.org/zap/zapcore" //nolint:depguard
|
||||||
|
)
|
||||||
|
|
||||||
|
// Deprecated: Use `NewLogger` from `pkg/instrumentation` instead.
|
||||||
|
func newZapLogger() *zap.Logger {
|
||||||
|
config := zap.NewProductionConfig()
|
||||||
|
config.EncoderConfig.TimeKey = "timestamp"
|
||||||
|
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||||
|
|
||||||
|
// Extract sampling config before building the logger.
|
||||||
|
// We need to disable sampling in the config and apply it manually later
|
||||||
|
// to ensure correct core ordering. See filteringCore documentation for details.
|
||||||
|
samplerConfig := config.Sampling
|
||||||
|
config.Sampling = nil
|
||||||
|
|
||||||
|
logger, _ := config.Build()
|
||||||
|
|
||||||
|
// Wrap with custom core wrapping to filter certain log entries.
|
||||||
|
// The order of wrapping is important:
|
||||||
|
// 1. First wrap with filteringCore
|
||||||
|
// 2. Then wrap with sampler
|
||||||
|
//
|
||||||
|
// This creates the call chain: sampler -> filteringCore -> ioCore
|
||||||
|
//
|
||||||
|
// During logging:
|
||||||
|
// - sampler.Check decides whether to sample the log entry
|
||||||
|
// - If sampled, filteringCore.Check is called
|
||||||
|
// - filteringCore adds itself to CheckedEntry.cores
|
||||||
|
// - All cores in CheckedEntry.cores have their Write method called
|
||||||
|
// - filteringCore.Write can now filter the entry before passing to ioCore
|
||||||
|
//
|
||||||
|
// If we didn't disable the sampler above, filteringCore would have wrapped
|
||||||
|
// sampler. By calling sampler.Check we would have allowed it to call
|
||||||
|
// ioCore.Check that adds itself to CheckedEntry.cores. Then ioCore.Write
|
||||||
|
// would have bypassed our checks, making filtering impossible.
|
||||||
|
return logger.WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core {
|
||||||
|
core = &filteringCore{core}
|
||||||
|
if samplerConfig != nil {
|
||||||
|
core = zapcore.NewSamplerWithOptions(
|
||||||
|
core,
|
||||||
|
time.Second,
|
||||||
|
samplerConfig.Initial,
|
||||||
|
samplerConfig.Thereafter,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return core
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// filteringCore wraps a zapcore.Core to filter out log entries based on a
|
||||||
|
// custom logic.
|
||||||
|
//
|
||||||
|
// Note: This core must be positioned before the sampler in the core chain
|
||||||
|
// to ensure Write is called. See newZapLogger for ordering details.
|
||||||
|
type filteringCore struct {
|
||||||
|
zapcore.Core
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter determines whether a log entry should be written based on its fields.
|
||||||
|
// Returns false if the entry should be suppressed, true otherwise.
|
||||||
|
//
|
||||||
|
// Current filters:
|
||||||
|
// - context.Canceled: These are expected errors from cancelled operations,
|
||||||
|
// and create noise in logs.
|
||||||
|
func (c *filteringCore) filter(fields []zapcore.Field) bool {
|
||||||
|
for _, field := range fields {
|
||||||
|
if field.Type == zapcore.ErrorType {
|
||||||
|
if loggedErr, ok := field.Interface.(error); ok {
|
||||||
|
// Suppress logs containing context.Canceled errors
|
||||||
|
if errors.Is(loggedErr, context.Canceled) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// With implements zapcore.Core.With
|
||||||
|
// It returns a new copy with the added context.
|
||||||
|
func (c *filteringCore) With(fields []zapcore.Field) zapcore.Core {
|
||||||
|
return &filteringCore{c.Core.With(fields)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check implements zapcore.Core.Check.
|
||||||
|
// It adds this core to the CheckedEntry if the log level is enabled,
|
||||||
|
// ensuring that Write will be called for this entry.
|
||||||
|
func (c *filteringCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
|
||||||
|
if c.Enabled(ent.Level) {
|
||||||
|
return ce.AddCore(ent, c)
|
||||||
|
}
|
||||||
|
return ce
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write implements zapcore.Core.Write.
|
||||||
|
// It filters log entries based on their fields before delegating to the wrapped core.
|
||||||
|
func (c *filteringCore) Write(ent zapcore.Entry, fields []zapcore.Field) error {
|
||||||
|
if !c.filter(fields) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.Core.Write(ent, fields)
|
||||||
|
}
|
||||||
@@ -308,9 +308,6 @@ user:
|
|||||||
allow_self: true
|
allow_self: true
|
||||||
# The duration within which a user can reset their password.
|
# The duration within which a user can reset their password.
|
||||||
max_token_lifetime: 6h
|
max_token_lifetime: 6h
|
||||||
invite:
|
|
||||||
# The duration within which a user can accept their invite.
|
|
||||||
max_token_lifetime: 48h
|
|
||||||
root:
|
root:
|
||||||
# Whether to enable the root user. When enabled, a root user is provisioned
|
# Whether to enable the root user. When enabled, a root user is provisioned
|
||||||
# on startup using the email and password below. The root user cannot be
|
# on startup using the email and password below. The root user cannot be
|
||||||
@@ -324,19 +321,3 @@ user:
|
|||||||
org:
|
org:
|
||||||
name: default
|
name: default
|
||||||
id: 00000000-0000-0000-0000-000000000000
|
id: 00000000-0000-0000-0000-000000000000
|
||||||
|
|
||||||
##################### IdentN #####################
|
|
||||||
identn:
|
|
||||||
tokenizer:
|
|
||||||
# toggle the identN resolver
|
|
||||||
enabled: true
|
|
||||||
# headers to use for tokenizer identN resolver
|
|
||||||
headers:
|
|
||||||
- Authorization
|
|
||||||
- Sec-WebSocket-Protocol
|
|
||||||
apikey:
|
|
||||||
# toggle the identN resolver
|
|
||||||
enabled: true
|
|
||||||
# headers to use for apikey identN resolver
|
|
||||||
headers:
|
|
||||||
- SIGNOZ-API-KEY
|
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ services:
|
|||||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||||
signoz:
|
signoz:
|
||||||
!!merge <<: *db-depend
|
!!merge <<: *db-depend
|
||||||
image: signoz/signoz:v0.116.0
|
image: signoz/signoz:v0.115.0
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080" # signoz port
|
- "8080:8080" # signoz port
|
||||||
# - "6060:6060" # pprof port
|
# - "6060:6060" # pprof port
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ services:
|
|||||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||||
signoz:
|
signoz:
|
||||||
!!merge <<: *db-depend
|
!!merge <<: *db-depend
|
||||||
image: signoz/signoz:v0.116.0
|
image: signoz/signoz:v0.115.0
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080" # signoz port
|
- "8080:8080" # signoz port
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
38
deploy/docker-swarm/generator/hotrod/docker-compose.yaml
Normal file
38
deploy/docker-swarm/generator/hotrod/docker-compose.yaml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
version: "3"
|
||||||
|
x-common: &common
|
||||||
|
networks:
|
||||||
|
- signoz-net
|
||||||
|
extra_hosts:
|
||||||
|
- host.docker.internal:host-gateway
|
||||||
|
logging:
|
||||||
|
options:
|
||||||
|
max-size: 50m
|
||||||
|
max-file: "3"
|
||||||
|
deploy:
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
|
services:
|
||||||
|
hotrod:
|
||||||
|
<<: *common
|
||||||
|
image: jaegertracing/example-hotrod:1.61.0
|
||||||
|
command: [ "all" ]
|
||||||
|
environment:
|
||||||
|
- OTEL_EXPORTER_OTLP_ENDPOINT=http://host.docker.internal:4318 #
|
||||||
|
load-hotrod:
|
||||||
|
<<: *common
|
||||||
|
image: "signoz/locust:1.2.3"
|
||||||
|
environment:
|
||||||
|
ATTACKED_HOST: http://hotrod:8080
|
||||||
|
LOCUST_MODE: standalone
|
||||||
|
NO_PROXY: standalone
|
||||||
|
TASK_DELAY_FROM: 5
|
||||||
|
TASK_DELAY_TO: 30
|
||||||
|
QUIET_MODE: "${QUIET_MODE:-false}"
|
||||||
|
LOCUST_OPTS: "--headless -u 10 -r 1"
|
||||||
|
volumes:
|
||||||
|
- ../../../common/locust-scripts:/locust
|
||||||
|
|
||||||
|
networks:
|
||||||
|
signoz-net:
|
||||||
|
name: signoz-net
|
||||||
|
external: true
|
||||||
69
deploy/docker-swarm/generator/infra/docker-compose.yaml
Normal file
69
deploy/docker-swarm/generator/infra/docker-compose.yaml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
version: "3"
|
||||||
|
x-common: &common
|
||||||
|
networks:
|
||||||
|
- signoz-net
|
||||||
|
extra_hosts:
|
||||||
|
- host.docker.internal:host-gateway
|
||||||
|
logging:
|
||||||
|
options:
|
||||||
|
max-size: 50m
|
||||||
|
max-file: "3"
|
||||||
|
deploy:
|
||||||
|
mode: global
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
|
services:
|
||||||
|
otel-agent:
|
||||||
|
<<: *common
|
||||||
|
image: otel/opentelemetry-collector-contrib:0.111.0
|
||||||
|
command:
|
||||||
|
- --config=/etc/otel-collector-config.yaml
|
||||||
|
volumes:
|
||||||
|
- ./otel-agent-config.yaml:/etc/otel-collector-config.yaml
|
||||||
|
- /:/hostfs:ro
|
||||||
|
environment:
|
||||||
|
- SIGNOZ_COLLECTOR_ENDPOINT=http://host.docker.internal:4317 # In case of external SigNoz or cloud, update the endpoint and access token
|
||||||
|
- OTEL_RESOURCE_ATTRIBUTES=host.name={{.Node.Hostname}},os.type={{.Node.Platform.OS}}
|
||||||
|
# - SIGNOZ_ACCESS_TOKEN="<your-access-token>"
|
||||||
|
# Before exposing the ports, make sure the ports are not used by other services
|
||||||
|
# ports:
|
||||||
|
# - "4317:4317"
|
||||||
|
# - "4318:4318"
|
||||||
|
otel-metrics:
|
||||||
|
<<: *common
|
||||||
|
image: otel/opentelemetry-collector-contrib:0.111.0
|
||||||
|
user: 0:0 # If you have security concerns, you can replace this with your `UID:GID` that has necessary permissions to docker.sock
|
||||||
|
command:
|
||||||
|
- --config=/etc/otel-collector-config.yaml
|
||||||
|
volumes:
|
||||||
|
- ./otel-metrics-config.yaml:/etc/otel-collector-config.yaml
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
environment:
|
||||||
|
- SIGNOZ_COLLECTOR_ENDPOINT=http://host.docker.internal:4317 # In case of external SigNoz or cloud, update the endpoint and access token
|
||||||
|
- OTEL_RESOURCE_ATTRIBUTES=host.name={{.Node.Hostname}},os.type={{.Node.Platform.OS}}
|
||||||
|
# - SIGNOZ_ACCESS_TOKEN="<your-access-token>"
|
||||||
|
# Before exposing the ports, make sure the ports are not used by other services
|
||||||
|
# ports:
|
||||||
|
# - "4317:4317"
|
||||||
|
# - "4318:4318"
|
||||||
|
deploy:
|
||||||
|
mode: replicated
|
||||||
|
replicas: 1
|
||||||
|
placement:
|
||||||
|
constraints:
|
||||||
|
- node.role == manager
|
||||||
|
logspout:
|
||||||
|
<<: *common
|
||||||
|
image: "gliderlabs/logspout:v3.2.14"
|
||||||
|
command: syslog+tcp://otel-agent:2255
|
||||||
|
user: root
|
||||||
|
volumes:
|
||||||
|
- /etc/hostname:/etc/host_hostname:ro
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
depends_on:
|
||||||
|
- otel-agent
|
||||||
|
|
||||||
|
networks:
|
||||||
|
signoz-net:
|
||||||
|
name: signoz-net
|
||||||
|
external: true
|
||||||
102
deploy/docker-swarm/generator/infra/otel-agent-config.yaml
Normal file
102
deploy/docker-swarm/generator/infra/otel-agent-config.yaml
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
receivers:
|
||||||
|
hostmetrics:
|
||||||
|
collection_interval: 30s
|
||||||
|
root_path: /hostfs
|
||||||
|
scrapers:
|
||||||
|
cpu: {}
|
||||||
|
load: {}
|
||||||
|
memory: {}
|
||||||
|
disk: {}
|
||||||
|
filesystem: {}
|
||||||
|
network: {}
|
||||||
|
otlp:
|
||||||
|
protocols:
|
||||||
|
grpc:
|
||||||
|
endpoint: 0.0.0.0:4317
|
||||||
|
http:
|
||||||
|
endpoint: 0.0.0.0:4318
|
||||||
|
prometheus:
|
||||||
|
config:
|
||||||
|
global:
|
||||||
|
scrape_interval: 60s
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: otel-agent
|
||||||
|
static_configs:
|
||||||
|
- targets:
|
||||||
|
- localhost:8888
|
||||||
|
labels:
|
||||||
|
job_name: otel-agent
|
||||||
|
tcplog/docker:
|
||||||
|
listen_address: "0.0.0.0:2255"
|
||||||
|
operators:
|
||||||
|
- type: regex_parser
|
||||||
|
regex: '^<([0-9]+)>[0-9]+ (?P<timestamp>[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?) (?P<container_id>\S+) (?P<container_name>\S+) [0-9]+ - -( (?P<body>.*))?'
|
||||||
|
timestamp:
|
||||||
|
parse_from: attributes.timestamp
|
||||||
|
layout: '%Y-%m-%dT%H:%M:%S.%LZ'
|
||||||
|
- type: move
|
||||||
|
from: attributes["body"]
|
||||||
|
to: body
|
||||||
|
- type: remove
|
||||||
|
field: attributes.timestamp
|
||||||
|
# please remove names from below if you want to collect logs from them
|
||||||
|
- type: filter
|
||||||
|
id: signoz_logs_filter
|
||||||
|
expr: 'attributes.container_name matches "^(signoz_(logspout|signoz|otel-collector|clickhouse|zookeeper))|(infra_(logspout|otel-agent|otel-metrics)).*"'
|
||||||
|
processors:
|
||||||
|
batch:
|
||||||
|
send_batch_size: 10000
|
||||||
|
send_batch_max_size: 11000
|
||||||
|
timeout: 10s
|
||||||
|
resourcedetection:
|
||||||
|
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
|
||||||
|
detectors:
|
||||||
|
# - ec2
|
||||||
|
# - gcp
|
||||||
|
# - azure
|
||||||
|
- env
|
||||||
|
- system
|
||||||
|
timeout: 2s
|
||||||
|
extensions:
|
||||||
|
health_check:
|
||||||
|
endpoint: 0.0.0.0:13133
|
||||||
|
pprof:
|
||||||
|
endpoint: 0.0.0.0:1777
|
||||||
|
exporters:
|
||||||
|
otlp:
|
||||||
|
endpoint: ${env:SIGNOZ_COLLECTOR_ENDPOINT}
|
||||||
|
tls:
|
||||||
|
insecure: true
|
||||||
|
headers:
|
||||||
|
signoz-access-token: ${env:SIGNOZ_ACCESS_TOKEN}
|
||||||
|
# debug: {}
|
||||||
|
service:
|
||||||
|
telemetry:
|
||||||
|
logs:
|
||||||
|
encoding: json
|
||||||
|
metrics:
|
||||||
|
address: 0.0.0.0:8888
|
||||||
|
extensions:
|
||||||
|
- health_check
|
||||||
|
- pprof
|
||||||
|
pipelines:
|
||||||
|
traces:
|
||||||
|
receivers: [otlp]
|
||||||
|
processors: [resourcedetection, batch]
|
||||||
|
exporters: [otlp]
|
||||||
|
metrics:
|
||||||
|
receivers: [otlp]
|
||||||
|
processors: [resourcedetection, batch]
|
||||||
|
exporters: [otlp]
|
||||||
|
metrics/hostmetrics:
|
||||||
|
receivers: [hostmetrics]
|
||||||
|
processors: [resourcedetection, batch]
|
||||||
|
exporters: [otlp]
|
||||||
|
metrics/prometheus:
|
||||||
|
receivers: [prometheus]
|
||||||
|
processors: [resourcedetection, batch]
|
||||||
|
exporters: [otlp]
|
||||||
|
logs:
|
||||||
|
receivers: [otlp, tcplog/docker]
|
||||||
|
processors: [resourcedetection, batch]
|
||||||
|
exporters: [otlp]
|
||||||
103
deploy/docker-swarm/generator/infra/otel-metrics-config.yaml
Normal file
103
deploy/docker-swarm/generator/infra/otel-metrics-config.yaml
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
receivers:
|
||||||
|
prometheus:
|
||||||
|
config:
|
||||||
|
global:
|
||||||
|
scrape_interval: 60s
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: otel-metrics
|
||||||
|
static_configs:
|
||||||
|
- targets:
|
||||||
|
- localhost:8888
|
||||||
|
labels:
|
||||||
|
job_name: otel-metrics
|
||||||
|
# For Docker daemon metrics to be scraped, it must be configured to expose
|
||||||
|
# Prometheus metrics, as documented here: https://docs.docker.com/config/daemon/prometheus/
|
||||||
|
# - job_name: docker-daemon
|
||||||
|
# dockerswarm_sd_configs:
|
||||||
|
# - host: unix:///var/run/docker.sock
|
||||||
|
# role: nodes
|
||||||
|
# relabel_configs:
|
||||||
|
# - source_labels: [__meta_dockerswarm_node_address]
|
||||||
|
# target_label: __address__
|
||||||
|
# replacement: $1:9323
|
||||||
|
- job_name: "dockerswarm"
|
||||||
|
dockerswarm_sd_configs:
|
||||||
|
- host: unix:///var/run/docker.sock
|
||||||
|
role: tasks
|
||||||
|
relabel_configs:
|
||||||
|
- action: keep
|
||||||
|
regex: running
|
||||||
|
source_labels:
|
||||||
|
- __meta_dockerswarm_task_desired_state
|
||||||
|
- action: keep
|
||||||
|
regex: true
|
||||||
|
source_labels:
|
||||||
|
- __meta_dockerswarm_service_label_signoz_io_scrape
|
||||||
|
- regex: ([^:]+)(?::\d+)?
|
||||||
|
replacement: $1
|
||||||
|
source_labels:
|
||||||
|
- __address__
|
||||||
|
target_label: swarm_container_ip
|
||||||
|
- separator: .
|
||||||
|
source_labels:
|
||||||
|
- __meta_dockerswarm_service_name
|
||||||
|
- __meta_dockerswarm_task_slot
|
||||||
|
- __meta_dockerswarm_task_id
|
||||||
|
target_label: swarm_container_name
|
||||||
|
- target_label: __address__
|
||||||
|
source_labels:
|
||||||
|
- swarm_container_ip
|
||||||
|
- __meta_dockerswarm_service_label_signoz_io_port
|
||||||
|
separator: ":"
|
||||||
|
- source_labels:
|
||||||
|
- __meta_dockerswarm_service_label_signoz_io_path
|
||||||
|
target_label: __metrics_path__
|
||||||
|
- source_labels:
|
||||||
|
- __meta_dockerswarm_service_label_com_docker_stack_namespace
|
||||||
|
target_label: namespace
|
||||||
|
- source_labels:
|
||||||
|
- __meta_dockerswarm_service_name
|
||||||
|
target_label: service_name
|
||||||
|
- source_labels:
|
||||||
|
- __meta_dockerswarm_task_id
|
||||||
|
target_label: service_instance_id
|
||||||
|
- source_labels:
|
||||||
|
- __meta_dockerswarm_node_hostname
|
||||||
|
target_label: host_name
|
||||||
|
processors:
|
||||||
|
batch:
|
||||||
|
send_batch_size: 10000
|
||||||
|
send_batch_max_size: 11000
|
||||||
|
timeout: 10s
|
||||||
|
resourcedetection:
|
||||||
|
detectors:
|
||||||
|
- env
|
||||||
|
- system
|
||||||
|
timeout: 2s
|
||||||
|
extensions:
|
||||||
|
health_check:
|
||||||
|
endpoint: 0.0.0.0:13133
|
||||||
|
pprof:
|
||||||
|
endpoint: 0.0.0.0:1777
|
||||||
|
exporters:
|
||||||
|
otlp:
|
||||||
|
endpoint: ${env:SIGNOZ_COLLECTOR_ENDPOINT}
|
||||||
|
tls:
|
||||||
|
insecure: true
|
||||||
|
headers:
|
||||||
|
signoz-access-token: ${env:SIGNOZ_ACCESS_TOKEN}
|
||||||
|
# debug: {}
|
||||||
|
service:
|
||||||
|
telemetry:
|
||||||
|
logs:
|
||||||
|
encoding: json
|
||||||
|
metrics:
|
||||||
|
address: 0.0.0.0:8888
|
||||||
|
extensions:
|
||||||
|
- health_check
|
||||||
|
- pprof
|
||||||
|
pipelines:
|
||||||
|
metrics:
|
||||||
|
receivers: [prometheus]
|
||||||
|
processors: [resourcedetection, batch]
|
||||||
|
exporters: [otlp]
|
||||||
@@ -181,7 +181,7 @@ services:
|
|||||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||||
signoz:
|
signoz:
|
||||||
!!merge <<: *db-depend
|
!!merge <<: *db-depend
|
||||||
image: signoz/signoz:${VERSION:-v0.116.0}
|
image: signoz/signoz:${VERSION:-v0.115.0}
|
||||||
container_name: signoz
|
container_name: signoz
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080" # signoz port
|
- "8080:8080" # signoz port
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ services:
|
|||||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||||
signoz:
|
signoz:
|
||||||
!!merge <<: *db-depend
|
!!merge <<: *db-depend
|
||||||
image: signoz/signoz:${VERSION:-v0.116.0}
|
image: signoz/signoz:${VERSION:-v0.115.0}
|
||||||
container_name: signoz
|
container_name: signoz
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080" # signoz port
|
- "8080:8080" # signoz port
|
||||||
|
|||||||
39
deploy/docker/generator/hotrod/docker-compose.yaml
Normal file
39
deploy/docker/generator/hotrod/docker-compose.yaml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
version: "3"
|
||||||
|
x-common: &common
|
||||||
|
networks:
|
||||||
|
- signoz-net
|
||||||
|
extra_hosts:
|
||||||
|
- host.docker.internal:host-gateway
|
||||||
|
logging:
|
||||||
|
options:
|
||||||
|
max-size: 50m
|
||||||
|
max-file: "3"
|
||||||
|
restart: unless-stopped
|
||||||
|
services:
|
||||||
|
hotrod:
|
||||||
|
<<: *common
|
||||||
|
image: jaegertracing/example-hotrod:1.61.0
|
||||||
|
container_name: hotrod
|
||||||
|
command: [ "all" ]
|
||||||
|
environment:
|
||||||
|
- OTEL_EXPORTER_OTLP_ENDPOINT=http://host.docker.internal:4318 # In case of external SigNoz or cloud, update the endpoint and access token
|
||||||
|
# - OTEL_OTLP_HEADERS=signoz-access-token=<your-access-token>
|
||||||
|
load-hotrod:
|
||||||
|
<<: *common
|
||||||
|
image: "signoz/locust:1.2.3"
|
||||||
|
container_name: load-hotrod
|
||||||
|
environment:
|
||||||
|
ATTACKED_HOST: http://hotrod:8080
|
||||||
|
LOCUST_MODE: standalone
|
||||||
|
NO_PROXY: standalone
|
||||||
|
TASK_DELAY_FROM: 5
|
||||||
|
TASK_DELAY_TO: 30
|
||||||
|
QUIET_MODE: "${QUIET_MODE:-false}"
|
||||||
|
LOCUST_OPTS: "--headless -u 10 -r 1"
|
||||||
|
volumes:
|
||||||
|
- ../../../common/locust-scripts:/locust
|
||||||
|
|
||||||
|
networks:
|
||||||
|
signoz-net:
|
||||||
|
name: signoz-net
|
||||||
|
external: true
|
||||||
43
deploy/docker/generator/infra/docker-compose.yaml
Normal file
43
deploy/docker/generator/infra/docker-compose.yaml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
version: "3"
|
||||||
|
x-common: &common
|
||||||
|
networks:
|
||||||
|
- signoz-net
|
||||||
|
extra_hosts:
|
||||||
|
- host.docker.internal:host-gateway
|
||||||
|
logging:
|
||||||
|
options:
|
||||||
|
max-size: 50m
|
||||||
|
max-file: "3"
|
||||||
|
restart: unless-stopped
|
||||||
|
services:
|
||||||
|
otel-agent:
|
||||||
|
<<: *common
|
||||||
|
image: otel/opentelemetry-collector-contrib:0.111.0
|
||||||
|
command:
|
||||||
|
- --config=/etc/otel-collector-config.yaml
|
||||||
|
volumes:
|
||||||
|
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
||||||
|
- /:/hostfs:ro
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
environment:
|
||||||
|
- SIGNOZ_COLLECTOR_ENDPOINT=http://host.docker.internal:4317 # In case of external SigNoz or cloud, update the endpoint and access token
|
||||||
|
- OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux # Replace signoz-host with the actual hostname
|
||||||
|
# - SIGNOZ_ACCESS_TOKEN="<your-access-token>"
|
||||||
|
# Before exposing the ports, make sure the ports are not used by other services
|
||||||
|
# ports:
|
||||||
|
# - "4317:4317"
|
||||||
|
# - "4318:4318"
|
||||||
|
logspout:
|
||||||
|
<<: *common
|
||||||
|
image: "gliderlabs/logspout:v3.2.14"
|
||||||
|
volumes:
|
||||||
|
- /etc/hostname:/etc/host_hostname:ro
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
command: syslog+tcp://otel-agent:2255
|
||||||
|
depends_on:
|
||||||
|
- otel-agent
|
||||||
|
|
||||||
|
networks:
|
||||||
|
signoz-net:
|
||||||
|
name: signoz-net
|
||||||
|
external: true
|
||||||
139
deploy/docker/generator/infra/otel-collector-config.yaml
Normal file
139
deploy/docker/generator/infra/otel-collector-config.yaml
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
receivers:
|
||||||
|
hostmetrics:
|
||||||
|
collection_interval: 30s
|
||||||
|
root_path: /hostfs
|
||||||
|
scrapers:
|
||||||
|
cpu: {}
|
||||||
|
load: {}
|
||||||
|
memory: {}
|
||||||
|
disk: {}
|
||||||
|
filesystem: {}
|
||||||
|
network: {}
|
||||||
|
otlp:
|
||||||
|
protocols:
|
||||||
|
grpc:
|
||||||
|
endpoint: 0.0.0.0:4317
|
||||||
|
http:
|
||||||
|
endpoint: 0.0.0.0:4318
|
||||||
|
prometheus:
|
||||||
|
config:
|
||||||
|
global:
|
||||||
|
scrape_interval: 60s
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: otel-collector
|
||||||
|
static_configs:
|
||||||
|
- targets:
|
||||||
|
- localhost:8888
|
||||||
|
labels:
|
||||||
|
job_name: otel-collector
|
||||||
|
# For Docker daemon metrics to be scraped, it must be configured to expose
|
||||||
|
# Prometheus metrics, as documented here: https://docs.docker.com/config/daemon/prometheus/
|
||||||
|
# - job_name: docker-daemon
|
||||||
|
# static_configs:
|
||||||
|
# - targets:
|
||||||
|
# - host.docker.internal:9323
|
||||||
|
# labels:
|
||||||
|
# job_name: docker-daemon
|
||||||
|
- job_name: docker-container
|
||||||
|
docker_sd_configs:
|
||||||
|
- host: unix:///var/run/docker.sock
|
||||||
|
relabel_configs:
|
||||||
|
- action: keep
|
||||||
|
regex: true
|
||||||
|
source_labels:
|
||||||
|
- __meta_docker_container_label_signoz_io_scrape
|
||||||
|
- regex: true
|
||||||
|
source_labels:
|
||||||
|
- __meta_docker_container_label_signoz_io_path
|
||||||
|
target_label: __metrics_path__
|
||||||
|
- regex: (.+)
|
||||||
|
source_labels:
|
||||||
|
- __meta_docker_container_label_signoz_io_path
|
||||||
|
target_label: __metrics_path__
|
||||||
|
- separator: ":"
|
||||||
|
source_labels:
|
||||||
|
- __meta_docker_network_ip
|
||||||
|
- __meta_docker_container_label_signoz_io_port
|
||||||
|
target_label: __address__
|
||||||
|
- regex: '/(.*)'
|
||||||
|
replacement: '$1'
|
||||||
|
source_labels:
|
||||||
|
- __meta_docker_container_name
|
||||||
|
target_label: container_name
|
||||||
|
- regex: __meta_docker_container_label_signoz_io_(.+)
|
||||||
|
action: labelmap
|
||||||
|
replacement: $1
|
||||||
|
tcplog/docker:
|
||||||
|
listen_address: "0.0.0.0:2255"
|
||||||
|
operators:
|
||||||
|
- type: regex_parser
|
||||||
|
regex: '^<([0-9]+)>[0-9]+ (?P<timestamp>[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?) (?P<container_id>\S+) (?P<container_name>\S+) [0-9]+ - -( (?P<body>.*))?'
|
||||||
|
timestamp:
|
||||||
|
parse_from: attributes.timestamp
|
||||||
|
layout: '%Y-%m-%dT%H:%M:%S.%LZ'
|
||||||
|
- type: move
|
||||||
|
from: attributes["body"]
|
||||||
|
to: body
|
||||||
|
- type: remove
|
||||||
|
field: attributes.timestamp
|
||||||
|
# please remove names from below if you want to collect logs from them
|
||||||
|
- type: filter
|
||||||
|
id: signoz_logs_filter
|
||||||
|
expr: 'attributes.container_name matches "^signoz|(signoz-(|otel-collector|clickhouse|zookeeper))|(infra-(logspout|otel-agent)-.*)"'
|
||||||
|
processors:
|
||||||
|
batch:
|
||||||
|
send_batch_size: 10000
|
||||||
|
send_batch_max_size: 11000
|
||||||
|
timeout: 10s
|
||||||
|
resourcedetection:
|
||||||
|
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
|
||||||
|
detectors:
|
||||||
|
# - ec2
|
||||||
|
# - gcp
|
||||||
|
# - azure
|
||||||
|
- env
|
||||||
|
- system
|
||||||
|
timeout: 2s
|
||||||
|
extensions:
|
||||||
|
health_check:
|
||||||
|
endpoint: 0.0.0.0:13133
|
||||||
|
pprof:
|
||||||
|
endpoint: 0.0.0.0:1777
|
||||||
|
exporters:
|
||||||
|
otlp:
|
||||||
|
endpoint: ${env:SIGNOZ_COLLECTOR_ENDPOINT}
|
||||||
|
tls:
|
||||||
|
insecure: true
|
||||||
|
headers:
|
||||||
|
signoz-access-token: ${env:SIGNOZ_ACCESS_TOKEN}
|
||||||
|
# debug: {}
|
||||||
|
service:
|
||||||
|
telemetry:
|
||||||
|
logs:
|
||||||
|
encoding: json
|
||||||
|
metrics:
|
||||||
|
address: 0.0.0.0:8888
|
||||||
|
extensions:
|
||||||
|
- health_check
|
||||||
|
- pprof
|
||||||
|
pipelines:
|
||||||
|
traces:
|
||||||
|
receivers: [otlp]
|
||||||
|
processors: [resourcedetection, batch]
|
||||||
|
exporters: [otlp]
|
||||||
|
metrics:
|
||||||
|
receivers: [otlp]
|
||||||
|
processors: [resourcedetection, batch]
|
||||||
|
exporters: [otlp]
|
||||||
|
metrics/hostmetrics:
|
||||||
|
receivers: [hostmetrics]
|
||||||
|
processors: [resourcedetection, batch]
|
||||||
|
exporters: [otlp]
|
||||||
|
metrics/prometheus:
|
||||||
|
receivers: [prometheus]
|
||||||
|
processors: [resourcedetection, batch]
|
||||||
|
exporters: [otlp]
|
||||||
|
logs:
|
||||||
|
receivers: [otlp, tcplog/docker]
|
||||||
|
processors: [resourcedetection, batch]
|
||||||
|
exporters: [otlp]
|
||||||
@@ -220,13 +220,6 @@ components:
|
|||||||
- additions
|
- additions
|
||||||
- deletions
|
- deletions
|
||||||
type: object
|
type: object
|
||||||
AuthtypesPatchableRole:
|
|
||||||
properties:
|
|
||||||
description:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- description
|
|
||||||
type: object
|
|
||||||
AuthtypesPostableAuthDomain:
|
AuthtypesPostableAuthDomain:
|
||||||
properties:
|
properties:
|
||||||
config:
|
config:
|
||||||
@@ -243,15 +236,6 @@ components:
|
|||||||
password:
|
password:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
AuthtypesPostableRole:
|
|
||||||
properties:
|
|
||||||
description:
|
|
||||||
type: string
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- name
|
|
||||||
type: object
|
|
||||||
AuthtypesPostableRotateToken:
|
AuthtypesPostableRotateToken:
|
||||||
properties:
|
properties:
|
||||||
refreshToken:
|
refreshToken:
|
||||||
@@ -267,31 +251,6 @@ components:
|
|||||||
- name
|
- name
|
||||||
- type
|
- type
|
||||||
type: object
|
type: object
|
||||||
AuthtypesRole:
|
|
||||||
properties:
|
|
||||||
createdAt:
|
|
||||||
format: date-time
|
|
||||||
type: string
|
|
||||||
description:
|
|
||||||
type: string
|
|
||||||
id:
|
|
||||||
type: string
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
orgId:
|
|
||||||
type: string
|
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
updatedAt:
|
|
||||||
format: date-time
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- id
|
|
||||||
- name
|
|
||||||
- description
|
|
||||||
- type
|
|
||||||
- orgId
|
|
||||||
type: object
|
|
||||||
AuthtypesRoleMapping:
|
AuthtypesRoleMapping:
|
||||||
properties:
|
properties:
|
||||||
defaultRole:
|
defaultRole:
|
||||||
@@ -1763,6 +1722,47 @@ components:
|
|||||||
- status
|
- status
|
||||||
- error
|
- error
|
||||||
type: object
|
type: object
|
||||||
|
RoletypesPatchableRole:
|
||||||
|
properties:
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- description
|
||||||
|
type: object
|
||||||
|
RoletypesPostableRole:
|
||||||
|
properties:
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
type: object
|
||||||
|
RoletypesRole:
|
||||||
|
properties:
|
||||||
|
createdAt:
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
orgId:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
updatedAt:
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- name
|
||||||
|
- description
|
||||||
|
- type
|
||||||
|
- orgId
|
||||||
|
type: object
|
||||||
ServiceaccounttypesFactorAPIKey:
|
ServiceaccounttypesFactorAPIKey:
|
||||||
properties:
|
properties:
|
||||||
createdAt:
|
createdAt:
|
||||||
@@ -4234,7 +4234,7 @@ paths:
|
|||||||
properties:
|
properties:
|
||||||
data:
|
data:
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/AuthtypesRole'
|
$ref: '#/components/schemas/RoletypesRole'
|
||||||
type: array
|
type: array
|
||||||
status:
|
status:
|
||||||
type: string
|
type: string
|
||||||
@@ -4277,7 +4277,7 @@ paths:
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/AuthtypesPostableRole'
|
$ref: '#/components/schemas/RoletypesPostableRole'
|
||||||
responses:
|
responses:
|
||||||
"201":
|
"201":
|
||||||
content:
|
content:
|
||||||
@@ -4422,7 +4422,7 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
properties:
|
properties:
|
||||||
data:
|
data:
|
||||||
$ref: '#/components/schemas/AuthtypesRole'
|
$ref: '#/components/schemas/RoletypesRole'
|
||||||
status:
|
status:
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
@@ -4470,7 +4470,7 @@ paths:
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/AuthtypesPatchableRole'
|
$ref: '#/components/schemas/RoletypesPatchableRole'
|
||||||
responses:
|
responses:
|
||||||
"204":
|
"204":
|
||||||
content:
|
content:
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/licensing"
|
"github.com/SigNoz/signoz/pkg/licensing"
|
||||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/roletypes"
|
||||||
"github.com/SigNoz/signoz/pkg/valuer"
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
||||||
openfgapkgtransformer "github.com/openfga/language/pkg/go/transformer"
|
openfgapkgtransformer "github.com/openfga/language/pkg/go/transformer"
|
||||||
@@ -22,7 +23,7 @@ type provider struct {
|
|||||||
pkgAuthzService authz.AuthZ
|
pkgAuthzService authz.AuthZ
|
||||||
openfgaServer *openfgaserver.Server
|
openfgaServer *openfgaserver.Server
|
||||||
licensing licensing.Licensing
|
licensing licensing.Licensing
|
||||||
store authtypes.RoleStore
|
store roletypes.Store
|
||||||
registry []authz.RegisterTypeable
|
registry []authz.RegisterTypeable
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,23 +82,23 @@ func (provider *provider) Write(ctx context.Context, additions []*openfgav1.Tupl
|
|||||||
return provider.openfgaServer.Write(ctx, additions, deletions)
|
return provider.openfgaServer.Write(ctx, additions, deletions)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) Get(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*authtypes.Role, error) {
|
func (provider *provider) Get(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*roletypes.Role, error) {
|
||||||
return provider.pkgAuthzService.Get(ctx, orgID, id)
|
return provider.pkgAuthzService.Get(ctx, orgID, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) GetByOrgIDAndName(ctx context.Context, orgID valuer.UUID, name string) (*authtypes.Role, error) {
|
func (provider *provider) GetByOrgIDAndName(ctx context.Context, orgID valuer.UUID, name string) (*roletypes.Role, error) {
|
||||||
return provider.pkgAuthzService.GetByOrgIDAndName(ctx, orgID, name)
|
return provider.pkgAuthzService.GetByOrgIDAndName(ctx, orgID, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) List(ctx context.Context, orgID valuer.UUID) ([]*authtypes.Role, error) {
|
func (provider *provider) List(ctx context.Context, orgID valuer.UUID) ([]*roletypes.Role, error) {
|
||||||
return provider.pkgAuthzService.List(ctx, orgID)
|
return provider.pkgAuthzService.List(ctx, orgID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) ListByOrgIDAndNames(ctx context.Context, orgID valuer.UUID, names []string) ([]*authtypes.Role, error) {
|
func (provider *provider) ListByOrgIDAndNames(ctx context.Context, orgID valuer.UUID, names []string) ([]*roletypes.Role, error) {
|
||||||
return provider.pkgAuthzService.ListByOrgIDAndNames(ctx, orgID, names)
|
return provider.pkgAuthzService.ListByOrgIDAndNames(ctx, orgID, names)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) ListByOrgIDAndIDs(ctx context.Context, orgID valuer.UUID, ids []valuer.UUID) ([]*authtypes.Role, error) {
|
func (provider *provider) ListByOrgIDAndIDs(ctx context.Context, orgID valuer.UUID, ids []valuer.UUID) ([]*roletypes.Role, error) {
|
||||||
return provider.pkgAuthzService.ListByOrgIDAndIDs(ctx, orgID, ids)
|
return provider.pkgAuthzService.ListByOrgIDAndIDs(ctx, orgID, ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +114,7 @@ func (provider *provider) Revoke(ctx context.Context, orgID valuer.UUID, names [
|
|||||||
return provider.pkgAuthzService.Revoke(ctx, orgID, names, subject)
|
return provider.pkgAuthzService.Revoke(ctx, orgID, names, subject)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) CreateManagedRoles(ctx context.Context, orgID valuer.UUID, managedRoles []*authtypes.Role) error {
|
func (provider *provider) CreateManagedRoles(ctx context.Context, orgID valuer.UUID, managedRoles []*roletypes.Role) error {
|
||||||
return provider.pkgAuthzService.CreateManagedRoles(ctx, orgID, managedRoles)
|
return provider.pkgAuthzService.CreateManagedRoles(ctx, orgID, managedRoles)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,16 +136,16 @@ func (provider *provider) CreateManagedUserRoleTransactions(ctx context.Context,
|
|||||||
return provider.Write(ctx, tuples, nil)
|
return provider.Write(ctx, tuples, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) Create(ctx context.Context, orgID valuer.UUID, role *authtypes.Role) error {
|
func (provider *provider) Create(ctx context.Context, orgID valuer.UUID, role *roletypes.Role) error {
|
||||||
_, err := provider.licensing.GetActive(ctx, orgID)
|
_, err := provider.licensing.GetActive(ctx, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "a valid license is not available").WithAdditional("this feature requires a valid license").WithAdditional(err.Error())
|
return errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "a valid license is not available").WithAdditional("this feature requires a valid license").WithAdditional(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return provider.store.Create(ctx, authtypes.NewStorableRoleFromRole(role))
|
return provider.store.Create(ctx, roletypes.NewStorableRoleFromRole(role))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) GetOrCreate(ctx context.Context, orgID valuer.UUID, role *authtypes.Role) (*authtypes.Role, error) {
|
func (provider *provider) GetOrCreate(ctx context.Context, orgID valuer.UUID, role *roletypes.Role) (*roletypes.Role, error) {
|
||||||
_, err := provider.licensing.GetActive(ctx, orgID)
|
_, err := provider.licensing.GetActive(ctx, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "a valid license is not available").WithAdditional("this feature requires a valid license").WithAdditional(err.Error())
|
return nil, errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "a valid license is not available").WithAdditional("this feature requires a valid license").WithAdditional(err.Error())
|
||||||
@@ -158,10 +159,10 @@ func (provider *provider) GetOrCreate(ctx context.Context, orgID valuer.UUID, ro
|
|||||||
}
|
}
|
||||||
|
|
||||||
if existingRole != nil {
|
if existingRole != nil {
|
||||||
return authtypes.NewRoleFromStorableRole(existingRole), nil
|
return roletypes.NewRoleFromStorableRole(existingRole), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = provider.store.Create(ctx, authtypes.NewStorableRoleFromRole(role))
|
err = provider.store.Create(ctx, roletypes.NewStorableRoleFromRole(role))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -216,13 +217,13 @@ func (provider *provider) GetObjects(ctx context.Context, orgID valuer.UUID, id
|
|||||||
return objects, nil
|
return objects, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) Patch(ctx context.Context, orgID valuer.UUID, role *authtypes.Role) error {
|
func (provider *provider) Patch(ctx context.Context, orgID valuer.UUID, role *roletypes.Role) error {
|
||||||
_, err := provider.licensing.GetActive(ctx, orgID)
|
_, err := provider.licensing.GetActive(ctx, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "a valid license is not available").WithAdditional("this feature requires a valid license").WithAdditional(err.Error())
|
return errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "a valid license is not available").WithAdditional("this feature requires a valid license").WithAdditional(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return provider.store.Update(ctx, orgID, authtypes.NewStorableRoleFromRole(role))
|
return provider.store.Update(ctx, orgID, roletypes.NewStorableRoleFromRole(role))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) PatchObjects(ctx context.Context, orgID valuer.UUID, name string, relation authtypes.Relation, additions, deletions []*authtypes.Object) error {
|
func (provider *provider) PatchObjects(ctx context.Context, orgID valuer.UUID, name string, relation authtypes.Relation, additions, deletions []*authtypes.Object) error {
|
||||||
@@ -231,12 +232,12 @@ func (provider *provider) PatchObjects(ctx context.Context, orgID valuer.UUID, n
|
|||||||
return errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "a valid license is not available").WithAdditional("this feature requires a valid license").WithAdditional(err.Error())
|
return errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "a valid license is not available").WithAdditional("this feature requires a valid license").WithAdditional(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
additionTuples, err := authtypes.GetAdditionTuples(name, orgID, relation, additions)
|
additionTuples, err := roletypes.GetAdditionTuples(name, orgID, relation, additions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
deletionTuples, err := authtypes.GetDeletionTuples(name, orgID, relation, deletions)
|
deletionTuples, err := roletypes.GetDeletionTuples(name, orgID, relation, deletions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -260,7 +261,7 @@ func (provider *provider) Delete(ctx context.Context, orgID valuer.UUID, id valu
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
role := authtypes.NewRoleFromStorableRole(storableRole)
|
role := roletypes.NewRoleFromStorableRole(storableRole)
|
||||||
err = role.ErrIfManaged()
|
err = role.ErrIfManaged()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -270,7 +271,7 @@ func (provider *provider) Delete(ctx context.Context, orgID valuer.UUID, id valu
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) MustGetTypeables() []authtypes.Typeable {
|
func (provider *provider) MustGetTypeables() []authtypes.Typeable {
|
||||||
return []authtypes.Typeable{authtypes.TypeableRole, authtypes.TypeableResourcesRoles}
|
return []authtypes.Typeable{authtypes.TypeableRole, roletypes.TypeableResourcesRoles}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) getManagedRoleGrantTuples(orgID valuer.UUID, userID valuer.UUID) ([]*openfgav1.TupleKey, error) {
|
func (provider *provider) getManagedRoleGrantTuples(orgID valuer.UUID, userID valuer.UUID) ([]*openfgav1.TupleKey, error) {
|
||||||
@@ -282,7 +283,7 @@ func (provider *provider) getManagedRoleGrantTuples(orgID valuer.UUID, userID va
|
|||||||
adminSubject,
|
adminSubject,
|
||||||
authtypes.RelationAssignee,
|
authtypes.RelationAssignee,
|
||||||
[]authtypes.Selector{
|
[]authtypes.Selector{
|
||||||
authtypes.MustNewSelector(authtypes.TypeRole, authtypes.SigNozAdminRoleName),
|
authtypes.MustNewSelector(authtypes.TypeRole, roletypes.SigNozAdminRoleName),
|
||||||
},
|
},
|
||||||
orgID,
|
orgID,
|
||||||
)
|
)
|
||||||
@@ -297,7 +298,7 @@ func (provider *provider) getManagedRoleGrantTuples(orgID valuer.UUID, userID va
|
|||||||
anonymousSubject,
|
anonymousSubject,
|
||||||
authtypes.RelationAssignee,
|
authtypes.RelationAssignee,
|
||||||
[]authtypes.Selector{
|
[]authtypes.Selector{
|
||||||
authtypes.MustNewSelector(authtypes.TypeRole, authtypes.SigNozAnonymousRoleName),
|
authtypes.MustNewSelector(authtypes.TypeRole, roletypes.SigNozAnonymousRoleName),
|
||||||
},
|
},
|
||||||
orgID,
|
orgID,
|
||||||
)
|
)
|
||||||
|
|||||||
22
ee/modules/cloudintegration/implcloudintegration/aws.go
Normal file
22
ee/modules/cloudintegration/implcloudintegration/aws.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package implcloudintegration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/cloudintegration"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/cloudintegrationtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type awsProvider struct{}
|
||||||
|
|
||||||
|
func (p *awsProvider) CreateArtifact(
|
||||||
|
_ context.Context,
|
||||||
|
_ valuer.UUID,
|
||||||
|
_ *cloudintegrationtypes.ConnectionArtifactRequest,
|
||||||
|
_ cloudintegration.Credentials,
|
||||||
|
_ valuer.UUID,
|
||||||
|
) (*cloudintegrationtypes.ConnectionArtifact, error) {
|
||||||
|
return nil, errors.New(errors.TypeUnsupported, errors.CodeUnsupported, "not implemented")
|
||||||
|
}
|
||||||
23
ee/modules/cloudintegration/implcloudintegration/azure.go
Normal file
23
ee/modules/cloudintegration/implcloudintegration/azure.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package implcloudintegration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/cloudintegration"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/cloudintegrationtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// keeping this for example to show how more cloud providers will be added
|
||||||
|
type azureProvider struct{}
|
||||||
|
|
||||||
|
func (p *azureProvider) CreateArtifact(
|
||||||
|
_ context.Context,
|
||||||
|
_ valuer.UUID,
|
||||||
|
_ *cloudintegrationtypes.ConnectionArtifactRequest,
|
||||||
|
_ cloudintegration.Credentials,
|
||||||
|
_ valuer.UUID,
|
||||||
|
) (*cloudintegrationtypes.ConnectionArtifact, error) {
|
||||||
|
return nil, errors.New(errors.TypeUnsupported, errors.CodeUnsupported, "not implemented")
|
||||||
|
}
|
||||||
267
ee/modules/cloudintegration/implcloudintegration/module.go
Normal file
267
ee/modules/cloudintegration/implcloudintegration/module.go
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
package implcloudintegration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
|
"github.com/SigNoz/signoz/pkg/gateway"
|
||||||
|
"github.com/SigNoz/signoz/pkg/global"
|
||||||
|
"github.com/SigNoz/signoz/pkg/licensing"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/cloudintegration"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/user/impluser"
|
||||||
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/cloudintegrationtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/dashboardtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/zeustypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
"github.com/SigNoz/signoz/pkg/zeus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type module struct {
|
||||||
|
store cloudintegrationtypes.Store
|
||||||
|
userStore types.UserStore
|
||||||
|
licensing licensing.Licensing
|
||||||
|
zeus zeus.Zeus
|
||||||
|
gateway gateway.Gateway
|
||||||
|
globalConfig global.Config
|
||||||
|
providers map[cloudintegrationtypes.CloudProviderType]cloudintegration.CloudProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewModule(
|
||||||
|
store cloudintegrationtypes.Store,
|
||||||
|
sqlStore sqlstore.SQLStore,
|
||||||
|
lic licensing.Licensing,
|
||||||
|
z zeus.Zeus,
|
||||||
|
gw gateway.Gateway,
|
||||||
|
gc global.Config,
|
||||||
|
) cloudintegration.Module {
|
||||||
|
return &module{
|
||||||
|
store: store,
|
||||||
|
userStore: impluser.NewStore(sqlStore, factory.ProviderSettings{}),
|
||||||
|
licensing: lic,
|
||||||
|
zeus: z,
|
||||||
|
gateway: gw,
|
||||||
|
globalConfig: gc,
|
||||||
|
providers: map[cloudintegrationtypes.CloudProviderType]cloudintegration.CloudProvider{
|
||||||
|
cloudintegrationtypes.CloudProviderTypeAWS: &awsProvider{},
|
||||||
|
cloudintegrationtypes.CloudProviderTypeAzure: &azureProvider{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *module) CreateConnectionArtifact(ctx context.Context, orgID valuer.UUID, provider cloudintegrationtypes.CloudProviderType, request *cloudintegrationtypes.ConnectionArtifactRequest) (*cloudintegrationtypes.ConnectionArtifact, error) {
|
||||||
|
p, ok := m.providers[provider]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.NewInvalidInputf(cloudintegrationtypes.ErrCodeCloudProviderInvalidInput, "unsupported cloud provider: %s", provider.StringValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
creds, err := m.resolveCredentials(ctx, orgID, provider)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newAccountID := valuer.GenerateUUID()
|
||||||
|
|
||||||
|
artifact, err := p.CreateArtifact(ctx, orgID, request, creds, newAccountID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
account := &cloudintegrationtypes.StorableCloudIntegration{
|
||||||
|
Identifiable: types.Identifiable{ID: newAccountID},
|
||||||
|
TimeAuditable: types.TimeAuditable{
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
},
|
||||||
|
Provider: provider,
|
||||||
|
OrgID: orgID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.store.UpsertAccount(ctx, account); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return artifact, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *module) resolveCredentials(ctx context.Context, orgID valuer.UUID, provider cloudintegrationtypes.CloudProviderType) (cloudintegration.Credentials, error) {
|
||||||
|
creds := cloudintegration.Credentials{}
|
||||||
|
|
||||||
|
pat, err := m.getOrCreateIntegrationPAT(ctx, orgID, provider)
|
||||||
|
if err != nil {
|
||||||
|
return creds, err
|
||||||
|
}
|
||||||
|
creds.SigNozAPIKey = pat
|
||||||
|
|
||||||
|
if m.licensing == nil {
|
||||||
|
return creds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
license, err := m.licensing.GetActive(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return creds, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if license == nil {
|
||||||
|
return creds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
respBytes, err := m.zeus.GetDeployment(ctx, license.Key)
|
||||||
|
if err != nil {
|
||||||
|
return creds, errors.NewInternalf(errors.CodeInternal, "couldn't query deployment info: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
deployment, err := zeustypes.NewGettableDeployment(respBytes)
|
||||||
|
if err != nil {
|
||||||
|
return creds, err
|
||||||
|
}
|
||||||
|
creds.SigNozAPIUrl = deployment.SignozAPIUrl
|
||||||
|
|
||||||
|
if m.globalConfig.IngestionURL != nil {
|
||||||
|
creds.IngestionUrl = m.globalConfig.IngestionURL.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.gateway != nil {
|
||||||
|
ingestionKeyName := fmt.Sprintf("%s-integration", provider.StringValue())
|
||||||
|
ingestionKey, err := m.getOrCreateIngestionKey(ctx, orgID, ingestionKeyName)
|
||||||
|
if err != nil {
|
||||||
|
return creds, err
|
||||||
|
}
|
||||||
|
creds.IngestionKey = ingestionKey
|
||||||
|
}
|
||||||
|
|
||||||
|
return creds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *module) getOrCreateIngestionKey(ctx context.Context, orgID valuer.UUID, keyName string) (string, error) {
|
||||||
|
result, err := m.gateway.SearchIngestionKeysByName(ctx, orgID, keyName, 1, 10)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.NewInternalf(errors.CodeInternal, "couldn't search ingestion keys: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, k := range result.Keys {
|
||||||
|
if k.Name == keyName {
|
||||||
|
return k.Value, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
created, err := m.gateway.CreateIngestionKey(ctx, orgID, keyName, []string{"integration"}, time.Time{})
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.NewInternalf(errors.CodeInternal, "couldn't create ingestion key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return created.Value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *module) getOrCreateIntegrationPAT(ctx context.Context, orgID valuer.UUID, provider cloudintegrationtypes.CloudProviderType) (string, error) {
|
||||||
|
integrationPATName := fmt.Sprintf("%s integration", provider.StringValue())
|
||||||
|
|
||||||
|
integrationUser, err := m.getOrCreateIntegrationUser(ctx, orgID, provider)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
allPATs, err := m.userStore.ListAPIKeys(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.NewInternalf(errors.CodeInternal, "couldn't list PATs: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range allPATs {
|
||||||
|
if p.UserID == integrationUser.ID && p.Name == integrationPATName {
|
||||||
|
return p.Token, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newPAT, err := types.NewStorableAPIKey(
|
||||||
|
integrationPATName,
|
||||||
|
integrationUser.ID,
|
||||||
|
types.RoleViewer,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.NewInternalf(errors.CodeInternal, "couldn't create cloud integration PAT: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.userStore.CreateAPIKey(ctx, newPAT); err != nil {
|
||||||
|
return "", errors.NewInternalf(errors.CodeInternal, "couldn't persist cloud integration PAT: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPAT.Token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *module) getOrCreateIntegrationUser(ctx context.Context, orgID valuer.UUID, provider cloudintegrationtypes.CloudProviderType) (*types.User, error) {
|
||||||
|
cloudIntegrationUserName := fmt.Sprintf("%s-integration", provider.StringValue())
|
||||||
|
email := valuer.MustNewEmail(fmt.Sprintf("%s@signoz.io", cloudIntegrationUserName))
|
||||||
|
|
||||||
|
existingUsers, err := m.userStore.GetUsersByEmailAndOrgID(ctx, email, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.NewInternalf(errors.CodeInternal, "couldn't look up integration user: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, u := range existingUsers {
|
||||||
|
if u.Status != types.UserStatusDeleted {
|
||||||
|
return u, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cloudIntegrationUser, err := types.NewUser(cloudIntegrationUserName, email, types.RoleViewer, orgID, types.UserStatusActive)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.NewInternalf(errors.CodeInternal, "couldn't construct integration user: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
password := types.MustGenerateFactorPassword(cloudIntegrationUser.ID.StringValue())
|
||||||
|
|
||||||
|
if err := m.userStore.CreateUser(ctx, cloudIntegrationUser); err != nil {
|
||||||
|
return nil, errors.NewInternalf(errors.CodeInternal, "couldn't create integration user: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.userStore.CreatePassword(ctx, password); err != nil {
|
||||||
|
return nil, errors.NewInternalf(errors.CodeInternal, "couldn't create integration user password: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cloudIntegrationUser, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *module) GetAccountStatus(_ context.Context, _, _ valuer.UUID) (*cloudintegrationtypes.AccountStatus, error) {
|
||||||
|
return nil, errors.New(errors.TypeUnsupported, errors.CodeUnsupported, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *module) ListConnectedAccounts(_ context.Context, _ valuer.UUID) (*cloudintegrationtypes.ConnectedAccounts, error) {
|
||||||
|
return nil, errors.New(errors.TypeUnsupported, errors.CodeUnsupported, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *module) DisconnectAccount(_ context.Context, _, _ valuer.UUID) error {
|
||||||
|
return errors.New(errors.TypeUnsupported, errors.CodeUnsupported, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *module) UpdateAccountConfig(_ context.Context, _, _ valuer.UUID, _ *cloudintegrationtypes.UpdateAccountConfigRequest) (*cloudintegrationtypes.Account, error) {
|
||||||
|
return nil, errors.New(errors.TypeUnsupported, errors.CodeUnsupported, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *module) ListServicesSummary(_ context.Context, _ valuer.UUID, _ *valuer.UUID) (*cloudintegrationtypes.ServicesSummary, error) {
|
||||||
|
return nil, errors.New(errors.TypeUnsupported, errors.CodeUnsupported, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *module) GetService(_ context.Context, _ valuer.UUID, _ string, _ *valuer.UUID) (*cloudintegrationtypes.Service, error) {
|
||||||
|
return nil, errors.New(errors.TypeUnsupported, errors.CodeUnsupported, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *module) UpdateServiceConfig(_ context.Context, _ string, _ valuer.UUID, _ *cloudintegrationtypes.UpdateServiceConfigRequest) (*cloudintegrationtypes.ServiceSummary, error) {
|
||||||
|
return nil, errors.New(errors.TypeUnsupported, errors.CodeUnsupported, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *module) AgentCheckIn(_ context.Context, _ valuer.UUID, _ *cloudintegrationtypes.AgentCheckInRequest) (cloudintegrationtypes.AgentCheckInResponse, error) {
|
||||||
|
return cloudintegrationtypes.AgentCheckInResponse{}, errors.New(errors.TypeUnsupported, errors.CodeUnsupported, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *module) GetDashboardByID(_ context.Context, _ string, _ valuer.UUID) (*dashboardtypes.Dashboard, error) {
|
||||||
|
return nil, errors.New(errors.TypeUnsupported, errors.CodeUnsupported, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *module) GetAllDashboards(_ context.Context, _ valuer.UUID) ([]*dashboardtypes.Dashboard, error) {
|
||||||
|
return nil, errors.New(errors.TypeUnsupported, errors.CodeUnsupported, "not implemented")
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/types/dashboardtypes"
|
"github.com/SigNoz/signoz/pkg/types/dashboardtypes"
|
||||||
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
|
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
|
||||||
"github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
"github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/roletypes"
|
||||||
"github.com/SigNoz/signoz/pkg/valuer"
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -223,7 +224,7 @@ func (module *module) MustGetTypeables() []authtypes.Typeable {
|
|||||||
|
|
||||||
func (module *module) MustGetManagedRoleTransactions() map[string][]*authtypes.Transaction {
|
func (module *module) MustGetManagedRoleTransactions() map[string][]*authtypes.Transaction {
|
||||||
return map[string][]*authtypes.Transaction{
|
return map[string][]*authtypes.Transaction{
|
||||||
authtypes.SigNozAnonymousRoleName: {
|
roletypes.SigNozAnonymousRoleName: {
|
||||||
{
|
{
|
||||||
ID: valuer.GenerateUUID(),
|
ID: valuer.GenerateUUID(),
|
||||||
Relation: authtypes.RelationRead,
|
Relation: authtypes.RelationRead,
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package anomaly
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log/slog"
|
|
||||||
"math"
|
"math"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -14,6 +13,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||||
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
|
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
|
||||||
"github.com/SigNoz/signoz/pkg/valuer"
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -67,7 +67,7 @@ func (p *BaseSeasonalProvider) getResults(ctx context.Context, orgID valuer.UUID
|
|||||||
instrumentationtypes.CodeNamespace: "anomaly",
|
instrumentationtypes.CodeNamespace: "anomaly",
|
||||||
instrumentationtypes.CodeFunctionName: "getResults",
|
instrumentationtypes.CodeFunctionName: "getResults",
|
||||||
})
|
})
|
||||||
slog.InfoContext(ctx, "fetching results for current period", "current_period_query", params.CurrentPeriodQuery)
|
zap.L().Info("fetching results for current period", zap.Any("currentPeriodQuery", params.CurrentPeriodQuery))
|
||||||
currentPeriodResults, _, err := p.querierV2.QueryRange(ctx, orgID, params.CurrentPeriodQuery)
|
currentPeriodResults, _, err := p.querierV2.QueryRange(ctx, orgID, params.CurrentPeriodQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -78,7 +78,7 @@ func (p *BaseSeasonalProvider) getResults(ctx context.Context, orgID valuer.UUID
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.InfoContext(ctx, "fetching results for past period", "past_period_query", params.PastPeriodQuery)
|
zap.L().Info("fetching results for past period", zap.Any("pastPeriodQuery", params.PastPeriodQuery))
|
||||||
pastPeriodResults, _, err := p.querierV2.QueryRange(ctx, orgID, params.PastPeriodQuery)
|
pastPeriodResults, _, err := p.querierV2.QueryRange(ctx, orgID, params.PastPeriodQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -89,7 +89,7 @@ func (p *BaseSeasonalProvider) getResults(ctx context.Context, orgID valuer.UUID
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.InfoContext(ctx, "fetching results for current season", "current_season_query", params.CurrentSeasonQuery)
|
zap.L().Info("fetching results for current season", zap.Any("currentSeasonQuery", params.CurrentSeasonQuery))
|
||||||
currentSeasonResults, _, err := p.querierV2.QueryRange(ctx, orgID, params.CurrentSeasonQuery)
|
currentSeasonResults, _, err := p.querierV2.QueryRange(ctx, orgID, params.CurrentSeasonQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -100,7 +100,7 @@ func (p *BaseSeasonalProvider) getResults(ctx context.Context, orgID valuer.UUID
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.InfoContext(ctx, "fetching results for past season", "past_season_query", params.PastSeasonQuery)
|
zap.L().Info("fetching results for past season", zap.Any("pastSeasonQuery", params.PastSeasonQuery))
|
||||||
pastSeasonResults, _, err := p.querierV2.QueryRange(ctx, orgID, params.PastSeasonQuery)
|
pastSeasonResults, _, err := p.querierV2.QueryRange(ctx, orgID, params.PastSeasonQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -111,7 +111,7 @@ func (p *BaseSeasonalProvider) getResults(ctx context.Context, orgID valuer.UUID
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.InfoContext(ctx, "fetching results for past 2 season", "past_2_season_query", params.Past2SeasonQuery)
|
zap.L().Info("fetching results for past 2 season", zap.Any("past2SeasonQuery", params.Past2SeasonQuery))
|
||||||
past2SeasonResults, _, err := p.querierV2.QueryRange(ctx, orgID, params.Past2SeasonQuery)
|
past2SeasonResults, _, err := p.querierV2.QueryRange(ctx, orgID, params.Past2SeasonQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -122,7 +122,7 @@ func (p *BaseSeasonalProvider) getResults(ctx context.Context, orgID valuer.UUID
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.InfoContext(ctx, "fetching results for past 3 season", "past_3_season_query", params.Past3SeasonQuery)
|
zap.L().Info("fetching results for past 3 season", zap.Any("past3SeasonQuery", params.Past3SeasonQuery))
|
||||||
past3SeasonResults, _, err := p.querierV2.QueryRange(ctx, orgID, params.Past3SeasonQuery)
|
past3SeasonResults, _, err := p.querierV2.QueryRange(ctx, orgID, params.Past3SeasonQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -235,17 +235,17 @@ func (p *BaseSeasonalProvider) getPredictedSeries(
|
|||||||
if predictedValue < 0 {
|
if predictedValue < 0 {
|
||||||
// this should not happen (except when the data has extreme outliers)
|
// this should not happen (except when the data has extreme outliers)
|
||||||
// we will use the moving avg of the previous period series in this case
|
// we will use the moving avg of the previous period series in this case
|
||||||
slog.Warn("predicted value is less than 0", "predicted_value", predictedValue, "labels", series.Labels)
|
zap.L().Warn("predictedValue is less than 0", zap.Float64("predictedValue", predictedValue), zap.Any("labels", series.Labels))
|
||||||
predictedValue = p.getMovingAvg(prevSeries, movingAvgWindowSize, idx)
|
predictedValue = p.getMovingAvg(prevSeries, movingAvgWindowSize, idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.Debug("predicted series",
|
zap.L().Debug("predictedSeries",
|
||||||
"moving_avg", movingAvg,
|
zap.Float64("movingAvg", movingAvg),
|
||||||
"avg", avg,
|
zap.Float64("avg", avg),
|
||||||
"mean", mean,
|
zap.Float64("mean", mean),
|
||||||
"labels", series.Labels,
|
zap.Any("labels", series.Labels),
|
||||||
"predicted_value", predictedValue,
|
zap.Float64("predictedValue", predictedValue),
|
||||||
"curr", curr.Value,
|
zap.Float64("curr", curr.Value),
|
||||||
)
|
)
|
||||||
predictedSeries.Points = append(predictedSeries.Points, v3.Point{
|
predictedSeries.Points = append(predictedSeries.Points, v3.Point{
|
||||||
Timestamp: curr.Timestamp,
|
Timestamp: curr.Timestamp,
|
||||||
@@ -418,7 +418,7 @@ func (p *BaseSeasonalProvider) getAnomalies(ctx context.Context, orgID valuer.UU
|
|||||||
|
|
||||||
for _, series := range result.Series {
|
for _, series := range result.Series {
|
||||||
stdDev := p.getStdDev(series)
|
stdDev := p.getStdDev(series)
|
||||||
slog.InfoContext(ctx, "computed standard deviation", "std_dev", stdDev, "labels", series.Labels)
|
zap.L().Info("stdDev", zap.Float64("stdDev", stdDev), zap.Any("labels", series.Labels))
|
||||||
|
|
||||||
pastPeriodSeries := p.getMatchingSeries(pastPeriodResult, series)
|
pastPeriodSeries := p.getMatchingSeries(pastPeriodResult, series)
|
||||||
currentSeasonSeries := p.getMatchingSeries(currentSeasonResult, series)
|
currentSeasonSeries := p.getMatchingSeries(currentSeasonResult, series)
|
||||||
@@ -431,7 +431,7 @@ func (p *BaseSeasonalProvider) getAnomalies(ctx context.Context, orgID valuer.UU
|
|||||||
pastSeasonSeriesAvg := p.getAvg(pastSeasonSeries)
|
pastSeasonSeriesAvg := p.getAvg(pastSeasonSeries)
|
||||||
past2SeasonSeriesAvg := p.getAvg(past2SeasonSeries)
|
past2SeasonSeriesAvg := p.getAvg(past2SeasonSeries)
|
||||||
past3SeasonSeriesAvg := p.getAvg(past3SeasonSeries)
|
past3SeasonSeriesAvg := p.getAvg(past3SeasonSeries)
|
||||||
slog.InfoContext(ctx, "computed averages", "prev_series_avg", prevSeriesAvg, "current_season_series_avg", currentSeasonSeriesAvg, "past_season_series_avg", pastSeasonSeriesAvg, "past_2_season_series_avg", past2SeasonSeriesAvg, "past_3_season_series_avg", past3SeasonSeriesAvg, "labels", series.Labels)
|
zap.L().Info("getAvg", zap.Float64("prevSeriesAvg", prevSeriesAvg), zap.Float64("currentSeasonSeriesAvg", currentSeasonSeriesAvg), zap.Float64("pastSeasonSeriesAvg", pastSeasonSeriesAvg), zap.Float64("past2SeasonSeriesAvg", past2SeasonSeriesAvg), zap.Float64("past3SeasonSeriesAvg", past3SeasonSeriesAvg), zap.Any("labels", series.Labels))
|
||||||
|
|
||||||
predictedSeries := p.getPredictedSeries(
|
predictedSeries := p.getPredictedSeries(
|
||||||
series,
|
series,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||||
"github.com/SigNoz/signoz/pkg/valuer"
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"log/slog"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CloudIntegrationConnectionParamsResponse struct {
|
type CloudIntegrationConnectionParamsResponse struct {
|
||||||
@@ -71,7 +71,7 @@ func (ah *APIHandler) CloudIntegrationsGenerateConnectionParams(w http.ResponseW
|
|||||||
// Return the API Key (PAT) even if the rest of the params can not be deduced.
|
// Return the API Key (PAT) even if the rest of the params can not be deduced.
|
||||||
// Params not returned from here will be requested from the user via form inputs.
|
// Params not returned from here will be requested from the user via form inputs.
|
||||||
// This enables gracefully degraded but working experience even for non-cloud deployments.
|
// This enables gracefully degraded but working experience even for non-cloud deployments.
|
||||||
slog.InfoContext(r.Context(), "ingestion params and signoz api url can not be deduced since no license was found")
|
zap.L().Info("ingestion params and signoz api url can not be deduced since no license was found")
|
||||||
ah.Respond(w, result)
|
ah.Respond(w, result)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ func (ah *APIHandler) CloudIntegrationsGenerateConnectionParams(w http.ResponseW
|
|||||||
result.IngestionKey = ingestionKey
|
result.IngestionKey = ingestionKey
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
slog.InfoContext(r.Context(), "ingestion key can't be deduced since no gateway url has been configured")
|
zap.L().Info("ingestion key can't be deduced since no gateway url has been configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
ah.Respond(w, result)
|
ah.Respond(w, result)
|
||||||
@@ -138,8 +138,9 @@ func (ah *APIHandler) getOrCreateCloudIntegrationPAT(ctx context.Context, orgId
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.InfoContext(ctx, "no PAT found for cloud integration, creating a new one",
|
zap.L().Info(
|
||||||
"cloud_provider", cloudProvider,
|
"no PAT found for cloud integration, creating a new one",
|
||||||
|
zap.String("cloudProvider", cloudProvider),
|
||||||
)
|
)
|
||||||
|
|
||||||
newPAT, err := types.NewStorableAPIKey(
|
newPAT, err := types.NewStorableAPIKey(
|
||||||
@@ -286,8 +287,9 @@ func getOrCreateCloudProviderIngestionKey(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.InfoContext(ctx, "no existing ingestion key found for cloud integration, creating a new one",
|
zap.L().Info(
|
||||||
"cloud_provider", cloudProvider,
|
"no existing ingestion key found for cloud integration, creating a new one",
|
||||||
|
zap.String("cloudProvider", cloudProvider),
|
||||||
)
|
)
|
||||||
createKeyResult, apiErr := requestGateway[createIngestionKeyResponse](
|
createKeyResult, apiErr := requestGateway[createIngestionKeyResponse](
|
||||||
ctx, gatewayUrl, licenseKey, "/v1/workspaces/me/keys",
|
ctx, gatewayUrl, licenseKey, "/v1/workspaces/me/keys",
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/types/featuretypes"
|
"github.com/SigNoz/signoz/pkg/types/featuretypes"
|
||||||
"github.com/SigNoz/signoz/pkg/types/licensetypes"
|
"github.com/SigNoz/signoz/pkg/types/licensetypes"
|
||||||
"github.com/SigNoz/signoz/pkg/valuer"
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
"log/slog"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
|
func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -35,23 +35,23 @@ func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if constants.FetchFeatures == "true" {
|
if constants.FetchFeatures == "true" {
|
||||||
slog.DebugContext(ctx, "fetching license")
|
zap.L().Debug("fetching license")
|
||||||
license, err := ah.Signoz.Licensing.GetActive(ctx, orgID)
|
license, err := ah.Signoz.Licensing.GetActive(ctx, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.ErrorContext(ctx, "failed to fetch license", "error", err)
|
zap.L().Error("failed to fetch license", zap.Error(err))
|
||||||
} else if license == nil {
|
} else if license == nil {
|
||||||
slog.DebugContext(ctx, "no active license found")
|
zap.L().Debug("no active license found")
|
||||||
} else {
|
} else {
|
||||||
licenseKey := license.Key
|
licenseKey := license.Key
|
||||||
|
|
||||||
slog.DebugContext(ctx, "fetching zeus features")
|
zap.L().Debug("fetching zeus features")
|
||||||
zeusFeatures, err := fetchZeusFeatures(constants.ZeusFeaturesURL, licenseKey)
|
zeusFeatures, err := fetchZeusFeatures(constants.ZeusFeaturesURL, licenseKey)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
slog.DebugContext(ctx, "fetched zeus features", "features", zeusFeatures)
|
zap.L().Debug("fetched zeus features", zap.Any("features", zeusFeatures))
|
||||||
// merge featureSet and zeusFeatures in featureSet with higher priority to zeusFeatures
|
// merge featureSet and zeusFeatures in featureSet with higher priority to zeusFeatures
|
||||||
featureSet = MergeFeatureSets(zeusFeatures, featureSet)
|
featureSet = MergeFeatureSets(zeusFeatures, featureSet)
|
||||||
} else {
|
} else {
|
||||||
slog.ErrorContext(ctx, "failed to fetch zeus features", "error", err)
|
zap.L().Error("failed to fetch zeus features", zap.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||||
"github.com/SigNoz/signoz/pkg/valuer"
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
"log/slog"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (aH *APIHandler) queryRangeV4(w http.ResponseWriter, r *http.Request) {
|
func (aH *APIHandler) queryRangeV4(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -35,7 +35,7 @@ func (aH *APIHandler) queryRangeV4(w http.ResponseWriter, r *http.Request) {
|
|||||||
queryRangeParams, apiErrorObj := baseapp.ParseQueryRangeParams(r)
|
queryRangeParams, apiErrorObj := baseapp.ParseQueryRangeParams(r)
|
||||||
|
|
||||||
if apiErrorObj != nil {
|
if apiErrorObj != nil {
|
||||||
slog.ErrorContext(r.Context(), "error parsing metric query range params", "error", apiErrorObj.Err)
|
zap.L().Error("error parsing metric query range params", zap.Error(apiErrorObj.Err))
|
||||||
RespondError(w, apiErrorObj, nil)
|
RespondError(w, apiErrorObj, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -44,7 +44,7 @@ func (aH *APIHandler) queryRangeV4(w http.ResponseWriter, r *http.Request) {
|
|||||||
// add temporality for each metric
|
// add temporality for each metric
|
||||||
temporalityErr := aH.PopulateTemporality(r.Context(), orgID, queryRangeParams)
|
temporalityErr := aH.PopulateTemporality(r.Context(), orgID, queryRangeParams)
|
||||||
if temporalityErr != nil {
|
if temporalityErr != nil {
|
||||||
slog.ErrorContext(r.Context(), "error while adding temporality for metrics", "error", temporalityErr)
|
zap.L().Error("Error while adding temporality for metrics", zap.Error(temporalityErr))
|
||||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: temporalityErr}, nil)
|
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: temporalityErr}, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ import (
|
|||||||
baseint "github.com/SigNoz/signoz/pkg/query-service/interfaces"
|
baseint "github.com/SigNoz/signoz/pkg/query-service/interfaces"
|
||||||
baserules "github.com/SigNoz/signoz/pkg/query-service/rules"
|
baserules "github.com/SigNoz/signoz/pkg/query-service/rules"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/utils"
|
"github.com/SigNoz/signoz/pkg/query-service/utils"
|
||||||
"log/slog"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Server runs HTTP, Mux and a grpc server
|
// Server runs HTTP, Mux and a grpc server
|
||||||
@@ -83,7 +83,6 @@ func NewServer(config signoz.Config, signoz *signoz.SigNoz) (*Server, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reader := clickhouseReader.NewReader(
|
reader := clickhouseReader.NewReader(
|
||||||
signoz.Instrumentation.Logger(),
|
|
||||||
signoz.SQLStore,
|
signoz.SQLStore,
|
||||||
signoz.TelemetryStore,
|
signoz.TelemetryStore,
|
||||||
signoz.Prometheus,
|
signoz.Prometheus,
|
||||||
@@ -217,7 +216,8 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*h
|
|||||||
}),
|
}),
|
||||||
otelmux.WithPublicEndpoint(),
|
otelmux.WithPublicEndpoint(),
|
||||||
))
|
))
|
||||||
r.Use(middleware.NewIdentN(s.signoz.IdentNResolver, s.signoz.Sharder, s.signoz.Instrumentation.Logger()).Wrap)
|
r.Use(middleware.NewAuthN([]string{"Authorization", "Sec-WebSocket-Protocol"}, s.signoz.Sharder, s.signoz.Tokenizer, s.signoz.Instrumentation.Logger()).Wrap)
|
||||||
|
r.Use(middleware.NewAPIKey(s.signoz.SQLStore, []string{"SIGNOZ-API-KEY"}, s.signoz.Instrumentation.Logger(), s.signoz.Sharder).Wrap)
|
||||||
r.Use(middleware.NewTimeout(s.signoz.Instrumentation.Logger(),
|
r.Use(middleware.NewTimeout(s.signoz.Instrumentation.Logger(),
|
||||||
s.config.APIServer.Timeout.ExcludedRoutes,
|
s.config.APIServer.Timeout.ExcludedRoutes,
|
||||||
s.config.APIServer.Timeout.Default,
|
s.config.APIServer.Timeout.Default,
|
||||||
@@ -278,7 +278,7 @@ func (s *Server) initListeners() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.Info(fmt.Sprintf("Query server started listening on %s...", s.httpHostPort))
|
zap.L().Info(fmt.Sprintf("Query server started listening on %s...", s.httpHostPort))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -298,31 +298,31 @@ func (s *Server) Start(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
slog.Info("Starting HTTP server", "port", httpPort, "addr", s.httpHostPort)
|
zap.L().Info("Starting HTTP server", zap.Int("port", httpPort), zap.String("addr", s.httpHostPort))
|
||||||
|
|
||||||
switch err := s.httpServer.Serve(s.httpConn); err {
|
switch err := s.httpServer.Serve(s.httpConn); err {
|
||||||
case nil, http.ErrServerClosed, cmux.ErrListenerClosed:
|
case nil, http.ErrServerClosed, cmux.ErrListenerClosed:
|
||||||
// normal exit, nothing to do
|
// normal exit, nothing to do
|
||||||
default:
|
default:
|
||||||
slog.Error("Could not start HTTP server", "error", err)
|
zap.L().Error("Could not start HTTP server", zap.Error(err))
|
||||||
}
|
}
|
||||||
s.unavailableChannel <- healthcheck.Unavailable
|
s.unavailableChannel <- healthcheck.Unavailable
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
slog.Info("Starting pprof server", "addr", baseconst.DebugHttpPort)
|
zap.L().Info("Starting pprof server", zap.String("addr", baseconst.DebugHttpPort))
|
||||||
|
|
||||||
err = http.ListenAndServe(baseconst.DebugHttpPort, nil)
|
err = http.ListenAndServe(baseconst.DebugHttpPort, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Could not start pprof server", "error", err)
|
zap.L().Error("Could not start pprof server", zap.Error(err))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
slog.Info("Starting OpAmp Websocket server", "addr", baseconst.OpAmpWsEndpoint)
|
zap.L().Info("Starting OpAmp Websocket server", zap.String("addr", baseconst.OpAmpWsEndpoint))
|
||||||
err := s.opampServer.Start(baseconst.OpAmpWsEndpoint)
|
err := s.opampServer.Start(baseconst.OpAmpWsEndpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("opamp ws server failed to start", "error", err)
|
zap.L().Error("opamp ws server failed to start", zap.Error(err))
|
||||||
s.unavailableChannel <- healthcheck.Unavailable
|
s.unavailableChannel <- healthcheck.Unavailable
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -358,9 +358,10 @@ func makeRulesManager(ch baseint.Reader, cache cache.Cache, alertmanager alertma
|
|||||||
MetadataStore: metadataStore,
|
MetadataStore: metadataStore,
|
||||||
Prometheus: prometheus,
|
Prometheus: prometheus,
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
|
Logger: zap.L(),
|
||||||
Reader: ch,
|
Reader: ch,
|
||||||
Querier: querier,
|
Querier: querier,
|
||||||
Logger: providerSettings.Logger,
|
SLogger: providerSettings.Logger,
|
||||||
Cache: cache,
|
Cache: cache,
|
||||||
EvalDelay: baseconst.GetEvalDelay(),
|
EvalDelay: baseconst.GetEvalDelay(),
|
||||||
PrepareTaskFunc: rules.PrepareTaskFunc,
|
PrepareTaskFunc: rules.PrepareTaskFunc,
|
||||||
@@ -379,7 +380,7 @@ func makeRulesManager(ch baseint.Reader, cache cache.Cache, alertmanager alertma
|
|||||||
return nil, fmt.Errorf("rule manager error: %v", err)
|
return nil, fmt.Errorf("rule manager error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.Info("rules manager is ready")
|
zap.L().Info("rules manager is ready")
|
||||||
|
|
||||||
return manager, nil
|
return manager, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log/slog"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -117,7 +116,7 @@ func TestAnomalyRule_NoData_AlertOnAbsent(t *testing.T) {
|
|||||||
|
|
||||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{}, nil)
|
telemetryStore := telemetrystoretest.New(telemetrystore.Config{}, nil)
|
||||||
options := clickhouseReader.NewOptions("primaryNamespace")
|
options := clickhouseReader.NewOptions("primaryNamespace")
|
||||||
reader := clickhouseReader.NewReader(slog.Default(), nil, telemetryStore, nil, "", time.Second, nil, nil, options)
|
reader := clickhouseReader.NewReader(nil, telemetryStore, nil, "", time.Second, nil, nil, options)
|
||||||
|
|
||||||
rule, err := NewAnomalyRule(
|
rule, err := NewAnomalyRule(
|
||||||
"test-anomaly-rule",
|
"test-anomaly-rule",
|
||||||
@@ -248,7 +247,7 @@ func TestAnomalyRule_NoData_AbsentFor(t *testing.T) {
|
|||||||
|
|
||||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{}, nil)
|
telemetryStore := telemetrystoretest.New(telemetrystore.Config{}, nil)
|
||||||
options := clickhouseReader.NewOptions("primaryNamespace")
|
options := clickhouseReader.NewOptions("primaryNamespace")
|
||||||
reader := clickhouseReader.NewReader(slog.Default(), nil, telemetryStore, nil, "", time.Second, nil, nil, options)
|
reader := clickhouseReader.NewReader(nil, telemetryStore, nil, "", time.Second, nil, nil, options)
|
||||||
|
|
||||||
rule, err := NewAnomalyRule("test-anomaly-rule", valuer.GenerateUUID(), &postableRule, reader, nil, logger, nil)
|
rule, err := NewAnomalyRule("test-anomaly-rule", valuer.GenerateUUID(), &postableRule, reader, nil, logger, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/types/ruletypes"
|
"github.com/SigNoz/signoz/pkg/types/ruletypes"
|
||||||
"github.com/SigNoz/signoz/pkg/valuer"
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"log/slog"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error) {
|
func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error) {
|
||||||
@@ -34,7 +34,7 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error)
|
|||||||
opts.Rule,
|
opts.Rule,
|
||||||
opts.Reader,
|
opts.Reader,
|
||||||
opts.Querier,
|
opts.Querier,
|
||||||
opts.Logger,
|
opts.SLogger,
|
||||||
baserules.WithEvalDelay(opts.ManagerOpts.EvalDelay),
|
baserules.WithEvalDelay(opts.ManagerOpts.EvalDelay),
|
||||||
baserules.WithSQLStore(opts.SQLStore),
|
baserules.WithSQLStore(opts.SQLStore),
|
||||||
baserules.WithQueryParser(opts.ManagerOpts.QueryParser),
|
baserules.WithQueryParser(opts.ManagerOpts.QueryParser),
|
||||||
@@ -57,7 +57,7 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error)
|
|||||||
ruleId,
|
ruleId,
|
||||||
opts.OrgID,
|
opts.OrgID,
|
||||||
opts.Rule,
|
opts.Rule,
|
||||||
opts.Logger,
|
opts.SLogger,
|
||||||
opts.Reader,
|
opts.Reader,
|
||||||
opts.ManagerOpts.Prometheus,
|
opts.ManagerOpts.Prometheus,
|
||||||
baserules.WithSQLStore(opts.SQLStore),
|
baserules.WithSQLStore(opts.SQLStore),
|
||||||
@@ -82,7 +82,7 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error)
|
|||||||
opts.Rule,
|
opts.Rule,
|
||||||
opts.Reader,
|
opts.Reader,
|
||||||
opts.Querier,
|
opts.Querier,
|
||||||
opts.Logger,
|
opts.SLogger,
|
||||||
opts.Cache,
|
opts.Cache,
|
||||||
baserules.WithEvalDelay(opts.ManagerOpts.EvalDelay),
|
baserules.WithEvalDelay(opts.ManagerOpts.EvalDelay),
|
||||||
baserules.WithSQLStore(opts.SQLStore),
|
baserules.WithSQLStore(opts.SQLStore),
|
||||||
@@ -142,7 +142,7 @@ func TestNotification(opts baserules.PrepareTestRuleOptions) (int, *basemodel.Ap
|
|||||||
parsedRule,
|
parsedRule,
|
||||||
opts.Reader,
|
opts.Reader,
|
||||||
opts.Querier,
|
opts.Querier,
|
||||||
opts.Logger,
|
opts.SLogger,
|
||||||
baserules.WithSendAlways(),
|
baserules.WithSendAlways(),
|
||||||
baserules.WithSendUnmatched(),
|
baserules.WithSendUnmatched(),
|
||||||
baserules.WithSQLStore(opts.SQLStore),
|
baserules.WithSQLStore(opts.SQLStore),
|
||||||
@@ -151,7 +151,7 @@ func TestNotification(opts baserules.PrepareTestRuleOptions) (int, *basemodel.Ap
|
|||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("failed to prepare a new threshold rule for test", "name", alertname, "error", err)
|
zap.L().Error("failed to prepare a new threshold rule for test", zap.String("name", alertname), zap.Error(err))
|
||||||
return 0, basemodel.BadRequest(err)
|
return 0, basemodel.BadRequest(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ func TestNotification(opts baserules.PrepareTestRuleOptions) (int, *basemodel.Ap
|
|||||||
alertname,
|
alertname,
|
||||||
opts.OrgID,
|
opts.OrgID,
|
||||||
parsedRule,
|
parsedRule,
|
||||||
opts.Logger,
|
opts.SLogger,
|
||||||
opts.Reader,
|
opts.Reader,
|
||||||
opts.ManagerOpts.Prometheus,
|
opts.ManagerOpts.Prometheus,
|
||||||
baserules.WithSendAlways(),
|
baserules.WithSendAlways(),
|
||||||
@@ -173,7 +173,7 @@ func TestNotification(opts baserules.PrepareTestRuleOptions) (int, *basemodel.Ap
|
|||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("failed to prepare a new promql rule for test", "name", alertname, "error", err)
|
zap.L().Error("failed to prepare a new promql rule for test", zap.String("name", alertname), zap.Error(err))
|
||||||
return 0, basemodel.BadRequest(err)
|
return 0, basemodel.BadRequest(err)
|
||||||
}
|
}
|
||||||
} else if parsedRule.RuleType == ruletypes.RuleTypeAnomaly {
|
} else if parsedRule.RuleType == ruletypes.RuleTypeAnomaly {
|
||||||
@@ -184,7 +184,7 @@ func TestNotification(opts baserules.PrepareTestRuleOptions) (int, *basemodel.Ap
|
|||||||
parsedRule,
|
parsedRule,
|
||||||
opts.Reader,
|
opts.Reader,
|
||||||
opts.Querier,
|
opts.Querier,
|
||||||
opts.Logger,
|
opts.SLogger,
|
||||||
opts.Cache,
|
opts.Cache,
|
||||||
baserules.WithSendAlways(),
|
baserules.WithSendAlways(),
|
||||||
baserules.WithSendUnmatched(),
|
baserules.WithSendUnmatched(),
|
||||||
@@ -193,7 +193,7 @@ func TestNotification(opts baserules.PrepareTestRuleOptions) (int, *basemodel.Ap
|
|||||||
baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore),
|
baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("failed to prepare a new anomaly rule for test", "name", alertname, "error", err)
|
zap.L().Error("failed to prepare a new anomaly rule for test", zap.String("name", alertname), zap.Error(err))
|
||||||
return 0, basemodel.BadRequest(err)
|
return 0, basemodel.BadRequest(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -205,7 +205,7 @@ func TestNotification(opts baserules.PrepareTestRuleOptions) (int, *basemodel.Ap
|
|||||||
|
|
||||||
alertsFound, err := rule.Eval(ctx, ts)
|
alertsFound, err := rule.Eval(ctx, ts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("evaluating rule failed", "rule", rule.Name(), "error", err)
|
zap.L().Error("evaluating rule failed", zap.String("rule", rule.Name()), zap.Error(err))
|
||||||
return 0, basemodel.InternalError(fmt.Errorf("rule evaluation failed"))
|
return 0, basemodel.InternalError(fmt.Errorf("rule evaluation failed"))
|
||||||
}
|
}
|
||||||
rule.SendAlerts(ctx, ts, 0, time.Minute, opts.NotifyFunc)
|
rule.SendAlerts(ctx, ts, 0, time.Minute, opts.NotifyFunc)
|
||||||
|
|||||||
@@ -80,21 +80,6 @@ func TestManager_TestNotification_SendUnmatched_ThresholdRule(t *testing.T) {
|
|||||||
alertDataRows := cmock.NewRows(cols, tc.Values)
|
alertDataRows := cmock.NewRows(cols, tc.Values)
|
||||||
|
|
||||||
mock := telemetryStore.Mock()
|
mock := telemetryStore.Mock()
|
||||||
// Mock metadata queries for FetchTemporalityAndTypeMulti
|
|
||||||
// First query: fetchMetricsTemporalityAndType (from signoz_metrics time series table)
|
|
||||||
metadataCols := []cmock.ColumnType{
|
|
||||||
{Name: "metric_name", Type: "String"},
|
|
||||||
{Name: "temporality", Type: "String"},
|
|
||||||
{Name: "type", Type: "String"},
|
|
||||||
{Name: "is_monotonic", Type: "Bool"},
|
|
||||||
}
|
|
||||||
metadataRows := cmock.NewRows(metadataCols, [][]any{
|
|
||||||
{"probe_success", metrictypes.Unspecified, metrictypes.GaugeType, false},
|
|
||||||
})
|
|
||||||
mock.ExpectQuery("*distributed_time_series_v4*").WithArgs(nil, nil, nil).WillReturnRows(metadataRows)
|
|
||||||
// Second query: fetchMeterSourceMetricsTemporalityAndType (from signoz_meter table)
|
|
||||||
emptyMetadataRows := cmock.NewRows(metadataCols, [][]any{})
|
|
||||||
mock.ExpectQuery("*meter*").WithArgs(nil).WillReturnRows(emptyMetadataRows)
|
|
||||||
|
|
||||||
// Generate query arguments for the metric query
|
// Generate query arguments for the metric query
|
||||||
evalTime := time.Now().UTC()
|
evalTime := time.Now().UTC()
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"log/slog"
|
|
||||||
|
|
||||||
"github.com/ClickHouse/clickhouse-go/v2"
|
"github.com/ClickHouse/clickhouse-go/v2"
|
||||||
"github.com/go-co-op/gocron"
|
"github.com/go-co-op/gocron"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/ee/query-service/model"
|
"github.com/SigNoz/signoz/ee/query-service/model"
|
||||||
"github.com/SigNoz/signoz/pkg/licensing"
|
"github.com/SigNoz/signoz/pkg/licensing"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||||
@@ -76,19 +76,19 @@ func (lm *Manager) Start(ctx context.Context) error {
|
|||||||
func (lm *Manager) UploadUsage(ctx context.Context) {
|
func (lm *Manager) UploadUsage(ctx context.Context) {
|
||||||
organizations, err := lm.orgGetter.ListByOwnedKeyRange(ctx)
|
organizations, err := lm.orgGetter.ListByOwnedKeyRange(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.ErrorContext(ctx, "failed to get organizations", "error", err)
|
zap.L().Error("failed to get organizations", zap.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, organization := range organizations {
|
for _, organization := range organizations {
|
||||||
// check if license is present or not
|
// check if license is present or not
|
||||||
license, err := lm.licenseService.GetActive(ctx, organization.ID)
|
license, err := lm.licenseService.GetActive(ctx, organization.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.ErrorContext(ctx, "failed to get active license", "error", err)
|
zap.L().Error("failed to get active license", zap.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if license == nil {
|
if license == nil {
|
||||||
// we will not start the usage reporting if license is not present.
|
// we will not start the usage reporting if license is not present.
|
||||||
slog.InfoContext(ctx, "no license present, skipping usage reporting")
|
zap.L().Info("no license present, skipping usage reporting")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ func (lm *Manager) UploadUsage(ctx context.Context) {
|
|||||||
dbusages := []model.UsageDB{}
|
dbusages := []model.UsageDB{}
|
||||||
err := lm.clickhouseConn.Select(ctx, &dbusages, fmt.Sprintf(query, db, db), time.Now().Add(-(24 * time.Hour)))
|
err := lm.clickhouseConn.Select(ctx, &dbusages, fmt.Sprintf(query, db, db), time.Now().Add(-(24 * time.Hour)))
|
||||||
if err != nil && !strings.Contains(err.Error(), "doesn't exist") {
|
if err != nil && !strings.Contains(err.Error(), "doesn't exist") {
|
||||||
slog.ErrorContext(ctx, "failed to get usage from clickhouse", "error", err)
|
zap.L().Error("failed to get usage from clickhouse: %v", zap.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, u := range dbusages {
|
for _, u := range dbusages {
|
||||||
@@ -125,24 +125,24 @@ func (lm *Manager) UploadUsage(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(usages) <= 0 {
|
if len(usages) <= 0 {
|
||||||
slog.InfoContext(ctx, "no snapshots to upload, skipping")
|
zap.L().Info("no snapshots to upload, skipping.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.InfoContext(ctx, "uploading usage data")
|
zap.L().Info("uploading usage data")
|
||||||
|
|
||||||
usagesPayload := []model.Usage{}
|
usagesPayload := []model.Usage{}
|
||||||
for _, usage := range usages {
|
for _, usage := range usages {
|
||||||
usageDataBytes, err := encryption.Decrypt([]byte(usage.ExporterID[:32]), []byte(usage.Data))
|
usageDataBytes, err := encryption.Decrypt([]byte(usage.ExporterID[:32]), []byte(usage.Data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.ErrorContext(ctx, "error while decrypting usage data", "error", err)
|
zap.L().Error("error while decrypting usage data: %v", zap.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
usageData := model.Usage{}
|
usageData := model.Usage{}
|
||||||
err = json.Unmarshal(usageDataBytes, &usageData)
|
err = json.Unmarshal(usageDataBytes, &usageData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.ErrorContext(ctx, "error while unmarshalling usage data", "error", err)
|
zap.L().Error("error while unmarshalling usage data: %v", zap.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,13 +163,13 @@ func (lm *Manager) UploadUsage(ctx context.Context) {
|
|||||||
|
|
||||||
body, errv2 := json.Marshal(payload)
|
body, errv2 := json.Marshal(payload)
|
||||||
if errv2 != nil {
|
if errv2 != nil {
|
||||||
slog.ErrorContext(ctx, "error while marshalling usage payload", "error", errv2)
|
zap.L().Error("error while marshalling usage payload: %v", zap.Error(errv2))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
errv2 = lm.zeus.PutMeters(ctx, payload.LicenseKey.String(), body)
|
errv2 = lm.zeus.PutMeters(ctx, payload.LicenseKey.String(), body)
|
||||||
if errv2 != nil {
|
if errv2 != nil {
|
||||||
slog.ErrorContext(ctx, "failed to upload usage", "error", errv2)
|
zap.L().Error("failed to upload usage: %v", zap.Error(errv2))
|
||||||
// not returning error here since it is captured in the failed count
|
// not returning error here since it is captured in the failed count
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -179,7 +179,7 @@ func (lm *Manager) UploadUsage(ctx context.Context) {
|
|||||||
func (lm *Manager) Stop(ctx context.Context) {
|
func (lm *Manager) Stop(ctx context.Context) {
|
||||||
lm.scheduler.Stop()
|
lm.scheduler.Stop()
|
||||||
|
|
||||||
slog.InfoContext(ctx, "sending usage data before shutting down")
|
zap.L().Info("sending usage data before shutting down")
|
||||||
// send usage before shutting down
|
// send usage before shutting down
|
||||||
lm.UploadUsage(ctx)
|
lm.UploadUsage(ctx)
|
||||||
atomic.StoreUint32(&locker, stateUnlocked)
|
atomic.StoreUint32(&locker, stateUnlocked)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/errors"
|
|
||||||
"github.com/SigNoz/signoz/pkg/factory"
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
"github.com/SigNoz/signoz/pkg/sqlschema"
|
"github.com/SigNoz/signoz/pkg/sqlschema"
|
||||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
@@ -33,9 +32,9 @@ func New(ctx context.Context, providerSettings factory.ProviderSettings, config
|
|||||||
fmter: fmter,
|
fmter: fmter,
|
||||||
settings: settings,
|
settings: settings,
|
||||||
operator: sqlschema.NewOperator(fmter, sqlschema.OperatorSupport{
|
operator: sqlschema.NewOperator(fmter, sqlschema.OperatorSupport{
|
||||||
SCreateAndDropConstraint: true,
|
DropConstraint: true,
|
||||||
SAlterTableAddAndDropColumnIfNotExistsAndExists: true,
|
ColumnIfNotExistsExists: true,
|
||||||
SAlterTableAlterColumnSetAndDrop: true,
|
AlterColumnSetNotNull: true,
|
||||||
}),
|
}),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -73,9 +72,8 @@ WHERE
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(columns) == 0 {
|
if len(columns) == 0 {
|
||||||
return nil, nil, provider.sqlstore.WrapNotFoundErrf(sql.ErrNoRows, errors.CodeNotFound, "table (%s) not found", tableName)
|
return nil, nil, sql.ErrNoRows
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlschemaColumns := make([]*sqlschema.Column, 0)
|
sqlschemaColumns := make([]*sqlschema.Column, 0)
|
||||||
@@ -222,9 +220,7 @@ SELECT
|
|||||||
ci.relname AS index_name,
|
ci.relname AS index_name,
|
||||||
i.indisunique AS unique,
|
i.indisunique AS unique,
|
||||||
i.indisprimary AS primary,
|
i.indisprimary AS primary,
|
||||||
a.attname AS column_name,
|
a.attname AS column_name
|
||||||
array_position(i.indkey, a.attnum) AS column_position,
|
|
||||||
pg_get_expr(i.indpred, i.indrelid) AS predicate
|
|
||||||
FROM
|
FROM
|
||||||
pg_index i
|
pg_index i
|
||||||
LEFT JOIN pg_class ct ON ct.oid = i.indrelid
|
LEFT JOIN pg_class ct ON ct.oid = i.indrelid
|
||||||
@@ -235,10 +231,9 @@ WHERE
|
|||||||
a.attnum = ANY(i.indkey)
|
a.attnum = ANY(i.indkey)
|
||||||
AND con.oid IS NULL
|
AND con.oid IS NULL
|
||||||
AND ct.relkind = 'r'
|
AND ct.relkind = 'r'
|
||||||
AND ct.relname = ?
|
AND ct.relname = ?`, string(name))
|
||||||
ORDER BY index_name, column_position`, string(name))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, provider.sqlstore.WrapNotFoundErrf(err, errors.CodeNotFound, "no indices for table (%s) found", name)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -247,12 +242,7 @@ ORDER BY index_name, column_position`, string(name))
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
type indexEntry struct {
|
uniqueIndicesMap := make(map[string]*sqlschema.UniqueIndex)
|
||||||
columns []sqlschema.ColumnName
|
|
||||||
predicate *string
|
|
||||||
}
|
|
||||||
|
|
||||||
uniqueIndicesMap := make(map[string]*indexEntry)
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var (
|
var (
|
||||||
tableName string
|
tableName string
|
||||||
@@ -260,53 +250,27 @@ ORDER BY index_name, column_position`, string(name))
|
|||||||
unique bool
|
unique bool
|
||||||
primary bool
|
primary bool
|
||||||
columnName string
|
columnName string
|
||||||
// starts from 0 and is unused in this function, this is to ensure that the column names are in the correct order
|
|
||||||
columnPosition int
|
|
||||||
predicate *string
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := rows.Scan(&tableName, &indexName, &unique, &primary, &columnName, &columnPosition, &predicate); err != nil {
|
if err := rows.Scan(&tableName, &indexName, &unique, &primary, &columnName); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if unique {
|
if unique {
|
||||||
if _, ok := uniqueIndicesMap[indexName]; !ok {
|
if _, ok := uniqueIndicesMap[indexName]; !ok {
|
||||||
uniqueIndicesMap[indexName] = &indexEntry{
|
uniqueIndicesMap[indexName] = &sqlschema.UniqueIndex{
|
||||||
columns: []sqlschema.ColumnName{sqlschema.ColumnName(columnName)},
|
TableName: name,
|
||||||
predicate: predicate,
|
ColumnNames: []sqlschema.ColumnName{sqlschema.ColumnName(columnName)},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uniqueIndicesMap[indexName].columns = append(uniqueIndicesMap[indexName].columns, sqlschema.ColumnName(columnName))
|
uniqueIndicesMap[indexName].ColumnNames = append(uniqueIndicesMap[indexName].ColumnNames, sqlschema.ColumnName(columnName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
indices := make([]sqlschema.Index, 0)
|
indices := make([]sqlschema.Index, 0)
|
||||||
for indexName, entry := range uniqueIndicesMap {
|
for _, index := range uniqueIndicesMap {
|
||||||
if entry.predicate != nil {
|
indices = append(indices, index)
|
||||||
index := &sqlschema.PartialUniqueIndex{
|
|
||||||
TableName: name,
|
|
||||||
ColumnNames: entry.columns,
|
|
||||||
Where: *entry.predicate,
|
|
||||||
}
|
|
||||||
|
|
||||||
if index.Name() == indexName {
|
|
||||||
indices = append(indices, index)
|
|
||||||
} else {
|
|
||||||
indices = append(indices, index.Named(indexName))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
index := &sqlschema.UniqueIndex{
|
|
||||||
TableName: name,
|
|
||||||
ColumnNames: entry.columns,
|
|
||||||
}
|
|
||||||
|
|
||||||
if index.Name() == indexName {
|
|
||||||
indices = append(indices, index)
|
|
||||||
} else {
|
|
||||||
indices = append(indices, index.Named(indexName))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return indices, nil
|
return indices, nil
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
import { PropsWithChildren } from 'react';
|
|
||||||
|
|
||||||
type CommonProps = PropsWithChildren<{
|
|
||||||
className?: string;
|
|
||||||
minSize?: number;
|
|
||||||
maxSize?: number;
|
|
||||||
defaultSize?: number;
|
|
||||||
direction?: 'horizontal' | 'vertical';
|
|
||||||
autoSaveId?: string;
|
|
||||||
withHandle?: boolean;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
export function ResizablePanelGroup({
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
}: CommonProps): JSX.Element {
|
|
||||||
return <div className={className}>{children}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ResizablePanel({
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
}: CommonProps): JSX.Element {
|
|
||||||
return <div className={className}>{children}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ResizableHandle({ className }: CommonProps): JSX.Element {
|
|
||||||
return <div className={className} />;
|
|
||||||
}
|
|
||||||
@@ -14,7 +14,6 @@ const config: Config.InitialOptions = {
|
|||||||
'\\.(css|less|scss)$': '<rootDir>/__mocks__/cssMock.ts',
|
'\\.(css|less|scss)$': '<rootDir>/__mocks__/cssMock.ts',
|
||||||
'\\.md$': '<rootDir>/__mocks__/cssMock.ts',
|
'\\.md$': '<rootDir>/__mocks__/cssMock.ts',
|
||||||
'^uplot$': '<rootDir>/__mocks__/uplotMock.ts',
|
'^uplot$': '<rootDir>/__mocks__/uplotMock.ts',
|
||||||
'^@signozhq/resizable$': '<rootDir>/__mocks__/resizableMock.tsx',
|
|
||||||
'^hooks/useSafeNavigate$': USE_SAFE_NAVIGATE_MOCK_PATH,
|
'^hooks/useSafeNavigate$': USE_SAFE_NAVIGATE_MOCK_PATH,
|
||||||
'^src/hooks/useSafeNavigate$': USE_SAFE_NAVIGATE_MOCK_PATH,
|
'^src/hooks/useSafeNavigate$': USE_SAFE_NAVIGATE_MOCK_PATH,
|
||||||
'^.*/useSafeNavigate$': USE_SAFE_NAVIGATE_MOCK_PATH,
|
'^.*/useSafeNavigate$': USE_SAFE_NAVIGATE_MOCK_PATH,
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
"prettify": "prettier --write .",
|
"prettify": "prettier --write .",
|
||||||
"fmt": "prettier --check .",
|
"fmt": "prettier --check .",
|
||||||
"lint": "eslint ./src",
|
"lint": "eslint ./src",
|
||||||
"lint:generated": "eslint ./src/api/generated --fix",
|
|
||||||
"lint:fix": "eslint ./src --fix",
|
"lint:fix": "eslint ./src --fix",
|
||||||
"jest": "jest",
|
"jest": "jest",
|
||||||
"jest:coverage": "jest --coverage",
|
"jest:coverage": "jest --coverage",
|
||||||
@@ -65,7 +64,7 @@
|
|||||||
"@signozhq/sonner": "0.1.0",
|
"@signozhq/sonner": "0.1.0",
|
||||||
"@signozhq/switch": "0.0.2",
|
"@signozhq/switch": "0.0.2",
|
||||||
"@signozhq/table": "0.3.7",
|
"@signozhq/table": "0.3.7",
|
||||||
"@signozhq/toggle-group": "0.0.1",
|
"@signozhq/toggle-group": "^0.0.1",
|
||||||
"@signozhq/tooltip": "0.0.2",
|
"@signozhq/tooltip": "0.0.2",
|
||||||
"@tanstack/react-table": "8.20.6",
|
"@tanstack/react-table": "8.20.6",
|
||||||
"@tanstack/react-virtual": "3.11.2",
|
"@tanstack/react-virtual": "3.11.2",
|
||||||
@@ -284,4 +283,4 @@
|
|||||||
"tmp": "0.2.4",
|
"tmp": "0.2.4",
|
||||||
"vite": "npm:rolldown-vite@7.3.1"
|
"vite": "npm:rolldown-vite@7.3.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 214 KiB |
@@ -25,7 +25,7 @@ echo "\n✅ Prettier formatting successful"
|
|||||||
|
|
||||||
# Fix linting issues
|
# Fix linting issues
|
||||||
echo "\n\n---\nRunning eslint...\n"
|
echo "\n\n---\nRunning eslint...\n"
|
||||||
if ! yarn lint:generated; then
|
if ! yarn lint --fix --quiet src/api/generated; then
|
||||||
echo "ESLint check failed! Please fix linting errors before proceeding."
|
echo "ESLint check failed! Please fix linting errors before proceeding."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ import type { BodyType, ErrorType } from '../../../generatedAPIInstance';
|
|||||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||||
import type {
|
import type {
|
||||||
AuthtypesPatchableObjectsDTO,
|
AuthtypesPatchableObjectsDTO,
|
||||||
AuthtypesPatchableRoleDTO,
|
|
||||||
AuthtypesPostableRoleDTO,
|
|
||||||
CreateRole201,
|
CreateRole201,
|
||||||
DeleteRolePathParameters,
|
DeleteRolePathParameters,
|
||||||
GetObjects200,
|
GetObjects200,
|
||||||
@@ -33,6 +31,8 @@ import type {
|
|||||||
PatchObjectsPathParameters,
|
PatchObjectsPathParameters,
|
||||||
PatchRolePathParameters,
|
PatchRolePathParameters,
|
||||||
RenderErrorResponseDTO,
|
RenderErrorResponseDTO,
|
||||||
|
RoletypesPatchableRoleDTO,
|
||||||
|
RoletypesPostableRoleDTO,
|
||||||
} from '../sigNoz.schemas';
|
} from '../sigNoz.schemas';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -118,14 +118,14 @@ export const invalidateListRoles = async (
|
|||||||
* @summary Create role
|
* @summary Create role
|
||||||
*/
|
*/
|
||||||
export const createRole = (
|
export const createRole = (
|
||||||
authtypesPostableRoleDTO: BodyType<AuthtypesPostableRoleDTO>,
|
roletypesPostableRoleDTO: BodyType<RoletypesPostableRoleDTO>,
|
||||||
signal?: AbortSignal,
|
signal?: AbortSignal,
|
||||||
) => {
|
) => {
|
||||||
return GeneratedAPIInstance<CreateRole201>({
|
return GeneratedAPIInstance<CreateRole201>({
|
||||||
url: `/api/v1/roles`,
|
url: `/api/v1/roles`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
data: authtypesPostableRoleDTO,
|
data: roletypesPostableRoleDTO,
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -137,13 +137,13 @@ export const getCreateRoleMutationOptions = <
|
|||||||
mutation?: UseMutationOptions<
|
mutation?: UseMutationOptions<
|
||||||
Awaited<ReturnType<typeof createRole>>,
|
Awaited<ReturnType<typeof createRole>>,
|
||||||
TError,
|
TError,
|
||||||
{ data: BodyType<AuthtypesPostableRoleDTO> },
|
{ data: BodyType<RoletypesPostableRoleDTO> },
|
||||||
TContext
|
TContext
|
||||||
>;
|
>;
|
||||||
}): UseMutationOptions<
|
}): UseMutationOptions<
|
||||||
Awaited<ReturnType<typeof createRole>>,
|
Awaited<ReturnType<typeof createRole>>,
|
||||||
TError,
|
TError,
|
||||||
{ data: BodyType<AuthtypesPostableRoleDTO> },
|
{ data: BodyType<RoletypesPostableRoleDTO> },
|
||||||
TContext
|
TContext
|
||||||
> => {
|
> => {
|
||||||
const mutationKey = ['createRole'];
|
const mutationKey = ['createRole'];
|
||||||
@@ -157,7 +157,7 @@ export const getCreateRoleMutationOptions = <
|
|||||||
|
|
||||||
const mutationFn: MutationFunction<
|
const mutationFn: MutationFunction<
|
||||||
Awaited<ReturnType<typeof createRole>>,
|
Awaited<ReturnType<typeof createRole>>,
|
||||||
{ data: BodyType<AuthtypesPostableRoleDTO> }
|
{ data: BodyType<RoletypesPostableRoleDTO> }
|
||||||
> = (props) => {
|
> = (props) => {
|
||||||
const { data } = props ?? {};
|
const { data } = props ?? {};
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ export const getCreateRoleMutationOptions = <
|
|||||||
export type CreateRoleMutationResult = NonNullable<
|
export type CreateRoleMutationResult = NonNullable<
|
||||||
Awaited<ReturnType<typeof createRole>>
|
Awaited<ReturnType<typeof createRole>>
|
||||||
>;
|
>;
|
||||||
export type CreateRoleMutationBody = BodyType<AuthtypesPostableRoleDTO>;
|
export type CreateRoleMutationBody = BodyType<RoletypesPostableRoleDTO>;
|
||||||
export type CreateRoleMutationError = ErrorType<RenderErrorResponseDTO>;
|
export type CreateRoleMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -183,13 +183,13 @@ export const useCreateRole = <
|
|||||||
mutation?: UseMutationOptions<
|
mutation?: UseMutationOptions<
|
||||||
Awaited<ReturnType<typeof createRole>>,
|
Awaited<ReturnType<typeof createRole>>,
|
||||||
TError,
|
TError,
|
||||||
{ data: BodyType<AuthtypesPostableRoleDTO> },
|
{ data: BodyType<RoletypesPostableRoleDTO> },
|
||||||
TContext
|
TContext
|
||||||
>;
|
>;
|
||||||
}): UseMutationResult<
|
}): UseMutationResult<
|
||||||
Awaited<ReturnType<typeof createRole>>,
|
Awaited<ReturnType<typeof createRole>>,
|
||||||
TError,
|
TError,
|
||||||
{ data: BodyType<AuthtypesPostableRoleDTO> },
|
{ data: BodyType<RoletypesPostableRoleDTO> },
|
||||||
TContext
|
TContext
|
||||||
> => {
|
> => {
|
||||||
const mutationOptions = getCreateRoleMutationOptions(options);
|
const mutationOptions = getCreateRoleMutationOptions(options);
|
||||||
@@ -370,13 +370,13 @@ export const invalidateGetRole = async (
|
|||||||
*/
|
*/
|
||||||
export const patchRole = (
|
export const patchRole = (
|
||||||
{ id }: PatchRolePathParameters,
|
{ id }: PatchRolePathParameters,
|
||||||
authtypesPatchableRoleDTO: BodyType<AuthtypesPatchableRoleDTO>,
|
roletypesPatchableRoleDTO: BodyType<RoletypesPatchableRoleDTO>,
|
||||||
) => {
|
) => {
|
||||||
return GeneratedAPIInstance<string>({
|
return GeneratedAPIInstance<string>({
|
||||||
url: `/api/v1/roles/${id}`,
|
url: `/api/v1/roles/${id}`,
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
data: authtypesPatchableRoleDTO,
|
data: roletypesPatchableRoleDTO,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -389,7 +389,7 @@ export const getPatchRoleMutationOptions = <
|
|||||||
TError,
|
TError,
|
||||||
{
|
{
|
||||||
pathParams: PatchRolePathParameters;
|
pathParams: PatchRolePathParameters;
|
||||||
data: BodyType<AuthtypesPatchableRoleDTO>;
|
data: BodyType<RoletypesPatchableRoleDTO>;
|
||||||
},
|
},
|
||||||
TContext
|
TContext
|
||||||
>;
|
>;
|
||||||
@@ -398,7 +398,7 @@ export const getPatchRoleMutationOptions = <
|
|||||||
TError,
|
TError,
|
||||||
{
|
{
|
||||||
pathParams: PatchRolePathParameters;
|
pathParams: PatchRolePathParameters;
|
||||||
data: BodyType<AuthtypesPatchableRoleDTO>;
|
data: BodyType<RoletypesPatchableRoleDTO>;
|
||||||
},
|
},
|
||||||
TContext
|
TContext
|
||||||
> => {
|
> => {
|
||||||
@@ -415,7 +415,7 @@ export const getPatchRoleMutationOptions = <
|
|||||||
Awaited<ReturnType<typeof patchRole>>,
|
Awaited<ReturnType<typeof patchRole>>,
|
||||||
{
|
{
|
||||||
pathParams: PatchRolePathParameters;
|
pathParams: PatchRolePathParameters;
|
||||||
data: BodyType<AuthtypesPatchableRoleDTO>;
|
data: BodyType<RoletypesPatchableRoleDTO>;
|
||||||
}
|
}
|
||||||
> = (props) => {
|
> = (props) => {
|
||||||
const { pathParams, data } = props ?? {};
|
const { pathParams, data } = props ?? {};
|
||||||
@@ -429,7 +429,7 @@ export const getPatchRoleMutationOptions = <
|
|||||||
export type PatchRoleMutationResult = NonNullable<
|
export type PatchRoleMutationResult = NonNullable<
|
||||||
Awaited<ReturnType<typeof patchRole>>
|
Awaited<ReturnType<typeof patchRole>>
|
||||||
>;
|
>;
|
||||||
export type PatchRoleMutationBody = BodyType<AuthtypesPatchableRoleDTO>;
|
export type PatchRoleMutationBody = BodyType<RoletypesPatchableRoleDTO>;
|
||||||
export type PatchRoleMutationError = ErrorType<RenderErrorResponseDTO>;
|
export type PatchRoleMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -444,7 +444,7 @@ export const usePatchRole = <
|
|||||||
TError,
|
TError,
|
||||||
{
|
{
|
||||||
pathParams: PatchRolePathParameters;
|
pathParams: PatchRolePathParameters;
|
||||||
data: BodyType<AuthtypesPatchableRoleDTO>;
|
data: BodyType<RoletypesPatchableRoleDTO>;
|
||||||
},
|
},
|
||||||
TContext
|
TContext
|
||||||
>;
|
>;
|
||||||
@@ -453,7 +453,7 @@ export const usePatchRole = <
|
|||||||
TError,
|
TError,
|
||||||
{
|
{
|
||||||
pathParams: PatchRolePathParameters;
|
pathParams: PatchRolePathParameters;
|
||||||
data: BodyType<AuthtypesPatchableRoleDTO>;
|
data: BodyType<RoletypesPatchableRoleDTO>;
|
||||||
},
|
},
|
||||||
TContext
|
TContext
|
||||||
> => {
|
> => {
|
||||||
|
|||||||
@@ -278,13 +278,6 @@ export interface AuthtypesPatchableObjectsDTO {
|
|||||||
deletions: AuthtypesGettableObjectsDTO[] | null;
|
deletions: AuthtypesGettableObjectsDTO[] | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthtypesPatchableRoleDTO {
|
|
||||||
/**
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
description: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AuthtypesPostableAuthDomainDTO {
|
export interface AuthtypesPostableAuthDomainDTO {
|
||||||
config?: AuthtypesAuthDomainConfigDTO;
|
config?: AuthtypesAuthDomainConfigDTO;
|
||||||
/**
|
/**
|
||||||
@@ -308,17 +301,6 @@ export interface AuthtypesPostableEmailPasswordSessionDTO {
|
|||||||
password?: string;
|
password?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthtypesPostableRoleDTO {
|
|
||||||
/**
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
description?: string;
|
|
||||||
/**
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AuthtypesPostableRotateTokenDTO {
|
export interface AuthtypesPostableRotateTokenDTO {
|
||||||
/**
|
/**
|
||||||
* @type string
|
* @type string
|
||||||
@@ -337,39 +319,6 @@ export interface AuthtypesResourceDTO {
|
|||||||
type: string;
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthtypesRoleDTO {
|
|
||||||
/**
|
|
||||||
* @type string
|
|
||||||
* @format date-time
|
|
||||||
*/
|
|
||||||
createdAt?: Date;
|
|
||||||
/**
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
description: string;
|
|
||||||
/**
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
id: string;
|
|
||||||
/**
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
/**
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
orgId: string;
|
|
||||||
/**
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
type: string;
|
|
||||||
/**
|
|
||||||
* @type string
|
|
||||||
* @format date-time
|
|
||||||
*/
|
|
||||||
updatedAt?: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @nullable
|
* @nullable
|
||||||
*/
|
*/
|
||||||
@@ -2090,6 +2039,57 @@ export interface RenderErrorResponseDTO {
|
|||||||
status: string;
|
status: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RoletypesPatchableRoleDTO {
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RoletypesPostableRoleDTO {
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
description?: string;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RoletypesRoleDTO {
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
* @format date-time
|
||||||
|
*/
|
||||||
|
createdAt?: Date;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
description: string;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
orgId: string;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
type: string;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
* @format date-time
|
||||||
|
*/
|
||||||
|
updatedAt?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ServiceaccounttypesFactorAPIKeyDTO {
|
export interface ServiceaccounttypesFactorAPIKeyDTO {
|
||||||
/**
|
/**
|
||||||
* @type string
|
* @type string
|
||||||
@@ -3163,7 +3163,7 @@ export type ListRoles200 = {
|
|||||||
/**
|
/**
|
||||||
* @type array
|
* @type array
|
||||||
*/
|
*/
|
||||||
data: AuthtypesRoleDTO[];
|
data: RoletypesRoleDTO[];
|
||||||
/**
|
/**
|
||||||
* @type string
|
* @type string
|
||||||
*/
|
*/
|
||||||
@@ -3185,7 +3185,7 @@ export type GetRolePathParameters = {
|
|||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
export type GetRole200 = {
|
export type GetRole200 = {
|
||||||
data: AuthtypesRoleDTO;
|
data: RoletypesRoleDTO;
|
||||||
/**
|
/**
|
||||||
* @type string
|
* @type string
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// ** Helpers
|
// ** Helpers
|
||||||
import { MetrictypesTypeDTO } from 'api/generated/services/sigNoz.schemas';
|
import { MetrictypesTypeDTO } from 'api/generated/services/sigNoz.schemas';
|
||||||
import { defaultTraceSelectedColumns } from 'container/OptionsMenu/constants';
|
|
||||||
import { createIdFromObjectFields } from 'lib/createIdFromObjectFields';
|
import { createIdFromObjectFields } from 'lib/createIdFromObjectFields';
|
||||||
import { createNewBuilderItemName } from 'lib/newQueryBuilder/createNewBuilderItemName';
|
import { createNewBuilderItemName } from 'lib/newQueryBuilder/createNewBuilderItemName';
|
||||||
import { IAttributeValuesResponse } from 'types/api/queryBuilder/getAttributesValues';
|
import { IAttributeValuesResponse } from 'types/api/queryBuilder/getAttributesValues';
|
||||||
@@ -549,49 +548,3 @@ export const DATA_TYPE_VS_ATTRIBUTE_VALUES_KEY: Record<
|
|||||||
[DataTypes.ArrayBool]: 'boolAttributeValues',
|
[DataTypes.ArrayBool]: 'boolAttributeValues',
|
||||||
[DataTypes.EMPTY]: 'stringAttributeValues',
|
[DataTypes.EMPTY]: 'stringAttributeValues',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const listViewInitialLogQuery: Query = {
|
|
||||||
...initialQueriesMap.logs,
|
|
||||||
builder: {
|
|
||||||
...initialQueriesMap.logs.builder,
|
|
||||||
queryData: [
|
|
||||||
{
|
|
||||||
...initialQueriesMap.logs.builder.queryData[0],
|
|
||||||
aggregateOperator: LogsAggregatorOperator.NOOP,
|
|
||||||
orderBy: [{ columnName: 'timestamp', order: 'desc' }],
|
|
||||||
offset: 0,
|
|
||||||
pageSize: 100,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const PANEL_TYPES_INITIAL_QUERY: Record<PANEL_TYPES, Query> = {
|
|
||||||
[PANEL_TYPES.TIME_SERIES]: initialQueriesMap.metrics,
|
|
||||||
[PANEL_TYPES.VALUE]: initialQueriesMap.metrics,
|
|
||||||
[PANEL_TYPES.TABLE]: initialQueriesMap.metrics,
|
|
||||||
[PANEL_TYPES.LIST]: listViewInitialLogQuery,
|
|
||||||
[PANEL_TYPES.TRACE]: initialQueriesMap.traces,
|
|
||||||
[PANEL_TYPES.BAR]: initialQueriesMap.metrics,
|
|
||||||
[PANEL_TYPES.PIE]: initialQueriesMap.metrics,
|
|
||||||
[PANEL_TYPES.HISTOGRAM]: initialQueriesMap.metrics,
|
|
||||||
[PANEL_TYPES.EMPTY_WIDGET]: initialQueriesMap.metrics,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const listViewInitialTraceQuery: Query = {
|
|
||||||
// it should be the above commented query
|
|
||||||
...initialQueriesMap.traces,
|
|
||||||
builder: {
|
|
||||||
...initialQueriesMap.traces.builder,
|
|
||||||
queryData: [
|
|
||||||
{
|
|
||||||
...initialQueriesMap.traces.builder.queryData[0],
|
|
||||||
aggregateOperator: LogsAggregatorOperator.NOOP,
|
|
||||||
orderBy: [{ columnName: 'timestamp', order: 'desc' }],
|
|
||||||
offset: 0,
|
|
||||||
pageSize: 10,
|
|
||||||
selectColumns: defaultTraceSelectedColumns,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.panel-type-selection-modal {
|
.graph-selection {
|
||||||
.ant-modal-content {
|
.ant-modal-content {
|
||||||
width: 515px;
|
width: 515px;
|
||||||
max-height: 646px;
|
max-height: 646px;
|
||||||
@@ -76,11 +76,6 @@
|
|||||||
content: none;
|
content: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-type-text {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +114,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.lightMode {
|
.lightMode {
|
||||||
.panel-type-selection-modal {
|
.graph-selection {
|
||||||
.ant-modal-content {
|
.ant-modal-content {
|
||||||
border: 1px solid var(--bg-vanilla-300);
|
border: 1px solid var(--bg-vanilla-300);
|
||||||
background: var(--bg-vanilla-100);
|
background: var(--bg-vanilla-100);
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
|
import { defaultTraceSelectedColumns } from 'container/OptionsMenu/constants';
|
||||||
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
import { LogsAggregatorOperator } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
|
export const PANEL_TYPES_INITIAL_QUERY = {
|
||||||
|
[PANEL_TYPES.TIME_SERIES]: initialQueriesMap.metrics,
|
||||||
|
[PANEL_TYPES.VALUE]: initialQueriesMap.metrics,
|
||||||
|
[PANEL_TYPES.TABLE]: initialQueriesMap.metrics,
|
||||||
|
[PANEL_TYPES.LIST]: initialQueriesMap.logs,
|
||||||
|
[PANEL_TYPES.TRACE]: initialQueriesMap.traces,
|
||||||
|
[PANEL_TYPES.BAR]: initialQueriesMap.metrics,
|
||||||
|
[PANEL_TYPES.PIE]: initialQueriesMap.metrics,
|
||||||
|
[PANEL_TYPES.HISTOGRAM]: initialQueriesMap.metrics,
|
||||||
|
[PANEL_TYPES.EMPTY_WIDGET]: initialQueriesMap.metrics,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const listViewInitialLogQuery: Query = {
|
||||||
|
...initialQueriesMap.logs,
|
||||||
|
builder: {
|
||||||
|
...initialQueriesMap.logs.builder,
|
||||||
|
queryData: [
|
||||||
|
{
|
||||||
|
...initialQueriesMap.logs.builder.queryData[0],
|
||||||
|
aggregateOperator: LogsAggregatorOperator.NOOP,
|
||||||
|
orderBy: [{ columnName: 'timestamp', order: 'desc' }],
|
||||||
|
offset: 0,
|
||||||
|
pageSize: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const listViewInitialTraceQuery: Query = {
|
||||||
|
// it should be the above commented query
|
||||||
|
...initialQueriesMap.traces,
|
||||||
|
builder: {
|
||||||
|
...initialQueriesMap.traces.builder,
|
||||||
|
queryData: [
|
||||||
|
{
|
||||||
|
...initialQueriesMap.traces.builder.queryData[0],
|
||||||
|
aggregateOperator: LogsAggregatorOperator.NOOP,
|
||||||
|
orderBy: [{ columnName: 'timestamp', order: 'desc' }],
|
||||||
|
offset: 0,
|
||||||
|
pageSize: 10,
|
||||||
|
selectColumns: defaultTraceSelectedColumns,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import { Card, Modal } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
|
import { QueryParams } from 'constants/query';
|
||||||
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
|
import createQueryParams from 'lib/createQueryParams';
|
||||||
|
import history from 'lib/history';
|
||||||
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
|
import { LogsAggregatorOperator } from 'types/common/queryBuilder';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
import { PANEL_TYPES_INITIAL_QUERY } from './constants';
|
||||||
|
import menuItems from './menuItems';
|
||||||
|
import { Text } from './styles';
|
||||||
|
|
||||||
|
import './ComponentSlider.styles.scss';
|
||||||
|
|
||||||
|
function DashboardGraphSlider(): JSX.Element {
|
||||||
|
const { handleToggleDashboardSlider, isDashboardSliderOpen } = useDashboard();
|
||||||
|
|
||||||
|
const onClickHandler = (name: PANEL_TYPES) => (): void => {
|
||||||
|
const id = uuid();
|
||||||
|
handleToggleDashboardSlider(false);
|
||||||
|
logEvent('Dashboard Detail: New panel type selected', {
|
||||||
|
// dashboardId: '',
|
||||||
|
// dashboardName: '',
|
||||||
|
// numberOfPanels: 0, // todo - at this point we don't know these attributes
|
||||||
|
panelType: name,
|
||||||
|
widgetId: id,
|
||||||
|
});
|
||||||
|
const queryParamsLog = {
|
||||||
|
graphType: name,
|
||||||
|
widgetId: id,
|
||||||
|
[QueryParams.compositeQuery]: JSON.stringify({
|
||||||
|
...PANEL_TYPES_INITIAL_QUERY[name],
|
||||||
|
builder: {
|
||||||
|
...PANEL_TYPES_INITIAL_QUERY[name].builder,
|
||||||
|
queryData: [
|
||||||
|
{
|
||||||
|
...PANEL_TYPES_INITIAL_QUERY[name].builder.queryData[0],
|
||||||
|
aggregateOperator: LogsAggregatorOperator.NOOP,
|
||||||
|
orderBy: [{ columnName: 'timestamp', order: 'desc' }],
|
||||||
|
offset: 0,
|
||||||
|
pageSize: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryParams = {
|
||||||
|
graphType: name,
|
||||||
|
widgetId: id,
|
||||||
|
[QueryParams.compositeQuery]: JSON.stringify(
|
||||||
|
PANEL_TYPES_INITIAL_QUERY[name],
|
||||||
|
),
|
||||||
|
};
|
||||||
|
if (name === PANEL_TYPES.LIST) {
|
||||||
|
history.push(
|
||||||
|
`${history.location.pathname}/new?${createQueryParams(queryParamsLog)}`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
history.push(
|
||||||
|
`${history.location.pathname}/new?${createQueryParams(queryParams)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCardClick = (panelType: PANEL_TYPES): void => {
|
||||||
|
onClickHandler(panelType)();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={isDashboardSliderOpen}
|
||||||
|
onCancel={(): void => {
|
||||||
|
handleToggleDashboardSlider(false);
|
||||||
|
}}
|
||||||
|
rootClassName="graph-selection"
|
||||||
|
footer={null}
|
||||||
|
title="New Panel"
|
||||||
|
>
|
||||||
|
<div className="panel-selection">
|
||||||
|
{menuItems.map(({ name, icon, display }) => (
|
||||||
|
<Card onClick={(): void => handleCardClick(name)} id={name} key={name}>
|
||||||
|
{icon}
|
||||||
|
<Text>{display}</Text>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DashboardGraphSlider;
|
||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
Table,
|
Table,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
export const PanelTypesWithData: ItemsProps[] = [
|
const Items: ItemsProps[] = [
|
||||||
{
|
{
|
||||||
name: PANEL_TYPES.TIME_SERIES,
|
name: PANEL_TYPES.TIME_SERIES,
|
||||||
icon: <LineChart size={16} color={Color.BG_ROBIN_400} />,
|
icon: <LineChart size={16} color={Color.BG_ROBIN_400} />,
|
||||||
@@ -52,3 +52,5 @@ export interface ItemsProps {
|
|||||||
icon: JSX.Element;
|
icon: JSX.Element;
|
||||||
display: string;
|
display: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default Items;
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { Card as CardComponent, Typography } from 'antd';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const Container = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: right;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Card = styled(CardComponent)`
|
||||||
|
min-height: 80px;
|
||||||
|
min-width: 120px;
|
||||||
|
overflow-y: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.2s;
|
||||||
|
|
||||||
|
.ant-card-body {
|
||||||
|
padding: 12px;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.ant-typography {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
border: 1px solid var(--bg-robin-400);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Text = styled(Typography)`
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 1rem;
|
||||||
|
`;
|
||||||
@@ -182,7 +182,9 @@ describe('Dashboard landing page actions header tests', () => {
|
|||||||
(useLocation as jest.Mock).mockReturnValue(mockLocation);
|
(useLocation as jest.Mock).mockReturnValue(mockLocation);
|
||||||
|
|
||||||
const mockContextValue: IDashboardContext = {
|
const mockContextValue: IDashboardContext = {
|
||||||
|
isDashboardSliderOpen: false,
|
||||||
isDashboardLocked: false,
|
isDashboardLocked: false,
|
||||||
|
handleToggleDashboardSlider: jest.fn(),
|
||||||
handleDashboardLockToggle: jest.fn(),
|
handleDashboardLockToggle: jest.fn(),
|
||||||
dashboardResponse: {} as IDashboardContext['dashboardResponse'],
|
dashboardResponse: {} as IDashboardContext['dashboardResponse'],
|
||||||
selectedDashboard: (getDashboardById.data as unknown) as Dashboard,
|
selectedDashboard: (getDashboardById.data as unknown) as Dashboard,
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import {
|
|||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useAppContext } from 'providers/App/App';
|
import { useAppContext } from 'providers/App/App';
|
||||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { usePanelTypeSelectionModalStore } from 'providers/Dashboard/helpers/panelTypeSelectionModalHelper';
|
|
||||||
import { sortLayout } from 'providers/Dashboard/util';
|
import { sortLayout } from 'providers/Dashboard/util';
|
||||||
import { DashboardData } from 'types/api/dashboard/getAll';
|
import { DashboardData } from 'types/api/dashboard/getAll';
|
||||||
import { Props } from 'types/api/dashboard/update';
|
import { Props } from 'types/api/dashboard/update';
|
||||||
@@ -49,10 +48,10 @@ import { ComponentTypes } from 'utils/permission';
|
|||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
import DashboardHeader from '../components/DashboardHeader/DashboardHeader';
|
import DashboardHeader from '../components/DashboardHeader/DashboardHeader';
|
||||||
|
import DashboardGraphSlider from '../ComponentsSlider';
|
||||||
import DashboardSettings from '../DashboardSettings';
|
import DashboardSettings from '../DashboardSettings';
|
||||||
import { Base64Icons } from '../DashboardSettings/General/utils';
|
import { Base64Icons } from '../DashboardSettings/General/utils';
|
||||||
import DashboardVariableSelection from '../DashboardVariablesSelection';
|
import DashboardVariableSelection from '../DashboardVariablesSelection';
|
||||||
import PanelTypeSelectionModal from '../PanelTypeSelectionModal';
|
|
||||||
import SettingsDrawer from './SettingsDrawer';
|
import SettingsDrawer from './SettingsDrawer';
|
||||||
import { VariablesSettingsTab } from './types';
|
import { VariablesSettingsTab } from './types';
|
||||||
import {
|
import {
|
||||||
@@ -70,9 +69,6 @@ interface DashboardDescriptionProps {
|
|||||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
|
function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
|
||||||
const { handle } = props;
|
const { handle } = props;
|
||||||
const setIsPanelTypeSelectionModalOpen = usePanelTypeSelectionModalStore(
|
|
||||||
(s) => s.setIsPanelTypeSelectionModalOpen,
|
|
||||||
);
|
|
||||||
const {
|
const {
|
||||||
selectedDashboard,
|
selectedDashboard,
|
||||||
panelMap,
|
panelMap,
|
||||||
@@ -81,6 +77,7 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
|
|||||||
setLayouts,
|
setLayouts,
|
||||||
isDashboardLocked,
|
isDashboardLocked,
|
||||||
setSelectedDashboard,
|
setSelectedDashboard,
|
||||||
|
handleToggleDashboardSlider,
|
||||||
handleDashboardLockToggle,
|
handleDashboardLockToggle,
|
||||||
} = useDashboard();
|
} = useDashboard();
|
||||||
|
|
||||||
@@ -148,14 +145,14 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
|
|||||||
const [addPanelPermission] = useComponentPermission(permissions, userRole);
|
const [addPanelPermission] = useComponentPermission(permissions, userRole);
|
||||||
|
|
||||||
const onEmptyWidgetHandler = useCallback(() => {
|
const onEmptyWidgetHandler = useCallback(() => {
|
||||||
setIsPanelTypeSelectionModalOpen(true);
|
handleToggleDashboardSlider(true);
|
||||||
logEvent('Dashboard Detail: Add new panel clicked', {
|
logEvent('Dashboard Detail: Add new panel clicked', {
|
||||||
dashboardId: selectedDashboard?.id,
|
dashboardId: selectedDashboard?.id,
|
||||||
dashboardName: selectedDashboard?.data.title,
|
dashboardName: selectedDashboard?.data.title,
|
||||||
numberOfPanels: selectedDashboard?.data.widgets?.length,
|
numberOfPanels: selectedDashboard?.data.widgets?.length,
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [setIsPanelTypeSelectionModalOpen]);
|
}, [handleToggleDashboardSlider]);
|
||||||
|
|
||||||
const handleLockDashboardToggle = (): void => {
|
const handleLockDashboardToggle = (): void => {
|
||||||
setIsDashbordSettingsOpen(false);
|
setIsDashbordSettingsOpen(false);
|
||||||
@@ -524,7 +521,7 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
|
|||||||
<DashboardVariableSelection />
|
<DashboardVariableSelection />
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
<PanelTypeSelectionModal />
|
<DashboardGraphSlider />
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
open={isRenameDashboardOpen}
|
open={isRenameDashboardOpen}
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
import { memo } from 'react';
|
|
||||||
import { Card, Modal, Typography } from 'antd';
|
|
||||||
import logEvent from 'api/common/logEvent';
|
|
||||||
import { QueryParams } from 'constants/query';
|
|
||||||
import { PANEL_TYPES, PANEL_TYPES_INITIAL_QUERY } from 'constants/queryBuilder';
|
|
||||||
import createQueryParams from 'lib/createQueryParams';
|
|
||||||
import history from 'lib/history';
|
|
||||||
import { usePanelTypeSelectionModalStore } from 'providers/Dashboard/helpers/panelTypeSelectionModalHelper';
|
|
||||||
import { v4 as uuid } from 'uuid';
|
|
||||||
|
|
||||||
import { PanelTypesWithData } from './menuItems';
|
|
||||||
|
|
||||||
import './PanelTypeSelectionModal.styles.scss';
|
|
||||||
|
|
||||||
function PanelTypeSelectionModal(): JSX.Element {
|
|
||||||
const {
|
|
||||||
isPanelTypeSelectionModalOpen,
|
|
||||||
setIsPanelTypeSelectionModalOpen,
|
|
||||||
} = usePanelTypeSelectionModalStore();
|
|
||||||
|
|
||||||
const onClickHandler = (name: PANEL_TYPES) => (): void => {
|
|
||||||
const id = uuid();
|
|
||||||
setIsPanelTypeSelectionModalOpen(false);
|
|
||||||
logEvent('Dashboard Detail: New panel type selected', {
|
|
||||||
panelType: name,
|
|
||||||
widgetId: id,
|
|
||||||
});
|
|
||||||
|
|
||||||
const queryParams = {
|
|
||||||
graphType: name,
|
|
||||||
widgetId: id,
|
|
||||||
[QueryParams.compositeQuery]: JSON.stringify(
|
|
||||||
PANEL_TYPES_INITIAL_QUERY[name],
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
history.push(
|
|
||||||
`${history.location.pathname}/new?${createQueryParams(queryParams)}`,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCardClick = (panelType: PANEL_TYPES): void => {
|
|
||||||
onClickHandler(panelType)();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
open={isPanelTypeSelectionModalOpen}
|
|
||||||
onCancel={(): void => {
|
|
||||||
setIsPanelTypeSelectionModalOpen(false);
|
|
||||||
}}
|
|
||||||
rootClassName="panel-type-selection-modal"
|
|
||||||
footer={null}
|
|
||||||
title="New Panel"
|
|
||||||
>
|
|
||||||
<div className="panel-selection">
|
|
||||||
{PanelTypesWithData.map(({ name, icon, display }) => (
|
|
||||||
<Card onClick={(): void => handleCardClick(name)} id={name} key={name}>
|
|
||||||
{icon}
|
|
||||||
<Typography className="panel-type-text">{display}</Typography>
|
|
||||||
</Card>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(PanelTypeSelectionModal);
|
|
||||||
@@ -224,7 +224,7 @@ describe('TimeSeriesPanel utils', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uses DrawStyle.Line and showPoints false when series has multiple valid points', () => {
|
it('uses DrawStyle.Line and VisibilityMode.Never when series has multiple valid points', () => {
|
||||||
const apiResponse = createApiResponse([
|
const apiResponse = createApiResponse([
|
||||||
{
|
{
|
||||||
metric: {},
|
metric: {},
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import getLabelName from 'lib/getLabelName';
|
|||||||
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
|
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
|
||||||
import {
|
import {
|
||||||
DrawStyle,
|
DrawStyle,
|
||||||
FillMode,
|
|
||||||
LineInterpolation,
|
LineInterpolation,
|
||||||
LineStyle,
|
LineStyle,
|
||||||
|
VisibilityMode,
|
||||||
} from 'lib/uPlotV2/config/types';
|
} from 'lib/uPlotV2/config/types';
|
||||||
import { UPlotConfigBuilder } from 'lib/uPlotV2/config/UPlotConfigBuilder';
|
import { UPlotConfigBuilder } from 'lib/uPlotV2/config/UPlotConfigBuilder';
|
||||||
import { isInvalidPlotValue } from 'lib/uPlotV2/utils/dataUtils';
|
import { isInvalidPlotValue } from 'lib/uPlotV2/utils/dataUtils';
|
||||||
@@ -124,12 +124,12 @@ export const prepareUPlotConfig = ({
|
|||||||
label: label,
|
label: label,
|
||||||
colorMapping: widget.customLegendColors ?? {},
|
colorMapping: widget.customLegendColors ?? {},
|
||||||
spanGaps: true,
|
spanGaps: true,
|
||||||
lineStyle: widget.lineStyle || LineStyle.Solid,
|
lineStyle: LineStyle.Solid,
|
||||||
lineInterpolation: widget.lineInterpolation || LineInterpolation.Spline,
|
lineInterpolation: LineInterpolation.Spline,
|
||||||
showPoints:
|
showPoints: hasSingleValidPoint
|
||||||
widget.showPoints || hasSingleValidPoint ? true : !!widget.showPoints,
|
? VisibilityMode.Always
|
||||||
|
: VisibilityMode.Never,
|
||||||
pointSize: 5,
|
pointSize: 5,
|
||||||
fillMode: widget.fillMode || FillMode.None,
|
|
||||||
isDarkMode,
|
isDarkMode,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,18 +9,17 @@ import DashboardSettings from 'container/DashboardContainer/DashboardSettings';
|
|||||||
import useComponentPermission from 'hooks/useComponentPermission';
|
import useComponentPermission from 'hooks/useComponentPermission';
|
||||||
import { useAppContext } from 'providers/App/App';
|
import { useAppContext } from 'providers/App/App';
|
||||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { usePanelTypeSelectionModalStore } from 'providers/Dashboard/helpers/panelTypeSelectionModalHelper';
|
|
||||||
import { ROLES, USER_ROLES } from 'types/roles';
|
import { ROLES, USER_ROLES } from 'types/roles';
|
||||||
import { ComponentTypes } from 'utils/permission';
|
import { ComponentTypes } from 'utils/permission';
|
||||||
|
|
||||||
import './DashboardEmptyState.styles.scss';
|
import './DashboardEmptyState.styles.scss';
|
||||||
|
|
||||||
export default function DashboardEmptyState(): JSX.Element {
|
export default function DashboardEmptyState(): JSX.Element {
|
||||||
const setIsPanelTypeSelectionModalOpen = usePanelTypeSelectionModalStore(
|
const {
|
||||||
(s) => s.setIsPanelTypeSelectionModalOpen,
|
selectedDashboard,
|
||||||
);
|
isDashboardLocked,
|
||||||
|
handleToggleDashboardSlider,
|
||||||
const { selectedDashboard, isDashboardLocked } = useDashboard();
|
} = useDashboard();
|
||||||
|
|
||||||
const variablesSettingsTabHandle = useRef<VariablesSettingsTab>(null);
|
const variablesSettingsTabHandle = useRef<VariablesSettingsTab>(null);
|
||||||
const [isSettingsDrawerOpen, setIsSettingsDrawerOpen] = useState<boolean>(
|
const [isSettingsDrawerOpen, setIsSettingsDrawerOpen] = useState<boolean>(
|
||||||
@@ -42,14 +41,14 @@ export default function DashboardEmptyState(): JSX.Element {
|
|||||||
const [addPanelPermission] = useComponentPermission(permissions, userRole);
|
const [addPanelPermission] = useComponentPermission(permissions, userRole);
|
||||||
|
|
||||||
const onEmptyWidgetHandler = useCallback(() => {
|
const onEmptyWidgetHandler = useCallback(() => {
|
||||||
setIsPanelTypeSelectionModalOpen(true);
|
handleToggleDashboardSlider(true);
|
||||||
logEvent('Dashboard Detail: Add new panel clicked', {
|
logEvent('Dashboard Detail: Add new panel clicked', {
|
||||||
dashboardId: selectedDashboard?.id,
|
dashboardId: selectedDashboard?.id,
|
||||||
dashboardName: selectedDashboard?.data.title,
|
dashboardName: selectedDashboard?.data.title,
|
||||||
numberOfPanels: selectedDashboard?.data.widgets?.length,
|
numberOfPanels: selectedDashboard?.data.widgets?.length,
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [setIsPanelTypeSelectionModalOpen]);
|
}, [handleToggleDashboardSlider]);
|
||||||
|
|
||||||
const onConfigureClick = useCallback((): void => {
|
const onConfigureClick = useCallback((): void => {
|
||||||
setIsSettingsDrawerOpen(true);
|
setIsSettingsDrawerOpen(true);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useCallback } from 'react';
|
|||||||
import { Select, Typography } from 'antd';
|
import { Select, Typography } from 'antd';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { PanelTypesWithData } from 'container/DashboardContainer/PanelTypeSelectionModal/menuItems';
|
import GraphTypes from 'container/DashboardContainer/ComponentsSlider/menuItems';
|
||||||
import { handleQueryChange } from 'container/NewWidget/utils';
|
import { handleQueryChange } from 'container/NewWidget/utils';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
@@ -59,7 +59,7 @@ function PanelTypeSelector({
|
|||||||
data-testid="panel-change-select"
|
data-testid="panel-change-select"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
{PanelTypesWithData.map((item) => (
|
{GraphTypes.map((item) => (
|
||||||
<Option key={item.name} value={item.name}>
|
<Option key={item.name} value={item.name}>
|
||||||
<div className="view-panel-select-option">
|
<div className="view-panel-select-option">
|
||||||
<div className="icon">{item.icon}</div>
|
<div className="icon">{item.icon}</div>
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import useComponentPermission from 'hooks/useComponentPermission';
|
|||||||
import { EllipsisIcon, PenLine, Plus, X } from 'lucide-react';
|
import { EllipsisIcon, PenLine, Plus, X } from 'lucide-react';
|
||||||
import { useAppContext } from 'providers/App/App';
|
import { useAppContext } from 'providers/App/App';
|
||||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { usePanelTypeSelectionModalStore } from 'providers/Dashboard/helpers/panelTypeSelectionModalHelper';
|
|
||||||
import { setSelectedRowWidgetId } from 'providers/Dashboard/helpers/selectedRowWidgetIdHelper';
|
import { setSelectedRowWidgetId } from 'providers/Dashboard/helpers/selectedRowWidgetIdHelper';
|
||||||
import { ROLES, USER_ROLES } from 'types/roles';
|
import { ROLES, USER_ROLES } from 'types/roles';
|
||||||
import { ComponentTypes } from 'utils/permission';
|
import { ComponentTypes } from 'utils/permission';
|
||||||
@@ -35,11 +34,11 @@ export function WidgetRowHeader(props: WidgetRowHeaderProps): JSX.Element {
|
|||||||
} = props;
|
} = props;
|
||||||
const [isRowSettingsOpen, setIsRowSettingsOpen] = useState<boolean>(false);
|
const [isRowSettingsOpen, setIsRowSettingsOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
const setIsPanelTypeSelectionModalOpen = usePanelTypeSelectionModalStore(
|
const {
|
||||||
(s) => s.setIsPanelTypeSelectionModalOpen,
|
handleToggleDashboardSlider,
|
||||||
);
|
selectedDashboard,
|
||||||
|
isDashboardLocked,
|
||||||
const { selectedDashboard, isDashboardLocked } = useDashboard();
|
} = useDashboard();
|
||||||
|
|
||||||
const permissions: ComponentTypes[] = ['add_panel'];
|
const permissions: ComponentTypes[] = ['add_panel'];
|
||||||
const { user } = useAppContext();
|
const { user } = useAppContext();
|
||||||
@@ -88,7 +87,7 @@ export function WidgetRowHeader(props: WidgetRowHeaderProps): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setSelectedRowWidgetId(selectedDashboard.id, id);
|
setSelectedRowWidgetId(selectedDashboard.id, id);
|
||||||
setIsPanelTypeSelectionModalOpen(true);
|
handleToggleDashboardSlider(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
New Panel
|
New Panel
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import ROUTES from 'constants/routes';
|
|||||||
import { getMetricsListQuery } from 'container/MetricsExplorer/Summary/utils';
|
import { getMetricsListQuery } from 'container/MetricsExplorer/Summary/utils';
|
||||||
import { useGetMetricsList } from 'hooks/metricsExplorer/useGetMetricsList';
|
import { useGetMetricsList } from 'hooks/metricsExplorer/useGetMetricsList';
|
||||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import cloneDeep from 'lodash-es/cloneDeep';
|
import cloneDeep from 'lodash-es/cloneDeep';
|
||||||
import { AnimatePresence } from 'motion/react';
|
import { AnimatePresence } from 'motion/react';
|
||||||
@@ -44,7 +43,6 @@ const homeInterval = 30 * 60 * 1000;
|
|||||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
export default function Home(): JSX.Element {
|
export default function Home(): JSX.Element {
|
||||||
const { user } = useAppContext();
|
const { user } = useAppContext();
|
||||||
const isDarkMode = useIsDarkMode();
|
|
||||||
|
|
||||||
const [startTime, setStartTime] = useState<number | null>(null);
|
const [startTime, setStartTime] = useState<number | null>(null);
|
||||||
const [endTime, setEndTime] = useState<number | null>(null);
|
const [endTime, setEndTime] = useState<number | null>(null);
|
||||||
@@ -682,11 +680,7 @@ export default function Home(): JSX.Element {
|
|||||||
|
|
||||||
<div className="checklist-img-container">
|
<div className="checklist-img-container">
|
||||||
<img
|
<img
|
||||||
src={
|
src="/Images/allInOne.svg"
|
||||||
isDarkMode
|
|
||||||
? '/Images/allInOne.svg'
|
|
||||||
: '/Images/allInOneLightMode.svg'
|
|
||||||
}
|
|
||||||
alt="checklist-img"
|
alt="checklist-img"
|
||||||
className="checklist-img"
|
className="checklist-img"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -65,35 +65,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.new-widget-container {
|
|
||||||
.resizable-panel-left-container {
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resizable-panel-right-container {
|
|
||||||
overflow-y: auto !important;
|
|
||||||
min-width: 350px;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
width: 0.3rem;
|
|
||||||
}
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
background: rgb(136, 136, 136);
|
|
||||||
border-radius: 0.625rem;
|
|
||||||
}
|
|
||||||
&::-webkit-scrollbar-track {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-resizable-panel-group {
|
|
||||||
.widget-resizable-handle {
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.lightMode {
|
.lightMode {
|
||||||
.edit-header {
|
.edit-header {
|
||||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||||
@@ -110,11 +81,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.widget-resizable-panel-group {
|
|
||||||
.bg-border {
|
|
||||||
background: var(--bg-vanilla-300);
|
|
||||||
border-color: var(--bg-vanilla-300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
.fill-mode-selector {
|
|
||||||
.fill-mode-icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fill-mode-label {
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--bg-vanilla-400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.lightMode {
|
|
||||||
.fill-mode-selector {
|
|
||||||
.fill-mode-label {
|
|
||||||
color: var(--bg-ink-400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
import { ToggleGroup, ToggleGroupItem } from '@signozhq/toggle-group';
|
|
||||||
import { Typography } from 'antd';
|
|
||||||
import { FillMode } from 'lib/uPlotV2/config/types';
|
|
||||||
|
|
||||||
import './FillModeSelector.styles.scss';
|
|
||||||
|
|
||||||
interface FillModeSelectorProps {
|
|
||||||
value: FillMode;
|
|
||||||
onChange: (value: FillMode) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function FillModeSelector({
|
|
||||||
value,
|
|
||||||
onChange,
|
|
||||||
}: FillModeSelectorProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<section className="fill-mode-selector control-container">
|
|
||||||
<Typography.Text className="section-heading">Fill mode</Typography.Text>
|
|
||||||
<ToggleGroup
|
|
||||||
type="single"
|
|
||||||
value={value}
|
|
||||||
variant="outline"
|
|
||||||
size="lg"
|
|
||||||
onValueChange={(newValue): void => {
|
|
||||||
if (newValue) {
|
|
||||||
onChange(newValue as FillMode);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ToggleGroupItem value={FillMode.None} aria-label="None" title="None">
|
|
||||||
<svg
|
|
||||||
className="fill-mode-icon"
|
|
||||||
viewBox="0 0 48 48"
|
|
||||||
fill="none"
|
|
||||||
stroke="#888"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
>
|
|
||||||
<rect x="8" y="16" width="32" height="16" stroke="#888" fill="none" />
|
|
||||||
</svg>
|
|
||||||
<Typography.Text className="section-heading-small">None</Typography.Text>
|
|
||||||
</ToggleGroupItem>
|
|
||||||
<ToggleGroupItem value={FillMode.Solid} aria-label="Solid" title="Solid">
|
|
||||||
<svg
|
|
||||||
className="fill-mode-icon"
|
|
||||||
viewBox="0 0 48 48"
|
|
||||||
fill="none"
|
|
||||||
stroke="#888"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
>
|
|
||||||
<rect x="8" y="16" width="32" height="16" fill="#888" />
|
|
||||||
</svg>
|
|
||||||
<Typography.Text className="section-heading-small">Solid</Typography.Text>
|
|
||||||
</ToggleGroupItem>
|
|
||||||
<ToggleGroupItem
|
|
||||||
value={FillMode.Gradient}
|
|
||||||
aria-label="Gradient"
|
|
||||||
title="Gradient"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="fill-mode-icon"
|
|
||||||
viewBox="0 0 48 48"
|
|
||||||
fill="none"
|
|
||||||
stroke="#888"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="fill-gradient" x1="0" y1="0" x2="1" y2="0">
|
|
||||||
<stop offset="0%" stopColor="#888" stopOpacity="0.2" />
|
|
||||||
<stop offset="100%" stopColor="#888" stopOpacity="0.8" />
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<rect
|
|
||||||
x="8"
|
|
||||||
y="16"
|
|
||||||
width="32"
|
|
||||||
height="16"
|
|
||||||
fill="url(#fill-gradient)"
|
|
||||||
stroke="#888"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<Typography.Text className="section-heading-small">
|
|
||||||
Gradient
|
|
||||||
</Typography.Text>
|
|
||||||
</ToggleGroupItem>
|
|
||||||
</ToggleGroup>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
.line-interpolation-selector {
|
|
||||||
.line-interpolation-icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line-interpolation-label {
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--bg-vanilla-400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.lightMode {
|
|
||||||
.line-interpolation-selector {
|
|
||||||
.line-interpolation-label {
|
|
||||||
color: var(--bg-ink-400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
import { ToggleGroup, ToggleGroupItem } from '@signozhq/toggle-group';
|
|
||||||
import { Typography } from 'antd';
|
|
||||||
import { LineInterpolation } from 'lib/uPlotV2/config/types';
|
|
||||||
|
|
||||||
import './LineInterpolationSelector.styles.scss';
|
|
||||||
|
|
||||||
interface LineInterpolationSelectorProps {
|
|
||||||
value: LineInterpolation;
|
|
||||||
onChange: (value: LineInterpolation) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function LineInterpolationSelector({
|
|
||||||
value,
|
|
||||||
onChange,
|
|
||||||
}: LineInterpolationSelectorProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<section className="line-interpolation-selector control-container">
|
|
||||||
<Typography.Text className="section-heading">
|
|
||||||
Line interpolation
|
|
||||||
</Typography.Text>
|
|
||||||
<ToggleGroup
|
|
||||||
type="single"
|
|
||||||
value={value}
|
|
||||||
variant="outline"
|
|
||||||
size="lg"
|
|
||||||
onValueChange={(newValue): void => {
|
|
||||||
if (newValue) {
|
|
||||||
onChange(newValue as LineInterpolation);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ToggleGroupItem
|
|
||||||
value={LineInterpolation.Linear}
|
|
||||||
aria-label="Linear"
|
|
||||||
title="Linear"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="line-interpolation-icon"
|
|
||||||
viewBox="0 0 48 48"
|
|
||||||
fill="none"
|
|
||||||
stroke="#888"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
>
|
|
||||||
<circle cx="8" cy="32" r="3" fill="#888" />
|
|
||||||
<circle cx="24" cy="16" r="3" fill="#888" />
|
|
||||||
<circle cx="40" cy="32" r="3" fill="#888" />
|
|
||||||
<path d="M8 32 L24 16 L40 32" stroke="#888" />
|
|
||||||
</svg>
|
|
||||||
</ToggleGroupItem>
|
|
||||||
<ToggleGroupItem value={LineInterpolation.Spline} aria-label="Spline">
|
|
||||||
<svg
|
|
||||||
className="line-interpolation-icon"
|
|
||||||
viewBox="0 0 48 48"
|
|
||||||
fill="none"
|
|
||||||
stroke="#888"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
>
|
|
||||||
<circle cx="8" cy="32" r="3" fill="#888" />
|
|
||||||
<circle cx="24" cy="16" r="3" fill="#888" />
|
|
||||||
<circle cx="40" cy="32" r="3" fill="#888" />
|
|
||||||
<path d="M8 32 C16 8, 32 8, 40 32" />
|
|
||||||
</svg>
|
|
||||||
</ToggleGroupItem>
|
|
||||||
<ToggleGroupItem
|
|
||||||
value={LineInterpolation.StepAfter}
|
|
||||||
aria-label="Step After"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="line-interpolation-icon"
|
|
||||||
viewBox="0 0 48 48"
|
|
||||||
fill="none"
|
|
||||||
stroke="#888"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
>
|
|
||||||
<circle cx="8" cy="32" r="3" fill="#888" />
|
|
||||||
<circle cx="24" cy="16" r="3" fill="#888" />
|
|
||||||
<circle cx="40" cy="32" r="3" fill="#888" />
|
|
||||||
<path d="M8 32 V16 H24 V32 H40" />
|
|
||||||
</svg>
|
|
||||||
</ToggleGroupItem>
|
|
||||||
|
|
||||||
<ToggleGroupItem
|
|
||||||
value={LineInterpolation.StepBefore}
|
|
||||||
aria-label="Step Before"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="line-interpolation-icon"
|
|
||||||
viewBox="0 0 48 48"
|
|
||||||
fill="none"
|
|
||||||
stroke="#888"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
>
|
|
||||||
<circle cx="8" cy="32" r="3" fill="#888" />
|
|
||||||
<circle cx="24" cy="16" r="3" fill="#888" />
|
|
||||||
<circle cx="40" cy="32" r="3" fill="#888" />
|
|
||||||
<path d="M8 32 H24 V16 H40 V32" />
|
|
||||||
</svg>
|
|
||||||
</ToggleGroupItem>
|
|
||||||
</ToggleGroup>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
.line-style-selector {
|
|
||||||
.line-style-icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line-style-label {
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--bg-vanilla-400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.lightMode {
|
|
||||||
.line-style-selector {
|
|
||||||
.line-style-label {
|
|
||||||
color: var(--bg-ink-400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
import { ToggleGroup, ToggleGroupItem } from '@signozhq/toggle-group';
|
|
||||||
import { Typography } from 'antd';
|
|
||||||
import { LineStyle } from 'lib/uPlotV2/config/types';
|
|
||||||
|
|
||||||
import './LineStyleSelector.styles.scss';
|
|
||||||
|
|
||||||
interface LineStyleSelectorProps {
|
|
||||||
value: LineStyle;
|
|
||||||
onChange: (value: LineStyle) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function LineStyleSelector({
|
|
||||||
value,
|
|
||||||
onChange,
|
|
||||||
}: LineStyleSelectorProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<section className="line-style-selector control-container">
|
|
||||||
<Typography.Text className="section-heading">Line style</Typography.Text>
|
|
||||||
<ToggleGroup
|
|
||||||
type="single"
|
|
||||||
value={value}
|
|
||||||
variant="outline"
|
|
||||||
size="lg"
|
|
||||||
onValueChange={(newValue): void => {
|
|
||||||
if (newValue) {
|
|
||||||
onChange(newValue as LineStyle);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ToggleGroupItem value={LineStyle.Solid} aria-label="Solid" title="Solid">
|
|
||||||
<svg
|
|
||||||
className="line-style-icon"
|
|
||||||
viewBox="0 0 48 48"
|
|
||||||
fill="none"
|
|
||||||
stroke="#888"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
>
|
|
||||||
<path d="M8 24 L40 24" />
|
|
||||||
</svg>
|
|
||||||
<Typography.Text className="section-heading-small">Solid</Typography.Text>
|
|
||||||
</ToggleGroupItem>
|
|
||||||
<ToggleGroupItem
|
|
||||||
value={LineStyle.Dashed}
|
|
||||||
aria-label="Dashed"
|
|
||||||
title="Dashed"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="line-style-icon"
|
|
||||||
viewBox="0 0 48 48"
|
|
||||||
fill="none"
|
|
||||||
stroke="#888"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeDasharray="6 4"
|
|
||||||
>
|
|
||||||
<path d="M8 24 L40 24" />
|
|
||||||
</svg>
|
|
||||||
<Typography.Text className="section-heading-small">Dashed</Typography.Text>
|
|
||||||
</ToggleGroupItem>
|
|
||||||
</ToggleGroup>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,35 +1,8 @@
|
|||||||
.right-container {
|
.right-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
font-family: 'Space Mono';
|
|
||||||
padding-bottom: 48px;
|
padding-bottom: 48px;
|
||||||
|
|
||||||
.section-heading {
|
|
||||||
font-family: 'Space Mono';
|
|
||||||
color: var(--bg-vanilla-400);
|
|
||||||
font-size: 13px;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 18px; /* 138.462% */
|
|
||||||
letter-spacing: 0.52px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-heading-small {
|
|
||||||
font-family: 'Space Mono';
|
|
||||||
color: var(--bg-vanilla-400);
|
|
||||||
font-size: 12px;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
word-break: initial;
|
|
||||||
line-height: 16px; /* 133.333% */
|
|
||||||
letter-spacing: 0.48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-type-select {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 14px 14px 14px 12px;
|
padding: 14px 14px 14px 12px;
|
||||||
@@ -58,25 +31,74 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.name-description {
|
||||||
|
padding: 0 0 4px 0;
|
||||||
|
|
||||||
|
.typography {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
font-family: 'Space Mono';
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 18px; /* 138.462% */
|
||||||
|
letter-spacing: 0.52px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name-input {
|
||||||
|
display: flex;
|
||||||
|
padding: 6px 6px 6px 8px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
flex: 1 0 0;
|
||||||
|
align-self: stretch;
|
||||||
|
border-radius: 2px;
|
||||||
|
border: 1px solid var(--bg-slate-400);
|
||||||
|
background: var(--bg-ink-300);
|
||||||
|
color: var(--bg-vanilla-100);
|
||||||
|
font-family: Inter;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 18px; /* 128.571% */
|
||||||
|
letter-spacing: -0.07px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-input {
|
||||||
|
border-style: unset;
|
||||||
|
.ant-input {
|
||||||
|
display: flex;
|
||||||
|
height: 80px;
|
||||||
|
padding: 6px 6px 6px 8px;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 4px;
|
||||||
|
border-radius: 2px;
|
||||||
|
border: 1px solid var(--bg-slate-400);
|
||||||
|
background: var(--bg-ink-300);
|
||||||
|
color: var(--bg-vanilla-100);
|
||||||
|
font-family: Inter;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 18px; /* 128.571% */
|
||||||
|
letter-spacing: -0.07px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.panel-config {
|
.panel-config {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
.toggle-card {
|
.typography {
|
||||||
display: flex;
|
color: var(--bg-vanilla-400);
|
||||||
flex-direction: row;
|
font-family: 'Space Mono';
|
||||||
justify-content: space-between;
|
font-size: 13px;
|
||||||
align-items: center;
|
font-style: normal;
|
||||||
padding: 12px;
|
font-weight: 400;
|
||||||
border-radius: 2px;
|
line-height: 18px; /* 138.462% */
|
||||||
border: 1px solid var(--bg-slate-400);
|
letter-spacing: 0.52px;
|
||||||
background: var(--bg-ink-400);
|
text-transform: uppercase;
|
||||||
}
|
|
||||||
|
|
||||||
.toggle-card-text-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-type-select {
|
.panel-type-select {
|
||||||
@@ -92,16 +114,55 @@
|
|||||||
border: 1px solid var(--bg-slate-400);
|
border: 1px solid var(--bg-slate-400);
|
||||||
background: var(--bg-ink-300);
|
background: var(--bg-ink-300);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.select-option {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
.icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.display {
|
||||||
|
color: var(--bg-vanilla-100);
|
||||||
|
font-family: Inter;
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 16px; /* 133.333% */
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-card-description {
|
.fill-gaps {
|
||||||
color: var(--bg-vanilla-400);
|
display: flex;
|
||||||
font-family: Inter;
|
padding: 12px;
|
||||||
font-size: 12px;
|
justify-content: space-between;
|
||||||
font-style: normal;
|
align-items: center;
|
||||||
font-weight: 400;
|
border-radius: 2px;
|
||||||
opacity: 0.6;
|
border: 1px solid var(--bg-slate-400);
|
||||||
line-height: 16px; /* 133.333% */
|
background: var(--bg-ink-400);
|
||||||
|
|
||||||
|
.fill-gaps-text {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
font-family: 'Space Mono';
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 18px; /* 138.462% */
|
||||||
|
letter-spacing: 0.52px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
.fill-gaps-text-description {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
font-family: Inter;
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
opacity: 0.6;
|
||||||
|
line-height: 16px; /* 133.333% */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.log-scale,
|
.log-scale,
|
||||||
@@ -110,6 +171,17 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.panel-time-text {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
font-family: 'Space Mono';
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 18px; /* 138.462% */
|
||||||
|
letter-spacing: 0.52px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
.y-axis-unit-selector,
|
.y-axis-unit-selector,
|
||||||
.y-axis-unit-selector-v2 {
|
.y-axis-unit-selector-v2 {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -117,7 +189,14 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
||||||
.heading {
|
.heading {
|
||||||
@extend .section-heading;
|
color: var(--bg-vanilla-400);
|
||||||
|
font-family: 'Space Mono';
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 18px; /* 138.462% */
|
||||||
|
letter-spacing: 0.52px;
|
||||||
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
@@ -170,6 +249,7 @@
|
|||||||
|
|
||||||
.text {
|
.text {
|
||||||
color: var(--bg-vanilla-400);
|
color: var(--bg-vanilla-400);
|
||||||
|
font-family: 'Space Mono';
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
@@ -190,7 +270,111 @@
|
|||||||
.stack-chart {
|
.stack-chart {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
.label {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
font-family: 'Space Mono';
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 18px; /* 138.462% */
|
||||||
|
letter-spacing: 0.52px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bucket-config {
|
||||||
|
.label {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
font-family: 'Space Mono';
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 18px; /* 138.462% */
|
||||||
|
letter-spacing: 0.52px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bucket-size-label {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bucket-input {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 32px;
|
||||||
|
padding: 6px 6px 6px 8px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
align-self: stretch;
|
||||||
|
border-radius: 2px;
|
||||||
|
border: 1px solid var(--bg-slate-400);
|
||||||
|
background: var(--bg-ink-300);
|
||||||
|
|
||||||
|
.ant-input {
|
||||||
|
background: var(--bg-ink-300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.combine-hist {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 8px;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
font-family: 'Space Mono';
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 18px; /* 138.462% */
|
||||||
|
letter-spacing: 0.52px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.alerts {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px;
|
||||||
|
min-height: 44px;
|
||||||
|
border-top: 1px solid var(--bg-slate-500);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.left-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.bell-icon {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alerts-text {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
font-family: Inter;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px; /* 142.857% */
|
||||||
|
letter-spacing: 0.14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.plus-icon {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.context-links {
|
||||||
|
padding: 12px 12px 16px 12px;
|
||||||
|
border-bottom: 1px solid var(--bg-slate-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
.thresholds-section {
|
||||||
|
padding: 12px 12px 16px 12px;
|
||||||
|
border-top: 1px solid var(--bg-slate-500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,16 +400,37 @@
|
|||||||
.lightMode {
|
.lightMode {
|
||||||
.right-container {
|
.right-container {
|
||||||
background-color: var(--bg-vanilla-100);
|
background-color: var(--bg-vanilla-100);
|
||||||
.section-heading {
|
|
||||||
color: var(--bg-ink-400);
|
|
||||||
}
|
|
||||||
.header {
|
.header {
|
||||||
.header-text {
|
.header-text {
|
||||||
color: var(--bg-ink-400);
|
color: var(--bg-ink-400);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.name-description {
|
||||||
|
.typography {
|
||||||
|
color: var(--bg-ink-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
.name-input {
|
||||||
|
border: 1px solid var(--bg-vanilla-300);
|
||||||
|
background: var(--bg-vanilla-300);
|
||||||
|
color: var(--bg-ink-300);
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-input {
|
||||||
|
.ant-input {
|
||||||
|
border: 1px solid var(--bg-vanilla-300);
|
||||||
|
background: var(--bg-vanilla-300);
|
||||||
|
color: var(--bg-ink-300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.panel-config {
|
.panel-config {
|
||||||
|
.typography {
|
||||||
|
color: var(--bg-ink-400);
|
||||||
|
}
|
||||||
|
|
||||||
.panel-type-select {
|
.panel-type-select {
|
||||||
.ant-select-selector {
|
.ant-select-selector {
|
||||||
border: 1px solid var(--bg-vanilla-300);
|
border: 1px solid var(--bg-vanilla-300);
|
||||||
@@ -250,18 +455,33 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-card {
|
.fill-gaps {
|
||||||
border: 1px solid var(--bg-vanilla-300);
|
border: 1px solid var(--bg-vanilla-300);
|
||||||
background: var(--bg-vanilla-300);
|
background: var(--bg-vanilla-300);
|
||||||
|
|
||||||
.fill-gaps-text {
|
.fill-gaps-text {
|
||||||
color: var(--bg-ink-400);
|
color: var(--bg-ink-400);
|
||||||
}
|
}
|
||||||
.toggle-card-description {
|
.fill-gaps-text-description {
|
||||||
color: var(--bg-ink-400);
|
color: var(--bg-ink-400);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bucket-config {
|
||||||
|
.label {
|
||||||
|
color: var(--bg-ink-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bucket-input {
|
||||||
|
border: 1px solid var(--bg-vanilla-300);
|
||||||
|
background: var(--bg-vanilla-300);
|
||||||
|
|
||||||
|
.ant-input {
|
||||||
|
background: var(--bg-vanilla-300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.panel-time-text {
|
.panel-time-text {
|
||||||
color: var(--bg-ink-400);
|
color: var(--bg-ink-400);
|
||||||
}
|
}
|
||||||
@@ -295,6 +515,31 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.alerts {
|
||||||
|
border-top: 1px solid var(--bg-vanilla-300);
|
||||||
|
|
||||||
|
.left-section {
|
||||||
|
.bell-icon {
|
||||||
|
color: var(--bg-ink-300);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alerts-text {
|
||||||
|
color: var(--bg-ink-300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.plus-icon {
|
||||||
|
color: var(--bg-ink-300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.context-links {
|
||||||
|
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||||
|
}
|
||||||
|
|
||||||
|
.thresholds-section {
|
||||||
|
border-top: 1px solid var(--bg-vanilla-300);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.select-option {
|
.select-option {
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
.alerts-section {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 12px;
|
|
||||||
min-height: 44px;
|
|
||||||
border-top: 1px solid var(--bg-slate-500);
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.alerts-section__left {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
|
|
||||||
.alerts-section__bell-icon {
|
|
||||||
color: var(--bg-vanilla-400);
|
|
||||||
}
|
|
||||||
|
|
||||||
.alerts-section__text {
|
|
||||||
color: var(--bg-vanilla-400);
|
|
||||||
font-size: 14px;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 20px; /* 142.857% */
|
|
||||||
letter-spacing: 0.14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.alerts-section__plus-icon {
|
|
||||||
color: var(--bg-vanilla-400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.lightMode {
|
|
||||||
.alerts-section {
|
|
||||||
border-top: 1px solid var(--bg-vanilla-300);
|
|
||||||
|
|
||||||
.alerts-section__left {
|
|
||||||
.alerts-section__bell-icon {
|
|
||||||
color: var(--bg-ink-300);
|
|
||||||
}
|
|
||||||
|
|
||||||
.alerts-section__text {
|
|
||||||
color: var(--bg-ink-300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.alerts-section__plus-icon {
|
|
||||||
color: var(--bg-ink-300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { Typography } from 'antd';
|
|
||||||
import { ConciergeBell, Plus, SquareArrowOutUpRight } from 'lucide-react';
|
|
||||||
|
|
||||||
import './AlertsSection.styles.scss';
|
|
||||||
|
|
||||||
interface AlertsSectionProps {
|
|
||||||
onCreateAlertsHandler: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function AlertsSection({
|
|
||||||
onCreateAlertsHandler,
|
|
||||||
}: AlertsSectionProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<section className="alerts-section" onClick={onCreateAlertsHandler}>
|
|
||||||
<div className="alerts-section__left">
|
|
||||||
<ConciergeBell size={14} className="alerts-section__bell-icon" />
|
|
||||||
<Typography.Text className="alerts-section__text">Alerts</Typography.Text>
|
|
||||||
<SquareArrowOutUpRight size={10} className="info-icon" />
|
|
||||||
</div>
|
|
||||||
<Plus size={14} className="alerts-section__plus-icon" />
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
import { Dispatch, SetStateAction } from 'react';
|
|
||||||
import { InputNumber, Select, Typography } from 'antd';
|
|
||||||
import { Axis3D, LineChart, Spline } from 'lucide-react';
|
|
||||||
|
|
||||||
import SettingsSection from '../../components/SettingsSection/SettingsSection';
|
|
||||||
|
|
||||||
enum LogScale {
|
|
||||||
LINEAR = 'linear',
|
|
||||||
LOGARITHMIC = 'logarithmic',
|
|
||||||
}
|
|
||||||
|
|
||||||
const { Option } = Select;
|
|
||||||
|
|
||||||
interface AxesSectionProps {
|
|
||||||
allowSoftMinMax: boolean;
|
|
||||||
allowLogScale: boolean;
|
|
||||||
softMin: number | null;
|
|
||||||
softMax: number | null;
|
|
||||||
setSoftMin: Dispatch<SetStateAction<number | null>>;
|
|
||||||
setSoftMax: Dispatch<SetStateAction<number | null>>;
|
|
||||||
isLogScale: boolean;
|
|
||||||
setIsLogScale: Dispatch<SetStateAction<boolean>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function AxesSection({
|
|
||||||
allowSoftMinMax,
|
|
||||||
allowLogScale,
|
|
||||||
softMin,
|
|
||||||
softMax,
|
|
||||||
setSoftMin,
|
|
||||||
setSoftMax,
|
|
||||||
isLogScale,
|
|
||||||
setIsLogScale,
|
|
||||||
}: AxesSectionProps): JSX.Element {
|
|
||||||
const softMinHandler = (value: number | null): void => {
|
|
||||||
setSoftMin(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const softMaxHandler = (value: number | null): void => {
|
|
||||||
setSoftMax(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SettingsSection title="Axes" icon={<Axis3D size={14} />}>
|
|
||||||
{allowSoftMinMax && (
|
|
||||||
<section className="soft-min-max">
|
|
||||||
<section className="container">
|
|
||||||
<Typography.Text className="text">Soft Min</Typography.Text>
|
|
||||||
<InputNumber
|
|
||||||
type="number"
|
|
||||||
value={softMin}
|
|
||||||
onChange={softMinHandler}
|
|
||||||
rootClassName="input"
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
<section className="container">
|
|
||||||
<Typography.Text className="text">Soft Max</Typography.Text>
|
|
||||||
<InputNumber
|
|
||||||
value={softMax}
|
|
||||||
type="number"
|
|
||||||
rootClassName="input"
|
|
||||||
onChange={softMaxHandler}
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{allowLogScale && (
|
|
||||||
<section className="log-scale control-container">
|
|
||||||
<Typography.Text className="section-heading">Y Axis Scale</Typography.Text>
|
|
||||||
<Select
|
|
||||||
onChange={(value): void => setIsLogScale(value === LogScale.LOGARITHMIC)}
|
|
||||||
value={isLogScale ? LogScale.LOGARITHMIC : LogScale.LINEAR}
|
|
||||||
className="panel-type-select"
|
|
||||||
defaultValue={LogScale.LINEAR}
|
|
||||||
>
|
|
||||||
<Option value={LogScale.LINEAR}>
|
|
||||||
<div className="select-option">
|
|
||||||
<div className="icon">
|
|
||||||
<LineChart size={16} />
|
|
||||||
</div>
|
|
||||||
<Typography.Text className="display">Linear</Typography.Text>
|
|
||||||
</div>
|
|
||||||
</Option>
|
|
||||||
<Option value={LogScale.LOGARITHMIC}>
|
|
||||||
<div className="select-option">
|
|
||||||
<div className="icon">
|
|
||||||
<Spline size={16} />
|
|
||||||
</div>
|
|
||||||
<Typography.Text className="display">Logarithmic</Typography.Text>
|
|
||||||
</div>
|
|
||||||
</Option>
|
|
||||||
</Select>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
</SettingsSection>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
import { Dispatch, SetStateAction } from 'react';
|
|
||||||
import { Switch, Typography } from 'antd';
|
|
||||||
import {
|
|
||||||
FillMode,
|
|
||||||
LineInterpolation,
|
|
||||||
LineStyle,
|
|
||||||
} from 'lib/uPlotV2/config/types';
|
|
||||||
import { Paintbrush } from 'lucide-react';
|
|
||||||
|
|
||||||
import { FillModeSelector } from '../../components/FillModeSelector/FillModeSelector';
|
|
||||||
import { LineInterpolationSelector } from '../../components/LineInterpolationSelector/LineInterpolationSelector';
|
|
||||||
import { LineStyleSelector } from '../../components/LineStyleSelector/LineStyleSelector';
|
|
||||||
import SettingsSection from '../../components/SettingsSection/SettingsSection';
|
|
||||||
|
|
||||||
interface ChartAppearanceSectionProps {
|
|
||||||
fillMode: FillMode;
|
|
||||||
setFillMode: Dispatch<SetStateAction<FillMode>>;
|
|
||||||
lineStyle: LineStyle;
|
|
||||||
setLineStyle: Dispatch<SetStateAction<LineStyle>>;
|
|
||||||
lineInterpolation: LineInterpolation;
|
|
||||||
setLineInterpolation: Dispatch<SetStateAction<LineInterpolation>>;
|
|
||||||
showPoints: boolean;
|
|
||||||
setShowPoints: Dispatch<SetStateAction<boolean>>;
|
|
||||||
allowFillMode: boolean;
|
|
||||||
allowLineStyle: boolean;
|
|
||||||
allowLineInterpolation: boolean;
|
|
||||||
allowShowPoints: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ChartAppearanceSection({
|
|
||||||
fillMode,
|
|
||||||
setFillMode,
|
|
||||||
lineStyle,
|
|
||||||
setLineStyle,
|
|
||||||
lineInterpolation,
|
|
||||||
setLineInterpolation,
|
|
||||||
showPoints,
|
|
||||||
setShowPoints,
|
|
||||||
allowFillMode,
|
|
||||||
allowLineStyle,
|
|
||||||
allowLineInterpolation,
|
|
||||||
allowShowPoints,
|
|
||||||
}: ChartAppearanceSectionProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<SettingsSection title="Chart Appearance" icon={<Paintbrush size={14} />}>
|
|
||||||
{allowFillMode && (
|
|
||||||
<FillModeSelector value={fillMode} onChange={setFillMode} />
|
|
||||||
)}
|
|
||||||
{allowLineStyle && (
|
|
||||||
<LineStyleSelector value={lineStyle} onChange={setLineStyle} />
|
|
||||||
)}
|
|
||||||
{allowLineInterpolation && (
|
|
||||||
<LineInterpolationSelector
|
|
||||||
value={lineInterpolation}
|
|
||||||
onChange={setLineInterpolation}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{allowShowPoints && (
|
|
||||||
<section className="show-points toggle-card">
|
|
||||||
<div className="toggle-card-text-container">
|
|
||||||
<Typography.Text className="section-heading">Show points</Typography.Text>
|
|
||||||
<Typography.Text className="toggle-card-description">
|
|
||||||
Display individual data points on the chart
|
|
||||||
</Typography.Text>
|
|
||||||
</div>
|
|
||||||
<Switch size="small" checked={showPoints} onChange={setShowPoints} />
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
</SettingsSection>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
.context-links-section {
|
|
||||||
padding: 12px 12px 16px 12px;
|
|
||||||
border-bottom: 1px solid var(--bg-slate-500);
|
|
||||||
}
|
|
||||||
|
|
||||||
.lightMode {
|
|
||||||
.context-links-section {
|
|
||||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import { Dispatch, SetStateAction } from 'react';
|
|
||||||
import { Link as LinkIcon } from 'lucide-react';
|
|
||||||
import { ContextLinksData, Widgets } from 'types/api/dashboard/getAll';
|
|
||||||
|
|
||||||
import SettingsSection from '../../components/SettingsSection/SettingsSection';
|
|
||||||
import ContextLinks from '../../ContextLinks';
|
|
||||||
|
|
||||||
import './ContextLinksSection.styles.scss';
|
|
||||||
|
|
||||||
interface ContextLinksSectionProps {
|
|
||||||
contextLinks: ContextLinksData;
|
|
||||||
setContextLinks: Dispatch<SetStateAction<ContextLinksData>>;
|
|
||||||
selectedWidget?: Widgets;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ContextLinksSection({
|
|
||||||
contextLinks,
|
|
||||||
setContextLinks,
|
|
||||||
selectedWidget,
|
|
||||||
}: ContextLinksSectionProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<SettingsSection
|
|
||||||
title="Context Links"
|
|
||||||
icon={<LinkIcon size={14} />}
|
|
||||||
defaultOpen={!!contextLinks.linksData.length}
|
|
||||||
>
|
|
||||||
<div className="context-links-section">
|
|
||||||
<ContextLinks
|
|
||||||
contextLinks={contextLinks}
|
|
||||||
setContextLinks={setContextLinks}
|
|
||||||
selectedWidget={selectedWidget}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</SettingsSection>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
import { Dispatch, SetStateAction } from 'react';
|
|
||||||
import { Select, Typography } from 'antd';
|
|
||||||
import { PrecisionOption } from 'components/Graph/types';
|
|
||||||
import { PanelDisplay } from 'constants/queryBuilder';
|
|
||||||
import { SlidersHorizontal } from 'lucide-react';
|
|
||||||
import { ColumnUnit } from 'types/api/dashboard/getAll';
|
|
||||||
|
|
||||||
import { ColumnUnitSelector } from '../../ColumnUnitSelector/ColumnUnitSelector';
|
|
||||||
import SettingsSection from '../../components/SettingsSection/SettingsSection';
|
|
||||||
import DashboardYAxisUnitSelectorWrapper from '../../DashboardYAxisUnitSelectorWrapper';
|
|
||||||
|
|
||||||
interface FormattingUnitsSectionProps {
|
|
||||||
selectedPanelDisplay: PanelDisplay | '';
|
|
||||||
yAxisUnit: string;
|
|
||||||
setYAxisUnit: Dispatch<SetStateAction<string>>;
|
|
||||||
isNewDashboard: boolean;
|
|
||||||
decimalPrecision: PrecisionOption;
|
|
||||||
setDecimalPrecision: Dispatch<SetStateAction<PrecisionOption>>;
|
|
||||||
columnUnits: ColumnUnit;
|
|
||||||
setColumnUnits: Dispatch<SetStateAction<ColumnUnit>>;
|
|
||||||
allowYAxisUnit: boolean;
|
|
||||||
allowDecimalPrecision: boolean;
|
|
||||||
allowPanelColumnPreference: boolean;
|
|
||||||
decimapPrecisionOptions: { label: string; value: PrecisionOption }[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function FormattingUnitsSection({
|
|
||||||
selectedPanelDisplay,
|
|
||||||
yAxisUnit,
|
|
||||||
setYAxisUnit,
|
|
||||||
isNewDashboard,
|
|
||||||
decimalPrecision,
|
|
||||||
setDecimalPrecision,
|
|
||||||
columnUnits,
|
|
||||||
setColumnUnits,
|
|
||||||
allowYAxisUnit,
|
|
||||||
allowDecimalPrecision,
|
|
||||||
allowPanelColumnPreference,
|
|
||||||
decimapPrecisionOptions,
|
|
||||||
}: FormattingUnitsSectionProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<SettingsSection
|
|
||||||
title="Formatting & Units"
|
|
||||||
icon={<SlidersHorizontal size={14} />}
|
|
||||||
>
|
|
||||||
{allowYAxisUnit && (
|
|
||||||
<DashboardYAxisUnitSelectorWrapper
|
|
||||||
onSelect={setYAxisUnit}
|
|
||||||
value={yAxisUnit || ''}
|
|
||||||
fieldLabel={
|
|
||||||
selectedPanelDisplay === PanelDisplay.VALUE ||
|
|
||||||
selectedPanelDisplay === PanelDisplay.PIE
|
|
||||||
? 'Unit'
|
|
||||||
: 'Y Axis Unit'
|
|
||||||
}
|
|
||||||
shouldUpdateYAxisUnit={isNewDashboard}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{allowDecimalPrecision && (
|
|
||||||
<section className="decimal-precision-selector control-container">
|
|
||||||
<Typography.Text className="section-heading">
|
|
||||||
Decimal Precision
|
|
||||||
</Typography.Text>
|
|
||||||
<Select
|
|
||||||
options={decimapPrecisionOptions}
|
|
||||||
value={decimalPrecision}
|
|
||||||
className="panel-type-select"
|
|
||||||
defaultValue={decimapPrecisionOptions[0]?.value}
|
|
||||||
onChange={(val: PrecisionOption): void => setDecimalPrecision(val)}
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{allowPanelColumnPreference && (
|
|
||||||
<ColumnUnitSelector
|
|
||||||
columnUnits={columnUnits}
|
|
||||||
setColumnUnits={setColumnUnits}
|
|
||||||
isNewDashboard={isNewDashboard}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</SettingsSection>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
.general-settings__name-description {
|
|
||||||
padding: 0 0 4px 0;
|
|
||||||
|
|
||||||
.general-settings__name-input {
|
|
||||||
display: flex;
|
|
||||||
padding: 6px 6px 6px 8px;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
flex: 1 0 0;
|
|
||||||
align-self: stretch;
|
|
||||||
border-radius: 2px;
|
|
||||||
border: 1px solid var(--bg-slate-400);
|
|
||||||
background: var(--bg-ink-300);
|
|
||||||
color: var(--bg-vanilla-100);
|
|
||||||
font-family: Inter;
|
|
||||||
font-size: 14px;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 18px; /* 128.571% */
|
|
||||||
letter-spacing: -0.07px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.general-settings__description-input {
|
|
||||||
border-style: unset;
|
|
||||||
.ant-input {
|
|
||||||
display: flex;
|
|
||||||
height: 80px;
|
|
||||||
padding: 6px 6px 6px 8px;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 4px;
|
|
||||||
border-radius: 2px;
|
|
||||||
border: 1px solid var(--bg-slate-400);
|
|
||||||
background: var(--bg-ink-300);
|
|
||||||
color: var(--bg-vanilla-100);
|
|
||||||
font-family: Inter;
|
|
||||||
font-size: 14px;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 18px; /* 128.571% */
|
|
||||||
letter-spacing: -0.07px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.lightMode {
|
|
||||||
.general-settings__name-description {
|
|
||||||
border-top: 1px solid var(--bg-vanilla-300);
|
|
||||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
|
||||||
|
|
||||||
.general-settings__name-input {
|
|
||||||
border: 1px solid var(--bg-vanilla-300);
|
|
||||||
background: var(--bg-vanilla-300);
|
|
||||||
color: var(--bg-ink-300);
|
|
||||||
}
|
|
||||||
|
|
||||||
.general-settings__description-input {
|
|
||||||
.ant-input {
|
|
||||||
border: 1px solid var(--bg-vanilla-300);
|
|
||||||
background: var(--bg-vanilla-300);
|
|
||||||
color: var(--bg-ink-300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
import {
|
|
||||||
Dispatch,
|
|
||||||
SetStateAction,
|
|
||||||
useCallback,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import type { InputRef } from 'antd';
|
|
||||||
import { AutoComplete, Input, Typography } from 'antd';
|
|
||||||
import { popupContainer } from 'utils/selectPopupContainer';
|
|
||||||
|
|
||||||
import SettingsSection from '../../components/SettingsSection/SettingsSection';
|
|
||||||
|
|
||||||
import './GeneralSettingsSection.styles.scss';
|
|
||||||
|
|
||||||
const { TextArea } = Input;
|
|
||||||
|
|
||||||
interface VariableOption {
|
|
||||||
value: string;
|
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GeneralSettingsSectionProps {
|
|
||||||
title: string;
|
|
||||||
setTitle: Dispatch<SetStateAction<string>>;
|
|
||||||
description: string;
|
|
||||||
setDescription: Dispatch<SetStateAction<string>>;
|
|
||||||
dashboardVariables: Record<string, { name?: string }>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function GeneralSettingsSection({
|
|
||||||
title,
|
|
||||||
setTitle,
|
|
||||||
description,
|
|
||||||
setDescription,
|
|
||||||
dashboardVariables,
|
|
||||||
}: GeneralSettingsSectionProps): JSX.Element {
|
|
||||||
const [inputValue, setInputValue] = useState(title);
|
|
||||||
const [autoCompleteOpen, setAutoCompleteOpen] = useState(false);
|
|
||||||
const [cursorPos, setCursorPos] = useState(0);
|
|
||||||
const inputRef = useRef<InputRef>(null);
|
|
||||||
|
|
||||||
const onChangeHandler = (
|
|
||||||
setFunc: Dispatch<SetStateAction<string>>,
|
|
||||||
value: string,
|
|
||||||
): void => {
|
|
||||||
setFunc(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const dashboardVariableOptions = useMemo<VariableOption[]>(() => {
|
|
||||||
return Object.entries(dashboardVariables).map(([, value]) => ({
|
|
||||||
value: value.name || '',
|
|
||||||
label: value.name || '',
|
|
||||||
}));
|
|
||||||
}, [dashboardVariables]);
|
|
||||||
|
|
||||||
const updateCursorAndDropdown = useCallback(
|
|
||||||
(value: string, pos: number): void => {
|
|
||||||
setCursorPos(pos);
|
|
||||||
const lastDollar = value.lastIndexOf('$', pos - 1);
|
|
||||||
setAutoCompleteOpen(lastDollar !== -1 && pos >= lastDollar + 1);
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onInputChange = useCallback(
|
|
||||||
(value: string): void => {
|
|
||||||
setInputValue(value);
|
|
||||||
onChangeHandler(setTitle, value);
|
|
||||||
setTimeout(() => {
|
|
||||||
const pos = inputRef.current?.input?.selectionStart ?? 0;
|
|
||||||
updateCursorAndDropdown(value, pos);
|
|
||||||
}, 0);
|
|
||||||
},
|
|
||||||
[setTitle, updateCursorAndDropdown],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onSelect = useCallback(
|
|
||||||
(selectedValue: string): void => {
|
|
||||||
const pos = cursorPos;
|
|
||||||
const value = inputValue;
|
|
||||||
const lastDollar = value.lastIndexOf('$', pos - 1);
|
|
||||||
const textBeforeDollar = value.substring(0, lastDollar);
|
|
||||||
const textAfterDollar = value.substring(lastDollar + 1);
|
|
||||||
const match = textAfterDollar.match(/^([a-zA-Z0-9_.]*)/);
|
|
||||||
const rest = textAfterDollar.substring(match ? match[1].length : 0);
|
|
||||||
const newValue = `${textBeforeDollar}$${selectedValue}${rest}`;
|
|
||||||
setInputValue(newValue);
|
|
||||||
onChangeHandler(setTitle, newValue);
|
|
||||||
setAutoCompleteOpen(false);
|
|
||||||
setTimeout(() => {
|
|
||||||
const newCursor = `${textBeforeDollar}$${selectedValue}`.length;
|
|
||||||
inputRef.current?.input?.setSelectionRange(newCursor, newCursor);
|
|
||||||
setCursorPos(newCursor);
|
|
||||||
}, 0);
|
|
||||||
},
|
|
||||||
[cursorPos, inputValue, setTitle],
|
|
||||||
);
|
|
||||||
|
|
||||||
const filterOption = useCallback(
|
|
||||||
(currentInputValue: string, option?: VariableOption): boolean => {
|
|
||||||
const pos = cursorPos;
|
|
||||||
const value = currentInputValue;
|
|
||||||
const lastDollar = value.lastIndexOf('$', pos - 1);
|
|
||||||
if (lastDollar === -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const afterDollar = value.substring(lastDollar + 1, pos).toLowerCase();
|
|
||||||
return option?.value.toLowerCase().startsWith(afterDollar) || false;
|
|
||||||
},
|
|
||||||
[cursorPos],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleInputCursor = useCallback((): void => {
|
|
||||||
const pos = inputRef.current?.input?.selectionStart ?? 0;
|
|
||||||
updateCursorAndDropdown(inputValue, pos);
|
|
||||||
}, [inputValue, updateCursorAndDropdown]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SettingsSection title="General" defaultOpen icon={null}>
|
|
||||||
<section className="general-settings__name-description control-container">
|
|
||||||
<Typography.Text className="section-heading">Name</Typography.Text>
|
|
||||||
<AutoComplete
|
|
||||||
options={dashboardVariableOptions}
|
|
||||||
value={inputValue}
|
|
||||||
onChange={onInputChange}
|
|
||||||
onSelect={onSelect}
|
|
||||||
filterOption={filterOption}
|
|
||||||
getPopupContainer={popupContainer}
|
|
||||||
placeholder="Enter the panel name here..."
|
|
||||||
open={autoCompleteOpen}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
rootClassName="general-settings__name-input"
|
|
||||||
ref={inputRef}
|
|
||||||
onSelect={handleInputCursor}
|
|
||||||
onClick={handleInputCursor}
|
|
||||||
onBlur={(): void => setAutoCompleteOpen(false)}
|
|
||||||
/>
|
|
||||||
</AutoComplete>
|
|
||||||
<Typography.Text className="section-heading">Description</Typography.Text>
|
|
||||||
<TextArea
|
|
||||||
placeholder="Enter the panel description here..."
|
|
||||||
bordered
|
|
||||||
allowClear
|
|
||||||
value={description}
|
|
||||||
onChange={(event): void =>
|
|
||||||
onChangeHandler(setDescription, event.target.value)
|
|
||||||
}
|
|
||||||
rootClassName="general-settings__description-input"
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
</SettingsSection>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
.histogram-settings__bucket-config {
|
|
||||||
.histogram-settings__bucket-size-label {
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.histogram-settings__bucket-input {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
height: 32px;
|
|
||||||
padding: 6px 6px 6px 8px;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
align-self: stretch;
|
|
||||||
border-radius: 2px;
|
|
||||||
border: 1px solid var(--bg-slate-400);
|
|
||||||
background: var(--bg-ink-300);
|
|
||||||
|
|
||||||
.ant-input {
|
|
||||||
background: var(--bg-ink-300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.histogram-settings__combine-hist {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-top: 8px;
|
|
||||||
|
|
||||||
.histogram-settings__merge-label {
|
|
||||||
color: var(--bg-vanilla-400);
|
|
||||||
font-size: 13px;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 18px; /* 138.462% */
|
|
||||||
letter-spacing: 0.52px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.lightMode {
|
|
||||||
.histogram-settings__bucket-config {
|
|
||||||
.histogram-settings__merge-label {
|
|
||||||
color: var(--bg-ink-400);
|
|
||||||
}
|
|
||||||
|
|
||||||
.histogram-settings__bucket-input {
|
|
||||||
border: 1px solid var(--bg-vanilla-300);
|
|
||||||
background: var(--bg-vanilla-300);
|
|
||||||
|
|
||||||
.ant-input {
|
|
||||||
background: var(--bg-vanilla-300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
import { Dispatch, SetStateAction } from 'react';
|
|
||||||
import { InputNumber, Switch, Typography } from 'antd';
|
|
||||||
|
|
||||||
import SettingsSection from '../../components/SettingsSection/SettingsSection';
|
|
||||||
|
|
||||||
import './HistogramBucketsSection.styles.scss';
|
|
||||||
|
|
||||||
interface HistogramBucketsSectionProps {
|
|
||||||
bucketCount: number;
|
|
||||||
setBucketCount: Dispatch<SetStateAction<number>>;
|
|
||||||
bucketWidth: number;
|
|
||||||
setBucketWidth: Dispatch<SetStateAction<number>>;
|
|
||||||
combineHistogram: boolean;
|
|
||||||
setCombineHistogram: Dispatch<SetStateAction<boolean>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function HistogramBucketsSection({
|
|
||||||
bucketCount,
|
|
||||||
setBucketCount,
|
|
||||||
bucketWidth,
|
|
||||||
setBucketWidth,
|
|
||||||
combineHistogram,
|
|
||||||
setCombineHistogram,
|
|
||||||
}: HistogramBucketsSectionProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<SettingsSection title="Histogram / Buckets">
|
|
||||||
<section className="histogram-settings__bucket-config control-container">
|
|
||||||
<Typography.Text className="section-heading">
|
|
||||||
Number of buckets
|
|
||||||
</Typography.Text>
|
|
||||||
<InputNumber
|
|
||||||
value={bucketCount || null}
|
|
||||||
type="number"
|
|
||||||
min={0}
|
|
||||||
rootClassName="bucket-input"
|
|
||||||
placeholder="Default: 30"
|
|
||||||
onChange={(val): void => {
|
|
||||||
setBucketCount(val || 0);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Typography.Text className="section-heading histogram-settings__bucket-size-label">
|
|
||||||
Bucket width
|
|
||||||
</Typography.Text>
|
|
||||||
<InputNumber
|
|
||||||
value={bucketWidth || null}
|
|
||||||
type="number"
|
|
||||||
precision={2}
|
|
||||||
placeholder="Default: Auto"
|
|
||||||
step={0.1}
|
|
||||||
min={0.0}
|
|
||||||
rootClassName="histogram-settings__bucket-input"
|
|
||||||
onChange={(val): void => {
|
|
||||||
setBucketWidth(val || 0);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<section className="histogram-settings__combine-hist">
|
|
||||||
<Typography.Text className="section-heading">
|
|
||||||
<span className="histogram-settings__merge-label">
|
|
||||||
Merge all series into one
|
|
||||||
</span>
|
|
||||||
</Typography.Text>
|
|
||||||
<Switch
|
|
||||||
checked={combineHistogram}
|
|
||||||
size="small"
|
|
||||||
onChange={(checked): void => setCombineHistogram(checked)}
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
</SettingsSection>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import { Dispatch, SetStateAction } from 'react';
|
|
||||||
import type { UseQueryResult } from 'react-query';
|
|
||||||
import { Select, Typography } from 'antd';
|
|
||||||
import { Layers } from 'lucide-react';
|
|
||||||
import { SuccessResponse } from 'types/api';
|
|
||||||
import { LegendPosition } from 'types/api/dashboard/getAll';
|
|
||||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
|
||||||
|
|
||||||
import SettingsSection from '../../components/SettingsSection/SettingsSection';
|
|
||||||
import LegendColors from '../../LegendColors/LegendColors';
|
|
||||||
|
|
||||||
const { Option } = Select;
|
|
||||||
|
|
||||||
interface LegendSectionProps {
|
|
||||||
allowLegendPosition: boolean;
|
|
||||||
allowLegendColors: boolean;
|
|
||||||
legendPosition: LegendPosition;
|
|
||||||
setLegendPosition: Dispatch<SetStateAction<LegendPosition>>;
|
|
||||||
customLegendColors: Record<string, string>;
|
|
||||||
setCustomLegendColors: Dispatch<SetStateAction<Record<string, string>>>;
|
|
||||||
queryResponse?: UseQueryResult<
|
|
||||||
SuccessResponse<MetricRangePayloadProps, unknown>,
|
|
||||||
Error
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function LegendSection({
|
|
||||||
allowLegendPosition,
|
|
||||||
allowLegendColors,
|
|
||||||
legendPosition,
|
|
||||||
setLegendPosition,
|
|
||||||
customLegendColors,
|
|
||||||
setCustomLegendColors,
|
|
||||||
queryResponse,
|
|
||||||
}: LegendSectionProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<SettingsSection title="Legend" icon={<Layers size={14} />}>
|
|
||||||
{allowLegendPosition && (
|
|
||||||
<section className="legend-position control-container">
|
|
||||||
<Typography.Text className="section-heading">Position</Typography.Text>
|
|
||||||
<Select
|
|
||||||
onChange={(value: LegendPosition): void => setLegendPosition(value)}
|
|
||||||
value={legendPosition}
|
|
||||||
className="panel-type-select"
|
|
||||||
defaultValue={LegendPosition.BOTTOM}
|
|
||||||
>
|
|
||||||
<Option value={LegendPosition.BOTTOM}>
|
|
||||||
<div className="select-option">
|
|
||||||
<Typography.Text className="display">Bottom</Typography.Text>
|
|
||||||
</div>
|
|
||||||
</Option>
|
|
||||||
<Option value={LegendPosition.RIGHT}>
|
|
||||||
<div className="select-option">
|
|
||||||
<Typography.Text className="display">Right</Typography.Text>
|
|
||||||
</div>
|
|
||||||
</Option>
|
|
||||||
</Select>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{allowLegendColors && (
|
|
||||||
<section className="legend-colors">
|
|
||||||
<LegendColors
|
|
||||||
customLegendColors={customLegendColors}
|
|
||||||
setCustomLegendColors={setCustomLegendColors}
|
|
||||||
queryResponse={queryResponse}
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
</SettingsSection>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
.thresholds-section {
|
|
||||||
padding: 12px 12px 16px 12px;
|
|
||||||
border-top: 1px solid var(--bg-slate-500);
|
|
||||||
}
|
|
||||||
|
|
||||||
.lightMode {
|
|
||||||
.thresholds-section {
|
|
||||||
border-top: 1px solid var(--bg-vanilla-300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import { Dispatch, SetStateAction } from 'react';
|
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
|
||||||
import { Antenna } from 'lucide-react';
|
|
||||||
import { ColumnUnit } from 'types/api/dashboard/getAll';
|
|
||||||
|
|
||||||
import SettingsSection from '../../components/SettingsSection/SettingsSection';
|
|
||||||
import ThresholdSelector from '../../Threshold/ThresholdSelector';
|
|
||||||
import { ThresholdProps } from '../../Threshold/types';
|
|
||||||
|
|
||||||
import './ThresholdsSection.styles.scss';
|
|
||||||
|
|
||||||
interface ThresholdsSectionProps {
|
|
||||||
thresholds: ThresholdProps[];
|
|
||||||
setThresholds: Dispatch<SetStateAction<ThresholdProps[]>>;
|
|
||||||
yAxisUnit: string;
|
|
||||||
selectedGraph: PANEL_TYPES;
|
|
||||||
columnUnits: ColumnUnit;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ThresholdsSection({
|
|
||||||
thresholds,
|
|
||||||
setThresholds,
|
|
||||||
yAxisUnit,
|
|
||||||
selectedGraph,
|
|
||||||
columnUnits,
|
|
||||||
}: ThresholdsSectionProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<SettingsSection
|
|
||||||
title="Thresholds"
|
|
||||||
icon={<Antenna size={14} />}
|
|
||||||
defaultOpen={!!thresholds.length}
|
|
||||||
>
|
|
||||||
<ThresholdSelector
|
|
||||||
thresholds={thresholds}
|
|
||||||
setThresholds={setThresholds}
|
|
||||||
yAxisUnit={yAxisUnit}
|
|
||||||
selectedGraph={selectedGraph}
|
|
||||||
columnUnits={columnUnits}
|
|
||||||
/>
|
|
||||||
</SettingsSection>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
|
|
||||||
import { Select, Switch, Typography } from 'antd';
|
|
||||||
import TimePreference from 'components/TimePreferenceDropDown';
|
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
|
||||||
import {
|
|
||||||
ItemsProps,
|
|
||||||
PanelTypesWithData,
|
|
||||||
} from 'container/DashboardContainer/PanelTypeSelectionModal/menuItems';
|
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
|
||||||
import { LayoutDashboard } from 'lucide-react';
|
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
|
||||||
|
|
||||||
import SettingsSection from '../../components/SettingsSection/SettingsSection';
|
|
||||||
import { timePreferance } from '../../timeItems';
|
|
||||||
|
|
||||||
const { Option } = Select;
|
|
||||||
|
|
||||||
interface VisualizationSettingsSectionProps {
|
|
||||||
selectedGraph: PANEL_TYPES;
|
|
||||||
setGraphHandler: (type: PANEL_TYPES) => void;
|
|
||||||
selectedTime: timePreferance;
|
|
||||||
setSelectedTime: Dispatch<SetStateAction<timePreferance>>;
|
|
||||||
stackedBarChart: boolean;
|
|
||||||
setStackedBarChart: Dispatch<SetStateAction<boolean>>;
|
|
||||||
isFillSpans: boolean;
|
|
||||||
setIsFillSpans: Dispatch<SetStateAction<boolean>>;
|
|
||||||
allowPanelTimePreference: boolean;
|
|
||||||
allowStackingBarChart: boolean;
|
|
||||||
allowFillSpans: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function VisualizationSettingsSection({
|
|
||||||
selectedGraph,
|
|
||||||
setGraphHandler,
|
|
||||||
selectedTime,
|
|
||||||
setSelectedTime,
|
|
||||||
stackedBarChart,
|
|
||||||
setStackedBarChart,
|
|
||||||
isFillSpans,
|
|
||||||
setIsFillSpans,
|
|
||||||
allowPanelTimePreference,
|
|
||||||
allowStackingBarChart,
|
|
||||||
allowFillSpans,
|
|
||||||
}: VisualizationSettingsSectionProps): JSX.Element {
|
|
||||||
const { currentQuery } = useQueryBuilder();
|
|
||||||
const [graphTypes, setGraphTypes] = useState<ItemsProps[]>(PanelTypesWithData);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const queryContainsMetricsDataSource = currentQuery.builder.queryData.some(
|
|
||||||
(query) => query.dataSource === DataSource.METRICS,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (queryContainsMetricsDataSource) {
|
|
||||||
setGraphTypes((prev) =>
|
|
||||||
prev.filter((graph) => graph.name !== PANEL_TYPES.LIST),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
setGraphTypes(PanelTypesWithData);
|
|
||||||
}
|
|
||||||
}, [currentQuery]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SettingsSection
|
|
||||||
title="Visualization"
|
|
||||||
defaultOpen
|
|
||||||
icon={<LayoutDashboard size={14} />}
|
|
||||||
>
|
|
||||||
<section className="panel-type control-container">
|
|
||||||
<Typography.Text className="section-heading">Panel Type</Typography.Text>
|
|
||||||
<Select
|
|
||||||
onChange={setGraphHandler}
|
|
||||||
value={selectedGraph}
|
|
||||||
className="panel-type-select"
|
|
||||||
data-testid="panel-change-select"
|
|
||||||
data-stacking-state={stackedBarChart ? 'true' : 'false'}
|
|
||||||
>
|
|
||||||
{graphTypes.map((item) => (
|
|
||||||
<Option key={item.name} value={item.name}>
|
|
||||||
<div className="select-option">
|
|
||||||
<div className="icon">{item.icon}</div>
|
|
||||||
<Typography.Text className="display">{item.display}</Typography.Text>
|
|
||||||
</div>
|
|
||||||
</Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{allowPanelTimePreference && (
|
|
||||||
<section className="panel-time-preference control-container">
|
|
||||||
<Typography.Text className="section-heading">
|
|
||||||
Panel Time Preference
|
|
||||||
</Typography.Text>
|
|
||||||
<TimePreference
|
|
||||||
{...{
|
|
||||||
selectedTime,
|
|
||||||
setSelectedTime,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{allowStackingBarChart && (
|
|
||||||
<section className="stack-chart control-container">
|
|
||||||
<Typography.Text className="section-heading">Stack series</Typography.Text>
|
|
||||||
<Switch
|
|
||||||
checked={stackedBarChart}
|
|
||||||
size="small"
|
|
||||||
onChange={(checked): void => setStackedBarChart(checked)}
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{allowFillSpans && (
|
|
||||||
<section className="fill-gaps toggle-card">
|
|
||||||
<div className="toggle-card-text-container">
|
|
||||||
<Typography className="section-heading">Fill gaps</Typography>
|
|
||||||
<Typography.Text className="toggle-card-description">
|
|
||||||
Fill gaps in data with 0 for continuity
|
|
||||||
</Typography.Text>
|
|
||||||
</div>
|
|
||||||
<Switch
|
|
||||||
checked={isFillSpans}
|
|
||||||
size="small"
|
|
||||||
onChange={(checked): void => setIsFillSpans(checked)}
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
</SettingsSection>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -6,11 +6,6 @@ import { MemoryRouter } from 'react-router-dom';
|
|||||||
import { render as rtlRender, screen } from '@testing-library/react';
|
import { render as rtlRender, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import {
|
|
||||||
FillMode,
|
|
||||||
LineInterpolation,
|
|
||||||
LineStyle,
|
|
||||||
} from 'lib/uPlotV2/config/types';
|
|
||||||
import { AppContext } from 'providers/App/App';
|
import { AppContext } from 'providers/App/App';
|
||||||
import { IAppContext } from 'providers/App/types';
|
import { IAppContext } from 'providers/App/types';
|
||||||
import { ErrorModalProvider } from 'providers/ErrorModalProvider';
|
import { ErrorModalProvider } from 'providers/ErrorModalProvider';
|
||||||
@@ -170,14 +165,6 @@ describe('RightContainer - Alerts Section', () => {
|
|||||||
setContextLinks: jest.fn(),
|
setContextLinks: jest.fn(),
|
||||||
enableDrillDown: false,
|
enableDrillDown: false,
|
||||||
isNewDashboard: false,
|
isNewDashboard: false,
|
||||||
lineInterpolation: LineInterpolation.Spline,
|
|
||||||
fillMode: FillMode.None,
|
|
||||||
lineStyle: LineStyle.Solid,
|
|
||||||
setLineInterpolation: jest.fn(),
|
|
||||||
setFillMode: jest.fn(),
|
|
||||||
setLineStyle: jest.fn(),
|
|
||||||
showPoints: false,
|
|
||||||
setShowPoints: jest.fn(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -189,7 +176,7 @@ describe('RightContainer - Alerts Section', () => {
|
|||||||
|
|
||||||
const alertsSection = screen.getByText('Alerts').closest('section');
|
const alertsSection = screen.getByText('Alerts').closest('section');
|
||||||
expect(alertsSection).toBeInTheDocument();
|
expect(alertsSection).toBeInTheDocument();
|
||||||
expect(alertsSection).toHaveClass('alerts-section');
|
expect(alertsSection).toHaveClass('alerts');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders alerts section with correct text and SquareArrowOutUpRight icon', () => {
|
it('renders alerts section with correct text and SquareArrowOutUpRight icon', () => {
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
.fill-mode-selector {
|
|
||||||
.fill-mode-icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fill-mode-label {
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--bg-vanilla-400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.lightMode {
|
|
||||||
.fill-mode-selector {
|
|
||||||
.fill-mode-label {
|
|
||||||
color: var(--bg-ink-400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
import { ToggleGroup, ToggleGroupItem } from '@signozhq/toggle-group';
|
|
||||||
import { Typography } from 'antd';
|
|
||||||
import { FillMode } from 'lib/uPlotV2/config/types';
|
|
||||||
|
|
||||||
import './FillModeSelector.styles.scss';
|
|
||||||
|
|
||||||
interface FillModeSelectorProps {
|
|
||||||
value: FillMode;
|
|
||||||
onChange: (value: FillMode) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function FillModeSelector({
|
|
||||||
value,
|
|
||||||
onChange,
|
|
||||||
}: FillModeSelectorProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<section className="fill-mode-selector control-container">
|
|
||||||
<Typography.Text className="section-heading">Fill mode</Typography.Text>
|
|
||||||
<ToggleGroup
|
|
||||||
type="single"
|
|
||||||
value={value}
|
|
||||||
variant="outline"
|
|
||||||
size="lg"
|
|
||||||
onValueChange={(newValue): void => {
|
|
||||||
if (newValue) {
|
|
||||||
onChange(newValue as FillMode);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ToggleGroupItem value={FillMode.None} aria-label="None" title="None">
|
|
||||||
<svg
|
|
||||||
className="fill-mode-icon"
|
|
||||||
viewBox="0 0 48 48"
|
|
||||||
fill="none"
|
|
||||||
stroke="#888"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
>
|
|
||||||
<rect x="8" y="16" width="32" height="16" stroke="#888" fill="none" />
|
|
||||||
</svg>
|
|
||||||
<Typography.Text className="section-heading-small">None</Typography.Text>
|
|
||||||
</ToggleGroupItem>
|
|
||||||
<ToggleGroupItem value={FillMode.Solid} aria-label="Solid" title="Solid">
|
|
||||||
<svg
|
|
||||||
className="fill-mode-icon"
|
|
||||||
viewBox="0 0 48 48"
|
|
||||||
fill="none"
|
|
||||||
stroke="#888"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
>
|
|
||||||
<rect x="8" y="16" width="32" height="16" fill="#888" />
|
|
||||||
</svg>
|
|
||||||
<Typography.Text className="section-heading-small">Solid</Typography.Text>
|
|
||||||
</ToggleGroupItem>
|
|
||||||
<ToggleGroupItem
|
|
||||||
value={FillMode.Gradient}
|
|
||||||
aria-label="Gradient"
|
|
||||||
title="Gradient"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="fill-mode-icon"
|
|
||||||
viewBox="0 0 48 48"
|
|
||||||
fill="none"
|
|
||||||
stroke="#888"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="fill-gradient" x1="0" y1="0" x2="1" y2="0">
|
|
||||||
<stop offset="0%" stopColor="#888" stopOpacity="0.2" />
|
|
||||||
<stop offset="100%" stopColor="#888" stopOpacity="0.8" />
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<rect
|
|
||||||
x="8"
|
|
||||||
y="16"
|
|
||||||
width="32"
|
|
||||||
height="16"
|
|
||||||
fill="url(#fill-gradient)"
|
|
||||||
stroke="#888"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<Typography.Text className="section-heading-small">
|
|
||||||
Gradient
|
|
||||||
</Typography.Text>
|
|
||||||
</ToggleGroupItem>
|
|
||||||
</ToggleGroup>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
.line-interpolation-selector {
|
|
||||||
.line-interpolation-icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line-interpolation-label {
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--bg-vanilla-400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.lightMode {
|
|
||||||
.line-interpolation-selector {
|
|
||||||
.line-interpolation-label {
|
|
||||||
color: var(--bg-ink-400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
import { ToggleGroup, ToggleGroupItem } from '@signozhq/toggle-group';
|
|
||||||
import { Typography } from 'antd';
|
|
||||||
import { LineInterpolation } from 'lib/uPlotV2/config/types';
|
|
||||||
|
|
||||||
import './LineInterpolationSelector.styles.scss';
|
|
||||||
|
|
||||||
interface LineInterpolationSelectorProps {
|
|
||||||
value: LineInterpolation;
|
|
||||||
onChange: (value: LineInterpolation) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function LineInterpolationSelector({
|
|
||||||
value,
|
|
||||||
onChange,
|
|
||||||
}: LineInterpolationSelectorProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<section className="line-interpolation-selector control-container">
|
|
||||||
<Typography.Text className="section-heading">
|
|
||||||
Line interpolation
|
|
||||||
</Typography.Text>
|
|
||||||
<ToggleGroup
|
|
||||||
type="single"
|
|
||||||
value={value}
|
|
||||||
variant="outline"
|
|
||||||
size="lg"
|
|
||||||
onValueChange={(newValue): void => {
|
|
||||||
if (newValue) {
|
|
||||||
onChange(newValue as LineInterpolation);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ToggleGroupItem
|
|
||||||
value={LineInterpolation.Linear}
|
|
||||||
aria-label="Linear"
|
|
||||||
title="Linear"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="line-interpolation-icon"
|
|
||||||
viewBox="0 0 48 48"
|
|
||||||
fill="none"
|
|
||||||
stroke="#888"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
>
|
|
||||||
<circle cx="8" cy="32" r="3" fill="#888" />
|
|
||||||
<circle cx="24" cy="16" r="3" fill="#888" />
|
|
||||||
<circle cx="40" cy="32" r="3" fill="#888" />
|
|
||||||
<path d="M8 32 L24 16 L40 32" stroke="#888" />
|
|
||||||
</svg>
|
|
||||||
</ToggleGroupItem>
|
|
||||||
<ToggleGroupItem value={LineInterpolation.Spline} aria-label="Spline">
|
|
||||||
<svg
|
|
||||||
className="line-interpolation-icon"
|
|
||||||
viewBox="0 0 48 48"
|
|
||||||
fill="none"
|
|
||||||
stroke="#888"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
>
|
|
||||||
<circle cx="8" cy="32" r="3" fill="#888" />
|
|
||||||
<circle cx="24" cy="16" r="3" fill="#888" />
|
|
||||||
<circle cx="40" cy="32" r="3" fill="#888" />
|
|
||||||
<path d="M8 32 C16 8, 32 8, 40 32" />
|
|
||||||
</svg>
|
|
||||||
</ToggleGroupItem>
|
|
||||||
<ToggleGroupItem
|
|
||||||
value={LineInterpolation.StepAfter}
|
|
||||||
aria-label="Step After"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="line-interpolation-icon"
|
|
||||||
viewBox="0 0 48 48"
|
|
||||||
fill="none"
|
|
||||||
stroke="#888"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
>
|
|
||||||
<circle cx="8" cy="32" r="3" fill="#888" />
|
|
||||||
<circle cx="24" cy="16" r="3" fill="#888" />
|
|
||||||
<circle cx="40" cy="32" r="3" fill="#888" />
|
|
||||||
<path d="M8 32 V16 H24 V32 H40" />
|
|
||||||
</svg>
|
|
||||||
</ToggleGroupItem>
|
|
||||||
|
|
||||||
<ToggleGroupItem
|
|
||||||
value={LineInterpolation.StepBefore}
|
|
||||||
aria-label="Step Before"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="line-interpolation-icon"
|
|
||||||
viewBox="0 0 48 48"
|
|
||||||
fill="none"
|
|
||||||
stroke="#888"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
>
|
|
||||||
<circle cx="8" cy="32" r="3" fill="#888" />
|
|
||||||
<circle cx="24" cy="16" r="3" fill="#888" />
|
|
||||||
<circle cx="40" cy="32" r="3" fill="#888" />
|
|
||||||
<path d="M8 32 H24 V16 H40 V32" />
|
|
||||||
</svg>
|
|
||||||
</ToggleGroupItem>
|
|
||||||
</ToggleGroup>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
.line-style-selector {
|
|
||||||
.line-style-icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line-style-label {
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--bg-vanilla-400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.lightMode {
|
|
||||||
.line-style-selector {
|
|
||||||
.line-style-label {
|
|
||||||
color: var(--bg-ink-400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
import { ToggleGroup, ToggleGroupItem } from '@signozhq/toggle-group';
|
|
||||||
import { Typography } from 'antd';
|
|
||||||
import { LineStyle } from 'lib/uPlotV2/config/types';
|
|
||||||
|
|
||||||
import './LineStyleSelector.styles.scss';
|
|
||||||
|
|
||||||
interface LineStyleSelectorProps {
|
|
||||||
value: LineStyle;
|
|
||||||
onChange: (value: LineStyle) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function LineStyleSelector({
|
|
||||||
value,
|
|
||||||
onChange,
|
|
||||||
}: LineStyleSelectorProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<section className="line-style-selector control-container">
|
|
||||||
<Typography.Text className="section-heading">Line style</Typography.Text>
|
|
||||||
<ToggleGroup
|
|
||||||
type="single"
|
|
||||||
value={value}
|
|
||||||
variant="outline"
|
|
||||||
size="lg"
|
|
||||||
onValueChange={(newValue): void => {
|
|
||||||
if (newValue) {
|
|
||||||
onChange(newValue as LineStyle);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ToggleGroupItem value={LineStyle.Solid} aria-label="Solid" title="Solid">
|
|
||||||
<svg
|
|
||||||
className="line-style-icon"
|
|
||||||
viewBox="0 0 48 48"
|
|
||||||
fill="none"
|
|
||||||
stroke="#888"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
>
|
|
||||||
<path d="M8 24 L40 24" />
|
|
||||||
</svg>
|
|
||||||
<Typography.Text className="section-heading-small">Solid</Typography.Text>
|
|
||||||
</ToggleGroupItem>
|
|
||||||
<ToggleGroupItem
|
|
||||||
value={LineStyle.Dashed}
|
|
||||||
aria-label="Dashed"
|
|
||||||
title="Dashed"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="line-style-icon"
|
|
||||||
viewBox="0 0 48 48"
|
|
||||||
fill="none"
|
|
||||||
stroke="#888"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeDasharray="6 4"
|
|
||||||
>
|
|
||||||
<path d="M8 24 L40 24" />
|
|
||||||
</svg>
|
|
||||||
<Typography.Text className="section-heading-small">Dashed</Typography.Text>
|
|
||||||
</ToggleGroupItem>
|
|
||||||
</ToggleGroup>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
max-height: 0;
|
max-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: max-height 0.1s ease, opacity 0.1s ease, padding 0.1s ease;
|
transition: max-height 0.25s ease, opacity 0.25s ease, padding 0.25s ease;
|
||||||
|
|
||||||
&.open {
|
&.open {
|
||||||
padding-bottom: 24px;
|
padding-bottom: 24px;
|
||||||
|
|||||||
@@ -206,59 +206,3 @@ export const panelTypeVsDecimalPrecision: {
|
|||||||
[PANEL_TYPES.TRACE]: false,
|
[PANEL_TYPES.TRACE]: false,
|
||||||
[PANEL_TYPES.EMPTY_WIDGET]: false,
|
[PANEL_TYPES.EMPTY_WIDGET]: false,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const panelTypeVsLineInterpolation: {
|
|
||||||
[key in PANEL_TYPES]: boolean;
|
|
||||||
} = {
|
|
||||||
[PANEL_TYPES.TIME_SERIES]: true,
|
|
||||||
[PANEL_TYPES.VALUE]: false,
|
|
||||||
[PANEL_TYPES.TABLE]: false,
|
|
||||||
[PANEL_TYPES.LIST]: false,
|
|
||||||
[PANEL_TYPES.PIE]: false,
|
|
||||||
[PANEL_TYPES.BAR]: false,
|
|
||||||
[PANEL_TYPES.HISTOGRAM]: false,
|
|
||||||
[PANEL_TYPES.TRACE]: false,
|
|
||||||
[PANEL_TYPES.EMPTY_WIDGET]: false,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const panelTypeVsLineStyle: {
|
|
||||||
[key in PANEL_TYPES]: boolean;
|
|
||||||
} = {
|
|
||||||
[PANEL_TYPES.TIME_SERIES]: true,
|
|
||||||
[PANEL_TYPES.VALUE]: false,
|
|
||||||
[PANEL_TYPES.TABLE]: false,
|
|
||||||
[PANEL_TYPES.LIST]: false,
|
|
||||||
[PANEL_TYPES.PIE]: false,
|
|
||||||
[PANEL_TYPES.BAR]: false,
|
|
||||||
[PANEL_TYPES.HISTOGRAM]: false,
|
|
||||||
[PANEL_TYPES.TRACE]: false,
|
|
||||||
[PANEL_TYPES.EMPTY_WIDGET]: false,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const panelTypeVsFillMode: {
|
|
||||||
[key in PANEL_TYPES]: boolean;
|
|
||||||
} = {
|
|
||||||
[PANEL_TYPES.TIME_SERIES]: true,
|
|
||||||
[PANEL_TYPES.VALUE]: false,
|
|
||||||
[PANEL_TYPES.TABLE]: false,
|
|
||||||
[PANEL_TYPES.LIST]: false,
|
|
||||||
[PANEL_TYPES.PIE]: false,
|
|
||||||
[PANEL_TYPES.BAR]: false,
|
|
||||||
[PANEL_TYPES.HISTOGRAM]: false,
|
|
||||||
[PANEL_TYPES.TRACE]: false,
|
|
||||||
[PANEL_TYPES.EMPTY_WIDGET]: false,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const panelTypeVsShowPoints: {
|
|
||||||
[key in PANEL_TYPES]: boolean;
|
|
||||||
} = {
|
|
||||||
[PANEL_TYPES.TIME_SERIES]: true,
|
|
||||||
[PANEL_TYPES.VALUE]: false,
|
|
||||||
[PANEL_TYPES.TABLE]: false,
|
|
||||||
[PANEL_TYPES.LIST]: false,
|
|
||||||
[PANEL_TYPES.PIE]: false,
|
|
||||||
[PANEL_TYPES.BAR]: false,
|
|
||||||
[PANEL_TYPES.HISTOGRAM]: false,
|
|
||||||
[PANEL_TYPES.TRACE]: false,
|
|
||||||
[PANEL_TYPES.EMPTY_WIDGET]: false,
|
|
||||||
} as const;
|
|
||||||
|
|||||||
@@ -1,16 +1,45 @@
|
|||||||
import { Dispatch, SetStateAction, useMemo } from 'react';
|
import {
|
||||||
|
Dispatch,
|
||||||
|
SetStateAction,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
import { UseQueryResult } from 'react-query';
|
import { UseQueryResult } from 'react-query';
|
||||||
import { Typography } from 'antd';
|
import type { InputRef } from 'antd';
|
||||||
|
import {
|
||||||
|
AutoComplete,
|
||||||
|
Input,
|
||||||
|
InputNumber,
|
||||||
|
Select,
|
||||||
|
Switch,
|
||||||
|
Typography,
|
||||||
|
} from 'antd';
|
||||||
import { PrecisionOption, PrecisionOptionsEnum } from 'components/Graph/types';
|
import { PrecisionOption, PrecisionOptionsEnum } from 'components/Graph/types';
|
||||||
|
import TimePreference from 'components/TimePreferenceDropDown';
|
||||||
import { PANEL_TYPES, PanelDisplay } from 'constants/queryBuilder';
|
import { PANEL_TYPES, PanelDisplay } from 'constants/queryBuilder';
|
||||||
import { PanelTypesWithData } from 'container/DashboardContainer/PanelTypeSelectionModal/menuItems';
|
import GraphTypes, {
|
||||||
|
ItemsProps,
|
||||||
|
} from 'container/DashboardContainer/ComponentsSlider/menuItems';
|
||||||
import { useDashboardVariables } from 'hooks/dashboard/useDashboardVariables';
|
import { useDashboardVariables } from 'hooks/dashboard/useDashboardVariables';
|
||||||
import useCreateAlerts from 'hooks/queryBuilder/useCreateAlerts';
|
import useCreateAlerts from 'hooks/queryBuilder/useCreateAlerts';
|
||||||
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import {
|
import {
|
||||||
FillMode,
|
Antenna,
|
||||||
LineInterpolation,
|
Axis3D,
|
||||||
LineStyle,
|
ConciergeBell,
|
||||||
} from 'lib/uPlotV2/config/types';
|
Layers,
|
||||||
|
LayoutDashboard,
|
||||||
|
LineChart,
|
||||||
|
Link,
|
||||||
|
Pencil,
|
||||||
|
Plus,
|
||||||
|
SlidersHorizontal,
|
||||||
|
Spline,
|
||||||
|
SquareArrowOutUpRight,
|
||||||
|
} from 'lucide-react';
|
||||||
import { SuccessResponse } from 'types/api';
|
import { SuccessResponse } from 'types/api';
|
||||||
import {
|
import {
|
||||||
ColumnUnit,
|
ColumnUnit,
|
||||||
@@ -19,55 +48,56 @@ import {
|
|||||||
Widgets,
|
Widgets,
|
||||||
} from 'types/api/dashboard/getAll';
|
} from 'types/api/dashboard/getAll';
|
||||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||||
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
import { popupContainer } from 'utils/selectPopupContainer';
|
||||||
|
|
||||||
|
import { ColumnUnitSelector } from './ColumnUnitSelector/ColumnUnitSelector';
|
||||||
|
import SettingsSection from './components/SettingsSection/SettingsSection';
|
||||||
import {
|
import {
|
||||||
panelTypeVsBucketConfig,
|
panelTypeVsBucketConfig,
|
||||||
panelTypeVsColumnUnitPreferences,
|
panelTypeVsColumnUnitPreferences,
|
||||||
panelTypeVsContextLinks,
|
panelTypeVsContextLinks,
|
||||||
panelTypeVsCreateAlert,
|
panelTypeVsCreateAlert,
|
||||||
panelTypeVsDecimalPrecision,
|
panelTypeVsDecimalPrecision,
|
||||||
panelTypeVsFillMode,
|
|
||||||
panelTypeVsFillSpan,
|
panelTypeVsFillSpan,
|
||||||
panelTypeVsLegendColors,
|
panelTypeVsLegendColors,
|
||||||
panelTypeVsLegendPosition,
|
panelTypeVsLegendPosition,
|
||||||
panelTypeVsLineInterpolation,
|
|
||||||
panelTypeVsLineStyle,
|
|
||||||
panelTypeVsLogScale,
|
panelTypeVsLogScale,
|
||||||
panelTypeVsPanelTimePreferences,
|
panelTypeVsPanelTimePreferences,
|
||||||
panelTypeVsShowPoints,
|
|
||||||
panelTypeVsSoftMinMax,
|
panelTypeVsSoftMinMax,
|
||||||
panelTypeVsStackingChartPreferences,
|
panelTypeVsStackingChartPreferences,
|
||||||
panelTypeVsThreshold,
|
panelTypeVsThreshold,
|
||||||
panelTypeVsYAxisUnit,
|
panelTypeVsYAxisUnit,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import AlertsSection from './SettingSections/AlertsSection/AlertsSection';
|
import ContextLinks from './ContextLinks';
|
||||||
import AxesSection from './SettingSections/AxesSection/AxesSection';
|
import DashboardYAxisUnitSelectorWrapper from './DashboardYAxisUnitSelectorWrapper';
|
||||||
import ChartAppearanceSection from './SettingSections/ChartAppearanceSection/ChartAppearanceSection';
|
import LegendColors from './LegendColors/LegendColors';
|
||||||
import ContextLinksSection from './SettingSections/ContextLinksSection/ContextLinksSection';
|
import ThresholdSelector from './Threshold/ThresholdSelector';
|
||||||
import FormattingUnitsSection from './SettingSections/FormattingUnitsSection/FormattingUnitsSection';
|
|
||||||
import GeneralSettingsSection from './SettingSections/GeneralSettingsSection/GeneralSettingsSection';
|
|
||||||
import HistogramBucketsSection from './SettingSections/HistogramBucketsSection/HistogramBucketsSection';
|
|
||||||
import LegendSection from './SettingSections/LegendSection/LegendSection';
|
|
||||||
import ThresholdsSection from './SettingSections/ThresholdsSection/ThresholdsSection';
|
|
||||||
import VisualizationSettingsSection from './SettingSections/VisualizationSettingsSection/VisualizationSettingsSection';
|
|
||||||
import { ThresholdProps } from './Threshold/types';
|
import { ThresholdProps } from './Threshold/types';
|
||||||
import { timePreferance } from './timeItems';
|
import { timePreferance } from './timeItems';
|
||||||
|
|
||||||
import './RightContainer.styles.scss';
|
import './RightContainer.styles.scss';
|
||||||
|
|
||||||
|
const { TextArea } = Input;
|
||||||
|
const { Option } = Select;
|
||||||
|
|
||||||
|
enum LogScale {
|
||||||
|
LINEAR = 'linear',
|
||||||
|
LOGARITHMIC = 'logarithmic',
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VariableOption {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
function RightContainer({
|
function RightContainer({
|
||||||
description,
|
description,
|
||||||
setDescription,
|
setDescription,
|
||||||
setTitle,
|
setTitle,
|
||||||
title,
|
title,
|
||||||
selectedGraph,
|
selectedGraph,
|
||||||
lineInterpolation,
|
|
||||||
setLineInterpolation,
|
|
||||||
fillMode,
|
|
||||||
setFillMode,
|
|
||||||
lineStyle,
|
|
||||||
setLineStyle,
|
|
||||||
showPoints,
|
|
||||||
setShowPoints,
|
|
||||||
bucketCount,
|
bucketCount,
|
||||||
bucketWidth,
|
bucketWidth,
|
||||||
stackedBarChart,
|
stackedBarChart,
|
||||||
@@ -107,10 +137,20 @@ function RightContainer({
|
|||||||
isNewDashboard,
|
isNewDashboard,
|
||||||
}: RightContainerProps): JSX.Element {
|
}: RightContainerProps): JSX.Element {
|
||||||
const { dashboardVariables } = useDashboardVariables();
|
const { dashboardVariables } = useDashboardVariables();
|
||||||
|
const [inputValue, setInputValue] = useState(title);
|
||||||
|
const [autoCompleteOpen, setAutoCompleteOpen] = useState(false);
|
||||||
|
const [cursorPos, setCursorPos] = useState(0);
|
||||||
|
const inputRef = useRef<InputRef>(null);
|
||||||
|
|
||||||
const selectedPanelDisplay = PanelTypesWithData.find(
|
const onChangeHandler = useCallback(
|
||||||
(e) => e.name === selectedGraph,
|
(setFunc: Dispatch<SetStateAction<string>>, value: string) => {
|
||||||
)?.display as PanelDisplay;
|
setFunc(value);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectedGraphType =
|
||||||
|
GraphTypes.find((e) => e.name === selectedGraph)?.display || '';
|
||||||
|
|
||||||
const onCreateAlertsHandler = useCreateAlerts(selectedWidget, 'panelView');
|
const onCreateAlertsHandler = useCreateAlerts(selectedWidget, 'panelView');
|
||||||
|
|
||||||
@@ -134,20 +174,16 @@ function RightContainer({
|
|||||||
panelTypeVsContextLinks[selectedGraph] && enableDrillDown;
|
panelTypeVsContextLinks[selectedGraph] && enableDrillDown;
|
||||||
const allowDecimalPrecision = panelTypeVsDecimalPrecision[selectedGraph];
|
const allowDecimalPrecision = panelTypeVsDecimalPrecision[selectedGraph];
|
||||||
|
|
||||||
const allowLineInterpolation = panelTypeVsLineInterpolation[selectedGraph];
|
const { currentQuery } = useQueryBuilder();
|
||||||
const allowLineStyle = panelTypeVsLineStyle[selectedGraph];
|
|
||||||
const allowFillMode = panelTypeVsFillMode[selectedGraph];
|
|
||||||
const allowShowPoints = panelTypeVsShowPoints[selectedGraph];
|
|
||||||
|
|
||||||
const decimapPrecisionOptions = useMemo(
|
const [graphTypes, setGraphTypes] = useState<ItemsProps[]>(GraphTypes);
|
||||||
() => [
|
|
||||||
{ label: '0 decimals', value: PrecisionOptionsEnum.ZERO },
|
const dashboardVariableOptions = useMemo<VariableOption[]>(() => {
|
||||||
{ label: '1 decimal', value: PrecisionOptionsEnum.ONE },
|
return Object.entries(dashboardVariables).map(([, value]) => ({
|
||||||
{ label: '2 decimals', value: PrecisionOptionsEnum.TWO },
|
value: value.name || '',
|
||||||
{ label: '3 decimals', value: PrecisionOptionsEnum.THREE },
|
label: value.name || '',
|
||||||
],
|
}));
|
||||||
[],
|
}, [dashboardVariables]);
|
||||||
);
|
|
||||||
|
|
||||||
const isAxisSectionVisible = useMemo(() => allowSoftMinMax || allowLogScale, [
|
const isAxisSectionVisible = useMemo(() => allowSoftMinMax || allowLogScale, [
|
||||||
allowSoftMinMax,
|
allowSoftMinMax,
|
||||||
@@ -164,20 +200,94 @@ function RightContainer({
|
|||||||
[allowLegendPosition, allowLegendColors],
|
[allowLegendPosition, allowLegendColors],
|
||||||
);
|
);
|
||||||
|
|
||||||
const isChartAppearanceSectionVisible = useMemo(
|
const updateCursorAndDropdown = (value: string, pos: number): void => {
|
||||||
() =>
|
setCursorPos(pos);
|
||||||
/**
|
const lastDollar = value.lastIndexOf('$', pos - 1);
|
||||||
* Disabled for now as we are not done with other settings in chart appearance section
|
setAutoCompleteOpen(lastDollar !== -1 && pos >= lastDollar + 1);
|
||||||
* TODO: @ahrefabhi Enable this after we are done other settings in chart appearance section
|
};
|
||||||
*/
|
|
||||||
|
|
||||||
// eslint-disable-next-line sonarjs/no-redundant-boolean
|
const onInputChange = (value: string): void => {
|
||||||
false &&
|
setInputValue(value);
|
||||||
(allowFillMode ||
|
onChangeHandler(setTitle, value);
|
||||||
allowLineStyle ||
|
setTimeout(() => {
|
||||||
allowLineInterpolation ||
|
const pos = inputRef.current?.input?.selectionStart ?? 0;
|
||||||
allowShowPoints),
|
updateCursorAndDropdown(value, pos);
|
||||||
[allowFillMode, allowLineStyle, allowLineInterpolation, allowShowPoints],
|
}, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const decimapPrecisionOptions = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{ label: '0 decimals', value: PrecisionOptionsEnum.ZERO },
|
||||||
|
{ label: '1 decimal', value: PrecisionOptionsEnum.ONE },
|
||||||
|
{ label: '2 decimals', value: PrecisionOptionsEnum.TWO },
|
||||||
|
{ label: '3 decimals', value: PrecisionOptionsEnum.THREE },
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleInputCursor = (): void => {
|
||||||
|
const pos = inputRef.current?.input?.selectionStart ?? 0;
|
||||||
|
updateCursorAndDropdown(inputValue, pos);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSelect = (selectedValue: string): void => {
|
||||||
|
const pos = cursorPos;
|
||||||
|
const value = inputValue;
|
||||||
|
const lastDollar = value.lastIndexOf('$', pos - 1);
|
||||||
|
const textBeforeDollar = value.substring(0, lastDollar);
|
||||||
|
const textAfterDollar = value.substring(lastDollar + 1);
|
||||||
|
const match = textAfterDollar.match(/^([a-zA-Z0-9_.]*)/);
|
||||||
|
const rest = textAfterDollar.substring(match ? match[1].length : 0);
|
||||||
|
const newValue = `${textBeforeDollar}$${selectedValue}${rest}`;
|
||||||
|
setInputValue(newValue);
|
||||||
|
onChangeHandler(setTitle, newValue);
|
||||||
|
setAutoCompleteOpen(false);
|
||||||
|
setTimeout(() => {
|
||||||
|
const newCursor = `${textBeforeDollar}$${selectedValue}`.length;
|
||||||
|
inputRef.current?.input?.setSelectionRange(newCursor, newCursor);
|
||||||
|
setCursorPos(newCursor);
|
||||||
|
}, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const filterOption = (
|
||||||
|
inputValue: string,
|
||||||
|
option?: VariableOption,
|
||||||
|
): boolean => {
|
||||||
|
const pos = cursorPos;
|
||||||
|
const value = inputValue;
|
||||||
|
const lastDollar = value.lastIndexOf('$', pos - 1);
|
||||||
|
if (lastDollar === -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const afterDollar = value.substring(lastDollar + 1, pos).toLowerCase();
|
||||||
|
return option?.value.toLowerCase().startsWith(afterDollar) || false;
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const queryContainsMetricsDataSource = currentQuery.builder.queryData.some(
|
||||||
|
(query) => query.dataSource === DataSource.METRICS,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (queryContainsMetricsDataSource) {
|
||||||
|
setGraphTypes((prev) =>
|
||||||
|
prev.filter((graph) => graph.name !== PANEL_TYPES.LIST),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setGraphTypes(GraphTypes);
|
||||||
|
}
|
||||||
|
}, [currentQuery]);
|
||||||
|
|
||||||
|
const softMinHandler = useCallback(
|
||||||
|
(value: number | null) => {
|
||||||
|
setSoftMin(value);
|
||||||
|
},
|
||||||
|
[setSoftMin],
|
||||||
|
);
|
||||||
|
|
||||||
|
const softMaxHandler = useCallback(
|
||||||
|
(value: number | null) => {
|
||||||
|
setSoftMax(value);
|
||||||
|
},
|
||||||
|
[setSoftMax],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -187,120 +297,336 @@ function RightContainer({
|
|||||||
<Typography.Text className="header-text">Panel Settings</Typography.Text>
|
<Typography.Text className="header-text">Panel Settings</Typography.Text>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<GeneralSettingsSection
|
<SettingsSection title="General" defaultOpen icon={<Pencil size={14} />}>
|
||||||
title={title}
|
<section className="name-description control-container">
|
||||||
setTitle={setTitle}
|
<Typography.Text className="typography">Name</Typography.Text>
|
||||||
description={description}
|
<AutoComplete
|
||||||
setDescription={setDescription}
|
options={dashboardVariableOptions}
|
||||||
dashboardVariables={dashboardVariables}
|
value={inputValue}
|
||||||
/>
|
onChange={onInputChange}
|
||||||
|
onSelect={onSelect}
|
||||||
|
filterOption={filterOption}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
getPopupContainer={popupContainer}
|
||||||
|
placeholder="Enter the panel name here..."
|
||||||
|
open={autoCompleteOpen}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
rootClassName="name-input"
|
||||||
|
ref={inputRef}
|
||||||
|
onSelect={handleInputCursor}
|
||||||
|
onClick={handleInputCursor}
|
||||||
|
onBlur={(): void => setAutoCompleteOpen(false)}
|
||||||
|
/>
|
||||||
|
</AutoComplete>
|
||||||
|
<Typography.Text className="typography">Description</Typography.Text>
|
||||||
|
<TextArea
|
||||||
|
placeholder="Enter the panel description here..."
|
||||||
|
bordered
|
||||||
|
allowClear
|
||||||
|
value={description}
|
||||||
|
onChange={(event): void =>
|
||||||
|
onChangeHandler(setDescription, event.target.value)
|
||||||
|
}
|
||||||
|
rootClassName="description-input"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</SettingsSection>
|
||||||
|
|
||||||
<section className="panel-config">
|
<section className="panel-config">
|
||||||
<VisualizationSettingsSection
|
<SettingsSection
|
||||||
selectedGraph={selectedGraph}
|
title="Visualization"
|
||||||
setGraphHandler={setGraphHandler}
|
defaultOpen
|
||||||
selectedTime={selectedTime}
|
icon={<LayoutDashboard size={14} />}
|
||||||
setSelectedTime={setSelectedTime}
|
>
|
||||||
stackedBarChart={stackedBarChart}
|
<section className="panel-type control-container">
|
||||||
setStackedBarChart={setStackedBarChart}
|
<Typography.Text className="typography">Panel Type</Typography.Text>
|
||||||
isFillSpans={isFillSpans}
|
<Select
|
||||||
setIsFillSpans={setIsFillSpans}
|
onChange={setGraphHandler}
|
||||||
allowPanelTimePreference={allowPanelTimePreference}
|
value={selectedGraph}
|
||||||
allowStackingBarChart={allowStackingBarChart}
|
className="panel-type-select"
|
||||||
allowFillSpans={allowFillSpans}
|
data-testid="panel-change-select"
|
||||||
/>
|
data-stacking-state={stackedBarChart ? 'true' : 'false'}
|
||||||
|
>
|
||||||
|
{graphTypes.map((item) => (
|
||||||
|
<Option key={item.name} value={item.name}>
|
||||||
|
<div className="select-option">
|
||||||
|
<div className="icon">{item.icon}</div>
|
||||||
|
<Typography.Text className="display">{item.display}</Typography.Text>
|
||||||
|
</div>
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{allowPanelTimePreference && (
|
||||||
|
<section className="panel-time-preference control-container">
|
||||||
|
<Typography.Text className="panel-time-text">
|
||||||
|
Panel Time Preference
|
||||||
|
</Typography.Text>
|
||||||
|
<TimePreference
|
||||||
|
{...{
|
||||||
|
selectedTime,
|
||||||
|
setSelectedTime,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{allowStackingBarChart && (
|
||||||
|
<section className="stack-chart control-container">
|
||||||
|
<Typography.Text className="label">Stack series</Typography.Text>
|
||||||
|
<Switch
|
||||||
|
checked={stackedBarChart}
|
||||||
|
size="small"
|
||||||
|
onChange={(checked): void => setStackedBarChart(checked)}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{allowFillSpans && (
|
||||||
|
<section className="fill-gaps">
|
||||||
|
<div className="fill-gaps-text-container">
|
||||||
|
<Typography className="fill-gaps-text">Fill gaps</Typography>
|
||||||
|
<Typography.Text className="fill-gaps-text-description">
|
||||||
|
Fill gaps in data with 0 for continuity
|
||||||
|
</Typography.Text>
|
||||||
|
</div>
|
||||||
|
<Switch
|
||||||
|
checked={isFillSpans}
|
||||||
|
size="small"
|
||||||
|
onChange={(checked): void => setIsFillSpans(checked)}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
</SettingsSection>
|
||||||
|
|
||||||
{isFormattingSectionVisible && (
|
{isFormattingSectionVisible && (
|
||||||
<FormattingUnitsSection
|
<SettingsSection
|
||||||
selectedPanelDisplay={selectedPanelDisplay}
|
title="Formatting & Units"
|
||||||
yAxisUnit={yAxisUnit}
|
icon={<SlidersHorizontal size={14} />}
|
||||||
setYAxisUnit={setYAxisUnit}
|
>
|
||||||
isNewDashboard={isNewDashboard}
|
{allowYAxisUnit && (
|
||||||
decimalPrecision={decimalPrecision}
|
<DashboardYAxisUnitSelectorWrapper
|
||||||
setDecimalPrecision={setDecimalPrecision}
|
onSelect={setYAxisUnit}
|
||||||
columnUnits={columnUnits}
|
value={yAxisUnit || ''}
|
||||||
setColumnUnits={setColumnUnits}
|
fieldLabel={
|
||||||
allowYAxisUnit={allowYAxisUnit}
|
selectedGraphType === PanelDisplay.VALUE ||
|
||||||
allowDecimalPrecision={allowDecimalPrecision}
|
selectedGraphType === PanelDisplay.PIE
|
||||||
allowPanelColumnPreference={allowPanelColumnPreference}
|
? 'Unit'
|
||||||
decimapPrecisionOptions={decimapPrecisionOptions}
|
: 'Y Axis Unit'
|
||||||
/>
|
}
|
||||||
)}
|
// Only update the y-axis unit value automatically in create mode
|
||||||
|
shouldUpdateYAxisUnit={isNewDashboard}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{isChartAppearanceSectionVisible && (
|
{allowDecimalPrecision && (
|
||||||
<ChartAppearanceSection
|
<section className="decimal-precision-selector control-container">
|
||||||
fillMode={fillMode}
|
<Typography.Text className="typography">
|
||||||
setFillMode={setFillMode}
|
Decimal Precision
|
||||||
lineStyle={lineStyle}
|
</Typography.Text>
|
||||||
setLineStyle={setLineStyle}
|
<Select
|
||||||
lineInterpolation={lineInterpolation}
|
options={decimapPrecisionOptions}
|
||||||
setLineInterpolation={setLineInterpolation}
|
value={decimalPrecision}
|
||||||
showPoints={showPoints}
|
style={{ width: '100%' }}
|
||||||
setShowPoints={setShowPoints}
|
className="panel-type-select"
|
||||||
allowFillMode={allowFillMode}
|
defaultValue={PrecisionOptionsEnum.TWO}
|
||||||
allowLineStyle={allowLineStyle}
|
onChange={(val: PrecisionOption): void => setDecimalPrecision(val)}
|
||||||
allowLineInterpolation={allowLineInterpolation}
|
/>
|
||||||
allowShowPoints={allowShowPoints}
|
</section>
|
||||||
/>
|
)}
|
||||||
|
|
||||||
|
{allowPanelColumnPreference && (
|
||||||
|
<ColumnUnitSelector
|
||||||
|
columnUnits={columnUnits}
|
||||||
|
setColumnUnits={setColumnUnits}
|
||||||
|
isNewDashboard={isNewDashboard}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</SettingsSection>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isAxisSectionVisible && (
|
{isAxisSectionVisible && (
|
||||||
<AxesSection
|
<SettingsSection title="Axes" icon={<Axis3D size={14} />}>
|
||||||
allowSoftMinMax={allowSoftMinMax}
|
{allowSoftMinMax && (
|
||||||
allowLogScale={allowLogScale}
|
<section className="soft-min-max">
|
||||||
softMin={softMin}
|
<section className="container">
|
||||||
softMax={softMax}
|
<Typography.Text className="text">Soft Min</Typography.Text>
|
||||||
setSoftMin={setSoftMin}
|
<InputNumber
|
||||||
setSoftMax={setSoftMax}
|
type="number"
|
||||||
isLogScale={isLogScale}
|
value={softMin}
|
||||||
setIsLogScale={setIsLogScale}
|
onChange={softMinHandler}
|
||||||
/>
|
rootClassName="input"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
<section className="container">
|
||||||
|
<Typography.Text className="text">Soft Max</Typography.Text>
|
||||||
|
<InputNumber
|
||||||
|
value={softMax}
|
||||||
|
type="number"
|
||||||
|
rootClassName="input"
|
||||||
|
onChange={softMaxHandler}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{allowLogScale && (
|
||||||
|
<section className="log-scale control-container">
|
||||||
|
<Typography.Text className="typography">Y Axis Scale</Typography.Text>
|
||||||
|
<Select
|
||||||
|
onChange={(value): void =>
|
||||||
|
setIsLogScale(value === LogScale.LOGARITHMIC)
|
||||||
|
}
|
||||||
|
value={isLogScale ? LogScale.LOGARITHMIC : LogScale.LINEAR}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
className="panel-type-select"
|
||||||
|
defaultValue={LogScale.LINEAR}
|
||||||
|
>
|
||||||
|
<Option value={LogScale.LINEAR}>
|
||||||
|
<div className="select-option">
|
||||||
|
<div className="icon">
|
||||||
|
<LineChart size={16} />
|
||||||
|
</div>
|
||||||
|
<Typography.Text className="display">Linear</Typography.Text>
|
||||||
|
</div>
|
||||||
|
</Option>
|
||||||
|
<Option value={LogScale.LOGARITHMIC}>
|
||||||
|
<div className="select-option">
|
||||||
|
<div className="icon">
|
||||||
|
<Spline size={16} />
|
||||||
|
</div>
|
||||||
|
<Typography.Text className="display">Logarithmic</Typography.Text>
|
||||||
|
</div>
|
||||||
|
</Option>
|
||||||
|
</Select>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
</SettingsSection>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isLegendSectionVisible && (
|
{isLegendSectionVisible && (
|
||||||
<LegendSection
|
<SettingsSection title="Legend" icon={<Layers size={14} />}>
|
||||||
allowLegendPosition={allowLegendPosition}
|
{allowLegendPosition && (
|
||||||
allowLegendColors={allowLegendColors}
|
<section className="legend-position control-container">
|
||||||
legendPosition={legendPosition}
|
<Typography.Text className="typography">Position</Typography.Text>
|
||||||
setLegendPosition={setLegendPosition}
|
<Select
|
||||||
customLegendColors={customLegendColors}
|
onChange={(value: LegendPosition): void => setLegendPosition(value)}
|
||||||
setCustomLegendColors={setCustomLegendColors}
|
value={legendPosition}
|
||||||
queryResponse={queryResponse}
|
style={{ width: '100%' }}
|
||||||
/>
|
className="panel-type-select"
|
||||||
|
defaultValue={LegendPosition.BOTTOM}
|
||||||
|
>
|
||||||
|
<Option value={LegendPosition.BOTTOM}>
|
||||||
|
<div className="select-option">
|
||||||
|
<Typography.Text className="display">Bottom</Typography.Text>
|
||||||
|
</div>
|
||||||
|
</Option>
|
||||||
|
<Option value={LegendPosition.RIGHT}>
|
||||||
|
<div className="select-option">
|
||||||
|
<Typography.Text className="display">Right</Typography.Text>
|
||||||
|
</div>
|
||||||
|
</Option>
|
||||||
|
</Select>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{allowLegendColors && (
|
||||||
|
<section className="legend-colors">
|
||||||
|
<LegendColors
|
||||||
|
customLegendColors={customLegendColors}
|
||||||
|
setCustomLegendColors={setCustomLegendColors}
|
||||||
|
queryResponse={queryResponse}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
</SettingsSection>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{allowBucketConfig && (
|
{allowBucketConfig && (
|
||||||
<HistogramBucketsSection
|
<SettingsSection title="Histogram / Buckets">
|
||||||
bucketCount={bucketCount}
|
<section className="bucket-config control-container">
|
||||||
setBucketCount={setBucketCount}
|
<Typography.Text className="label">Number of buckets</Typography.Text>
|
||||||
bucketWidth={bucketWidth}
|
<InputNumber
|
||||||
setBucketWidth={setBucketWidth}
|
value={bucketCount || null}
|
||||||
combineHistogram={combineHistogram}
|
type="number"
|
||||||
setCombineHistogram={setCombineHistogram}
|
min={0}
|
||||||
/>
|
rootClassName="bucket-input"
|
||||||
|
placeholder="Default: 30"
|
||||||
|
onChange={(val): void => {
|
||||||
|
setBucketCount(val || 0);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography.Text className="label bucket-size-label">
|
||||||
|
Bucket width
|
||||||
|
</Typography.Text>
|
||||||
|
<InputNumber
|
||||||
|
value={bucketWidth || null}
|
||||||
|
type="number"
|
||||||
|
precision={2}
|
||||||
|
placeholder="Default: Auto"
|
||||||
|
step={0.1}
|
||||||
|
min={0.0}
|
||||||
|
rootClassName="bucket-input"
|
||||||
|
onChange={(val): void => {
|
||||||
|
setBucketWidth(val || 0);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<section className="combine-hist">
|
||||||
|
<Typography.Text className="label">
|
||||||
|
Merge all series into one
|
||||||
|
</Typography.Text>
|
||||||
|
<Switch
|
||||||
|
checked={combineHistogram}
|
||||||
|
size="small"
|
||||||
|
onChange={(checked): void => setCombineHistogram(checked)}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</SettingsSection>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{allowCreateAlerts && (
|
{allowCreateAlerts && (
|
||||||
<AlertsSection onCreateAlertsHandler={onCreateAlertsHandler} />
|
<section className="alerts" onClick={onCreateAlertsHandler}>
|
||||||
|
<div className="left-section">
|
||||||
|
<ConciergeBell size={14} className="bell-icon" />
|
||||||
|
<Typography.Text className="alerts-text">Alerts</Typography.Text>
|
||||||
|
<SquareArrowOutUpRight size={10} className="info-icon" />
|
||||||
|
</div>
|
||||||
|
<Plus size={14} className="plus-icon" />
|
||||||
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{allowContextLinks && (
|
{allowContextLinks && (
|
||||||
<ContextLinksSection
|
<SettingsSection
|
||||||
contextLinks={contextLinks}
|
title="Context Links"
|
||||||
setContextLinks={setContextLinks}
|
icon={<Link size={14} />}
|
||||||
selectedWidget={selectedWidget}
|
defaultOpen={!!contextLinks.linksData.length}
|
||||||
/>
|
>
|
||||||
|
<ContextLinks
|
||||||
|
contextLinks={contextLinks}
|
||||||
|
setContextLinks={setContextLinks}
|
||||||
|
selectedWidget={selectedWidget}
|
||||||
|
/>
|
||||||
|
</SettingsSection>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{allowThreshold && (
|
{allowThreshold && (
|
||||||
<ThresholdsSection
|
<SettingsSection
|
||||||
thresholds={thresholds}
|
title="Thresholds"
|
||||||
setThresholds={setThresholds}
|
icon={<Antenna size={14} />}
|
||||||
yAxisUnit={yAxisUnit}
|
defaultOpen={!!thresholds.length}
|
||||||
selectedGraph={selectedGraph}
|
>
|
||||||
columnUnits={columnUnits}
|
<ThresholdSelector
|
||||||
/>
|
thresholds={thresholds}
|
||||||
|
setThresholds={setThresholds}
|
||||||
|
yAxisUnit={yAxisUnit}
|
||||||
|
selectedGraph={selectedGraph}
|
||||||
|
columnUnits={columnUnits}
|
||||||
|
/>
|
||||||
|
</SettingsSection>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -356,14 +682,6 @@ export interface RightContainerProps {
|
|||||||
setContextLinks: Dispatch<SetStateAction<ContextLinksData>>;
|
setContextLinks: Dispatch<SetStateAction<ContextLinksData>>;
|
||||||
enableDrillDown?: boolean;
|
enableDrillDown?: boolean;
|
||||||
isNewDashboard: boolean;
|
isNewDashboard: boolean;
|
||||||
lineInterpolation: LineInterpolation;
|
|
||||||
setLineInterpolation: Dispatch<SetStateAction<LineInterpolation>>;
|
|
||||||
fillMode: FillMode;
|
|
||||||
setFillMode: Dispatch<SetStateAction<FillMode>>;
|
|
||||||
lineStyle: LineStyle;
|
|
||||||
setLineStyle: Dispatch<SetStateAction<LineStyle>>;
|
|
||||||
showPoints: boolean;
|
|
||||||
setShowPoints: Dispatch<SetStateAction<boolean>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RightContainer.defaultProps = {
|
RightContainer.defaultProps = {
|
||||||
|
|||||||
@@ -6,11 +6,6 @@ import { UseQueryResult } from 'react-query';
|
|||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { generatePath } from 'react-router-dom';
|
import { generatePath } from 'react-router-dom';
|
||||||
import { WarningOutlined } from '@ant-design/icons';
|
import { WarningOutlined } from '@ant-design/icons';
|
||||||
import {
|
|
||||||
ResizableHandle,
|
|
||||||
ResizablePanel,
|
|
||||||
ResizablePanelGroup,
|
|
||||||
} from '@signozhq/resizable';
|
|
||||||
import { Button, Flex, Modal, Space, Typography } from 'antd';
|
import { Button, Flex, Modal, Space, Typography } from 'antd';
|
||||||
import logEvent from 'api/common/logEvent';
|
import logEvent from 'api/common/logEvent';
|
||||||
import { PrecisionOption, PrecisionOptionsEnum } from 'components/Graph/types';
|
import { PrecisionOption, PrecisionOptionsEnum } from 'components/Graph/types';
|
||||||
@@ -29,16 +24,12 @@ import { useDashboardVariables } from 'hooks/dashboard/useDashboardVariables';
|
|||||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||||
import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys';
|
import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import { useSafeNavigate } from 'hooks/useSafeNavigate';
|
import { useSafeNavigate } from 'hooks/useSafeNavigate';
|
||||||
import useUrlQuery from 'hooks/useUrlQuery';
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
import createQueryParams from 'lib/createQueryParams';
|
import createQueryParams from 'lib/createQueryParams';
|
||||||
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||||
import { getDashboardVariables } from 'lib/dashboardVariables/getDashboardVariables';
|
import { getDashboardVariables } from 'lib/dashboardVariables/getDashboardVariables';
|
||||||
import {
|
|
||||||
FillMode,
|
|
||||||
LineInterpolation,
|
|
||||||
LineStyle,
|
|
||||||
} from 'lib/uPlotV2/config/types';
|
|
||||||
import { cloneDeep, defaultTo, isEmpty, isUndefined } from 'lodash-es';
|
import { cloneDeep, defaultTo, isEmpty, isUndefined } from 'lodash-es';
|
||||||
import { Check, X } from 'lucide-react';
|
import { Check, X } from 'lucide-react';
|
||||||
import { useScrollToWidgetIdStore } from 'providers/Dashboard/helpers/scrollToWidgetIdHelper';
|
import { useScrollToWidgetIdStore } from 'providers/Dashboard/helpers/scrollToWidgetIdHelper';
|
||||||
@@ -72,7 +63,12 @@ import QueryTypeTag from './LeftContainer/QueryTypeTag';
|
|||||||
import RightContainer from './RightContainer';
|
import RightContainer from './RightContainer';
|
||||||
import { ThresholdProps } from './RightContainer/Threshold/types';
|
import { ThresholdProps } from './RightContainer/Threshold/types';
|
||||||
import TimeItems, { timePreferance } from './RightContainer/timeItems';
|
import TimeItems, { timePreferance } from './RightContainer/timeItems';
|
||||||
import { Container, PanelContainer } from './styles';
|
import {
|
||||||
|
Container,
|
||||||
|
LeftContainerWrapper,
|
||||||
|
PanelContainer,
|
||||||
|
RightContainerWrapper,
|
||||||
|
} from './styles';
|
||||||
import { NewWidgetProps } from './types';
|
import { NewWidgetProps } from './types';
|
||||||
import {
|
import {
|
||||||
getDefaultWidgetData,
|
getDefaultWidgetData,
|
||||||
@@ -208,18 +204,6 @@ function NewWidget({
|
|||||||
const [legendPosition, setLegendPosition] = useState<LegendPosition>(
|
const [legendPosition, setLegendPosition] = useState<LegendPosition>(
|
||||||
selectedWidget?.legendPosition || LegendPosition.BOTTOM,
|
selectedWidget?.legendPosition || LegendPosition.BOTTOM,
|
||||||
);
|
);
|
||||||
const [lineInterpolation, setLineInterpolation] = useState<LineInterpolation>(
|
|
||||||
selectedWidget?.lineInterpolation || LineInterpolation.Spline,
|
|
||||||
);
|
|
||||||
const [fillMode, setFillMode] = useState<FillMode>(
|
|
||||||
selectedWidget?.fillMode || FillMode.None,
|
|
||||||
);
|
|
||||||
const [lineStyle, setLineStyle] = useState<LineStyle>(
|
|
||||||
selectedWidget?.lineStyle || LineStyle.Solid,
|
|
||||||
);
|
|
||||||
const [showPoints, setShowPoints] = useState<boolean>(
|
|
||||||
selectedWidget?.showPoints ?? false,
|
|
||||||
);
|
|
||||||
const [customLegendColors, setCustomLegendColors] = useState<
|
const [customLegendColors, setCustomLegendColors] = useState<
|
||||||
Record<string, string>
|
Record<string, string>
|
||||||
>(selectedWidget?.customLegendColors || {});
|
>(selectedWidget?.customLegendColors || {});
|
||||||
@@ -285,10 +269,6 @@ function NewWidget({
|
|||||||
softMin,
|
softMin,
|
||||||
softMax,
|
softMax,
|
||||||
fillSpans: isFillSpans,
|
fillSpans: isFillSpans,
|
||||||
lineInterpolation,
|
|
||||||
fillMode,
|
|
||||||
lineStyle,
|
|
||||||
showPoints,
|
|
||||||
columnUnits,
|
columnUnits,
|
||||||
bucketCount,
|
bucketCount,
|
||||||
stackedBarChart,
|
stackedBarChart,
|
||||||
@@ -324,10 +304,6 @@ function NewWidget({
|
|||||||
stackedBarChart,
|
stackedBarChart,
|
||||||
isLogScale,
|
isLogScale,
|
||||||
legendPosition,
|
legendPosition,
|
||||||
lineInterpolation,
|
|
||||||
fillMode,
|
|
||||||
lineStyle,
|
|
||||||
showPoints,
|
|
||||||
customLegendColors,
|
customLegendColors,
|
||||||
contextLinks,
|
contextLinks,
|
||||||
selectedWidget.columnWidths,
|
selectedWidget.columnWidths,
|
||||||
@@ -781,7 +757,7 @@ function NewWidget({
|
|||||||
}, [query, safeNavigate, dashboardId, currentQuery]);
|
}, [query, safeNavigate, dashboardId, currentQuery]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className="new-widget-container">
|
<Container>
|
||||||
<div className="edit-header">
|
<div className="edit-header">
|
||||||
<div className="left-header">
|
<div className="left-header">
|
||||||
<X
|
<X
|
||||||
@@ -835,102 +811,79 @@ function NewWidget({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PanelContainer>
|
<PanelContainer>
|
||||||
<ResizablePanelGroup
|
<LeftContainerWrapper isDarkMode={useIsDarkMode()}>
|
||||||
direction="horizontal"
|
<OverlayScrollbar>
|
||||||
className="widget-resizable-panel-group"
|
{selectedWidget && (
|
||||||
autoSaveId="panel-editor"
|
<LeftContainer
|
||||||
>
|
selectedGraph={graphType}
|
||||||
<ResizablePanel
|
selectedLogFields={selectedLogFields}
|
||||||
minSize={70}
|
setSelectedLogFields={setSelectedLogFields}
|
||||||
maxSize={80}
|
selectedTracesFields={selectedTracesFields}
|
||||||
defaultSize={80}
|
setSelectedTracesFields={setSelectedTracesFields}
|
||||||
className="resizable-panel-left-container"
|
selectedWidget={selectedWidget}
|
||||||
>
|
selectedTime={selectedTime}
|
||||||
<OverlayScrollbar>
|
requestData={requestData}
|
||||||
{selectedWidget && (
|
setRequestData={setRequestData}
|
||||||
<LeftContainer
|
isLoadingPanelData={isLoadingPanelData}
|
||||||
selectedDashboard={selectedDashboard}
|
setQueryResponse={setQueryResponse}
|
||||||
selectedGraph={graphType}
|
enableDrillDown={enableDrillDown}
|
||||||
selectedLogFields={selectedLogFields}
|
selectedDashboard={selectedDashboard}
|
||||||
setSelectedLogFields={setSelectedLogFields}
|
isNewPanel={isNewPanel}
|
||||||
selectedTracesFields={selectedTracesFields}
|
/>
|
||||||
setSelectedTracesFields={setSelectedTracesFields}
|
)}
|
||||||
selectedWidget={selectedWidget}
|
</OverlayScrollbar>
|
||||||
selectedTime={selectedTime}
|
</LeftContainerWrapper>
|
||||||
requestData={requestData}
|
|
||||||
setRequestData={setRequestData}
|
<RightContainerWrapper>
|
||||||
isLoadingPanelData={isLoadingPanelData}
|
<RightContainer
|
||||||
setQueryResponse={setQueryResponse}
|
setGraphHandler={setGraphHandler}
|
||||||
enableDrillDown={enableDrillDown}
|
title={title}
|
||||||
/>
|
setTitle={setTitle}
|
||||||
)}
|
description={description}
|
||||||
</OverlayScrollbar>
|
setDescription={setDescription}
|
||||||
</ResizablePanel>
|
stackedBarChart={stackedBarChart}
|
||||||
<ResizableHandle withHandle className="widget-resizable-handle" />
|
setStackedBarChart={setStackedBarChart}
|
||||||
<ResizablePanel
|
opacity={opacity}
|
||||||
minSize={20}
|
yAxisUnit={yAxisUnit}
|
||||||
maxSize={30}
|
columnUnits={columnUnits}
|
||||||
defaultSize={20}
|
setColumnUnits={setColumnUnits}
|
||||||
className="resizable-panel-right-container"
|
bucketCount={bucketCount}
|
||||||
>
|
bucketWidth={bucketWidth}
|
||||||
<RightContainer
|
combineHistogram={combineHistogram}
|
||||||
setGraphHandler={setGraphHandler}
|
setCombineHistogram={setCombineHistogram}
|
||||||
title={title}
|
setBucketWidth={setBucketWidth}
|
||||||
setTitle={setTitle}
|
setBucketCount={setBucketCount}
|
||||||
description={description}
|
setOpacity={setOpacity}
|
||||||
setDescription={setDescription}
|
selectedNullZeroValue={selectedNullZeroValue}
|
||||||
stackedBarChart={stackedBarChart}
|
setSelectedNullZeroValue={setSelectedNullZeroValue}
|
||||||
setStackedBarChart={setStackedBarChart}
|
selectedGraph={graphType}
|
||||||
lineInterpolation={lineInterpolation}
|
setSelectedTime={setSelectedTime}
|
||||||
setLineInterpolation={setLineInterpolation}
|
selectedTime={selectedTime}
|
||||||
fillMode={fillMode}
|
setYAxisUnit={setYAxisUnit}
|
||||||
setFillMode={setFillMode}
|
decimalPrecision={decimalPrecision}
|
||||||
lineStyle={lineStyle}
|
setDecimalPrecision={setDecimalPrecision}
|
||||||
setLineStyle={setLineStyle}
|
thresholds={thresholds}
|
||||||
showPoints={showPoints}
|
setThresholds={setThresholds}
|
||||||
setShowPoints={setShowPoints}
|
selectedWidget={selectedWidget}
|
||||||
opacity={opacity}
|
isFillSpans={isFillSpans}
|
||||||
yAxisUnit={yAxisUnit}
|
setIsFillSpans={setIsFillSpans}
|
||||||
columnUnits={columnUnits}
|
isLogScale={isLogScale}
|
||||||
setColumnUnits={setColumnUnits}
|
setIsLogScale={setIsLogScale}
|
||||||
bucketCount={bucketCount}
|
legendPosition={legendPosition}
|
||||||
bucketWidth={bucketWidth}
|
setLegendPosition={setLegendPosition}
|
||||||
combineHistogram={combineHistogram}
|
customLegendColors={customLegendColors}
|
||||||
setCombineHistogram={setCombineHistogram}
|
setCustomLegendColors={setCustomLegendColors}
|
||||||
setBucketWidth={setBucketWidth}
|
queryResponse={queryResponse}
|
||||||
setBucketCount={setBucketCount}
|
softMin={softMin}
|
||||||
setOpacity={setOpacity}
|
setSoftMin={setSoftMin}
|
||||||
selectedNullZeroValue={selectedNullZeroValue}
|
softMax={softMax}
|
||||||
setSelectedNullZeroValue={setSelectedNullZeroValue}
|
setSoftMax={setSoftMax}
|
||||||
selectedGraph={graphType}
|
contextLinks={contextLinks}
|
||||||
setSelectedTime={setSelectedTime}
|
setContextLinks={setContextLinks}
|
||||||
selectedTime={selectedTime}
|
enableDrillDown={enableDrillDown}
|
||||||
setYAxisUnit={setYAxisUnit}
|
isNewDashboard={isNewDashboard}
|
||||||
decimalPrecision={decimalPrecision}
|
/>
|
||||||
setDecimalPrecision={setDecimalPrecision}
|
</RightContainerWrapper>
|
||||||
thresholds={thresholds}
|
|
||||||
setThresholds={setThresholds}
|
|
||||||
selectedWidget={selectedWidget}
|
|
||||||
isFillSpans={isFillSpans}
|
|
||||||
setIsFillSpans={setIsFillSpans}
|
|
||||||
isLogScale={isLogScale}
|
|
||||||
setIsLogScale={setIsLogScale}
|
|
||||||
legendPosition={legendPosition}
|
|
||||||
setLegendPosition={setLegendPosition}
|
|
||||||
customLegendColors={customLegendColors}
|
|
||||||
setCustomLegendColors={setCustomLegendColors}
|
|
||||||
queryResponse={queryResponse}
|
|
||||||
softMin={softMin}
|
|
||||||
setSoftMin={setSoftMin}
|
|
||||||
softMax={softMax}
|
|
||||||
setSoftMax={setSoftMax}
|
|
||||||
contextLinks={contextLinks}
|
|
||||||
setContextLinks={setContextLinks}
|
|
||||||
enableDrillDown={enableDrillDown}
|
|
||||||
isNewDashboard={isNewDashboard}
|
|
||||||
/>
|
|
||||||
</ResizablePanel>
|
|
||||||
</ResizablePanelGroup>
|
|
||||||
</PanelContainer>
|
</PanelContainer>
|
||||||
<Modal
|
<Modal
|
||||||
title={
|
title={
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Tag as AntDTag } from 'antd';
|
import { Col, Tag as AntDTag } from 'antd';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const Container = styled.div`
|
export const Container = styled.div`
|
||||||
@@ -8,6 +8,42 @@ export const Container = styled.div`
|
|||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const RightContainerWrapper = styled(Col)`
|
||||||
|
&&& {
|
||||||
|
max-width: 400px;
|
||||||
|
width: 30%;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 0.3rem;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: rgb(136, 136, 136);
|
||||||
|
border-radius: 0.625rem;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface LeftContainerWrapperProps {
|
||||||
|
isDarkMode: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LeftContainerWrapper = styled(Col)<LeftContainerWrapperProps>`
|
||||||
|
&&& {
|
||||||
|
width: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
border-right: ${({ isDarkMode }): string =>
|
||||||
|
isDarkMode
|
||||||
|
? '1px solid var(--bg-slate-300)'
|
||||||
|
: '1px solid var(--bg-vanilla-300)'};
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 0rem;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const ButtonContainer = styled.div`
|
export const ButtonContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|||||||
@@ -11,8 +11,11 @@ import { getYAxisCategories } from 'components/YAxisUnitSelector/utils';
|
|||||||
import {
|
import {
|
||||||
initialQueryBuilderFormValuesMap,
|
initialQueryBuilderFormValuesMap,
|
||||||
PANEL_TYPES,
|
PANEL_TYPES,
|
||||||
PANEL_TYPES_INITIAL_QUERY,
|
|
||||||
} from 'constants/queryBuilder';
|
} from 'constants/queryBuilder';
|
||||||
|
import {
|
||||||
|
listViewInitialLogQuery,
|
||||||
|
PANEL_TYPES_INITIAL_QUERY,
|
||||||
|
} from 'container/DashboardContainer/ComponentsSlider/constants';
|
||||||
import {
|
import {
|
||||||
defaultLogsSelectedColumns,
|
defaultLogsSelectedColumns,
|
||||||
defaultTraceSelectedColumns,
|
defaultTraceSelectedColumns,
|
||||||
@@ -546,7 +549,10 @@ export const getDefaultWidgetData = (
|
|||||||
nullZeroValues: '',
|
nullZeroValues: '',
|
||||||
opacity: '',
|
opacity: '',
|
||||||
panelTypes: name,
|
panelTypes: name,
|
||||||
query: PANEL_TYPES_INITIAL_QUERY[name],
|
query:
|
||||||
|
name === PANEL_TYPES.LIST
|
||||||
|
? listViewInitialLogQuery
|
||||||
|
: PANEL_TYPES_INITIAL_QUERY[name],
|
||||||
timePreferance: 'GLOBAL_TIME',
|
timePreferance: 'GLOBAL_TIME',
|
||||||
softMax: null,
|
softMax: null,
|
||||||
softMin: null,
|
softMin: null,
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import {
|
|||||||
usePatchRole,
|
usePatchRole,
|
||||||
} from 'api/generated/services/role';
|
} from 'api/generated/services/role';
|
||||||
import {
|
import {
|
||||||
AuthtypesPostableRoleDTO,
|
|
||||||
RenderErrorResponseDTO,
|
RenderErrorResponseDTO,
|
||||||
|
RoletypesPostableRoleDTO,
|
||||||
} from 'api/generated/services/sigNoz.schemas';
|
} from 'api/generated/services/sigNoz.schemas';
|
||||||
import { ErrorType } from 'api/generatedAPIInstance';
|
import { ErrorType } from 'api/generatedAPIInstance';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
@@ -114,7 +114,7 @@ function CreateRoleModal({
|
|||||||
data: { description: values.description || '' },
|
data: { description: values.description || '' },
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const data: AuthtypesPostableRoleDTO = {
|
const data: RoletypesPostableRoleDTO = {
|
||||||
name: values.name,
|
name: values.name,
|
||||||
...(values.description ? { description: values.description } : {}),
|
...(values.description ? { description: values.description } : {}),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo } from 'react';
|
|||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { Pagination, Skeleton } from 'antd';
|
import { Pagination, Skeleton } from 'antd';
|
||||||
import { useListRoles } from 'api/generated/services/role';
|
import { useListRoles } from 'api/generated/services/role';
|
||||||
import { AuthtypesRoleDTO } from 'api/generated/services/sigNoz.schemas';
|
import { RoletypesRoleDTO } from 'api/generated/services/sigNoz.schemas';
|
||||||
import ErrorInPlace from 'components/ErrorInPlace/ErrorInPlace';
|
import ErrorInPlace from 'components/ErrorInPlace/ErrorInPlace';
|
||||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
@@ -20,7 +20,7 @@ const PAGE_SIZE = 20;
|
|||||||
|
|
||||||
type DisplayItem =
|
type DisplayItem =
|
||||||
| { type: 'section'; label: string; count?: number }
|
| { type: 'section'; label: string; count?: number }
|
||||||
| { type: 'role'; role: AuthtypesRoleDTO };
|
| { type: 'role'; role: RoletypesRoleDTO };
|
||||||
|
|
||||||
interface RolesListingTableProps {
|
interface RolesListingTableProps {
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
@@ -187,7 +187,7 @@ function RolesListingTable({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// todo: use table from periscope when its available for consumption
|
// todo: use table from periscope when its available for consumption
|
||||||
const renderRow = (role: AuthtypesRoleDTO): JSX.Element => (
|
const renderRow = (role: RoletypesRoleDTO): JSX.Element => (
|
||||||
<div
|
<div
|
||||||
key={role.id}
|
key={role.id}
|
||||||
className={`roles-table-row ${
|
className={`roles-table-row ${
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ import {
|
|||||||
ATTRIBUTE_TYPES,
|
ATTRIBUTE_TYPES,
|
||||||
initialAutocompleteData,
|
initialAutocompleteData,
|
||||||
initialQueryBuilderFormValuesMap,
|
initialQueryBuilderFormValuesMap,
|
||||||
listViewInitialLogQuery,
|
|
||||||
listViewInitialTraceQuery,
|
|
||||||
mapOfFormulaToFilters,
|
mapOfFormulaToFilters,
|
||||||
mapOfQueryFilters,
|
mapOfQueryFilters,
|
||||||
PANEL_TYPES,
|
PANEL_TYPES,
|
||||||
@@ -25,6 +23,10 @@ import {
|
|||||||
metricsUnknownSpaceAggregateOperatorOptions,
|
metricsUnknownSpaceAggregateOperatorOptions,
|
||||||
metricsUnknownTimeAggregateOperatorOptions,
|
metricsUnknownTimeAggregateOperatorOptions,
|
||||||
} from 'constants/queryBuilderOperators';
|
} from 'constants/queryBuilderOperators';
|
||||||
|
import {
|
||||||
|
listViewInitialLogQuery,
|
||||||
|
listViewInitialTraceQuery,
|
||||||
|
} from 'container/DashboardContainer/ComponentsSlider/constants';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import { getMetricsOperatorsByAttributeType } from 'lib/newQueryBuilder/getMetricsOperatorsByAttributeType';
|
import { getMetricsOperatorsByAttributeType } from 'lib/newQueryBuilder/getMetricsOperatorsByAttributeType';
|
||||||
import { getOperatorsBySourceAndPanelType } from 'lib/newQueryBuilder/getOperatorsBySourceAndPanelType';
|
import { getOperatorsBySourceAndPanelType } from 'lib/newQueryBuilder/getOperatorsBySourceAndPanelType';
|
||||||
|
|||||||
@@ -3,15 +3,14 @@ import { generateColor } from 'lib/uPlotLib/utils/generateColor';
|
|||||||
import { calculateWidthBasedOnStepInterval } from 'lib/uPlotV2/utils';
|
import { calculateWidthBasedOnStepInterval } from 'lib/uPlotV2/utils';
|
||||||
import uPlot, { Series } from 'uplot';
|
import uPlot, { Series } from 'uplot';
|
||||||
|
|
||||||
import { generateGradientFill } from '../utils/generateGradientFill';
|
|
||||||
import {
|
import {
|
||||||
BarAlignment,
|
BarAlignment,
|
||||||
ConfigBuilder,
|
ConfigBuilder,
|
||||||
DrawStyle,
|
DrawStyle,
|
||||||
FillMode,
|
|
||||||
LineInterpolation,
|
LineInterpolation,
|
||||||
LineStyle,
|
LineStyle,
|
||||||
SeriesProps,
|
SeriesProps,
|
||||||
|
VisibilityMode,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,7 +52,7 @@ export class UPlotSeriesBuilder extends ConfigBuilder<SeriesProps, Series> {
|
|||||||
}: {
|
}: {
|
||||||
resolvedLineColor: string;
|
resolvedLineColor: string;
|
||||||
}): Partial<Series> {
|
}): Partial<Series> {
|
||||||
const { lineWidth, lineStyle, lineCap, fillColor, fillMode } = this.props;
|
const { lineWidth, lineStyle, lineCap, fillColor } = this.props;
|
||||||
const lineConfig: Partial<Series> = {
|
const lineConfig: Partial<Series> = {
|
||||||
stroke: resolvedLineColor,
|
stroke: resolvedLineColor,
|
||||||
width: lineWidth ?? DEFAULT_LINE_WIDTH,
|
width: lineWidth ?? DEFAULT_LINE_WIDTH,
|
||||||
@@ -67,26 +66,12 @@ export class UPlotSeriesBuilder extends ConfigBuilder<SeriesProps, Series> {
|
|||||||
lineConfig.cap = lineCap;
|
lineConfig.cap = lineCap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (fillColor) {
|
||||||
* Configure area fill based on draw style and fill mode:
|
lineConfig.fill = fillColor;
|
||||||
* - bar charts always use a solid fill with the series color
|
} else if (this.props.drawStyle === DrawStyle.Bar) {
|
||||||
* - histogram uses the same color with a fixed alpha suffix for translucency
|
lineConfig.fill = resolvedLineColor;
|
||||||
* - for other series, an explicit fillMode controls whether we use a solid fill
|
|
||||||
* or a vertical gradient from the series color to transparent
|
|
||||||
*/
|
|
||||||
const finalFillColor = fillColor ?? resolvedLineColor;
|
|
||||||
|
|
||||||
if (this.props.drawStyle === DrawStyle.Bar) {
|
|
||||||
lineConfig.fill = finalFillColor;
|
|
||||||
} else if (this.props.drawStyle === DrawStyle.Histogram) {
|
} else if (this.props.drawStyle === DrawStyle.Histogram) {
|
||||||
lineConfig.fill = `${finalFillColor}40`;
|
lineConfig.fill = `${resolvedLineColor}40`;
|
||||||
} else if (fillMode && fillMode !== FillMode.None) {
|
|
||||||
if (fillMode === FillMode.Solid) {
|
|
||||||
lineConfig.fill = finalFillColor;
|
|
||||||
} else if (fillMode === FillMode.Gradient) {
|
|
||||||
lineConfig.fill = (self: uPlot): CanvasGradient =>
|
|
||||||
generateGradientFill(self, finalFillColor, 'rgba(0, 0, 0, 0)');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return lineConfig;
|
return lineConfig;
|
||||||
@@ -174,8 +159,12 @@ export class UPlotSeriesBuilder extends ConfigBuilder<SeriesProps, Series> {
|
|||||||
pointsConfig.show = pointsBuilder;
|
pointsConfig.show = pointsBuilder;
|
||||||
} else if (drawStyle === DrawStyle.Points) {
|
} else if (drawStyle === DrawStyle.Points) {
|
||||||
pointsConfig.show = true;
|
pointsConfig.show = true;
|
||||||
|
} else if (showPoints === VisibilityMode.Never) {
|
||||||
|
pointsConfig.show = false;
|
||||||
|
} else if (showPoints === VisibilityMode.Always) {
|
||||||
|
pointsConfig.show = true;
|
||||||
} else {
|
} else {
|
||||||
pointsConfig.show = !!showPoints;
|
pointsConfig.show = false; // default to hidden
|
||||||
}
|
}
|
||||||
|
|
||||||
return pointsConfig;
|
return pointsConfig;
|
||||||
|
|||||||
@@ -2,7 +2,12 @@ import { themeColors } from 'constants/theme';
|
|||||||
import uPlot from 'uplot';
|
import uPlot from 'uplot';
|
||||||
|
|
||||||
import type { SeriesProps } from '../types';
|
import type { SeriesProps } from '../types';
|
||||||
import { DrawStyle, LineInterpolation, LineStyle } from '../types';
|
import {
|
||||||
|
DrawStyle,
|
||||||
|
LineInterpolation,
|
||||||
|
LineStyle,
|
||||||
|
VisibilityMode,
|
||||||
|
} from '../types';
|
||||||
import { POINT_SIZE_FACTOR, UPlotSeriesBuilder } from '../UPlotSeriesBuilder';
|
import { POINT_SIZE_FACTOR, UPlotSeriesBuilder } from '../UPlotSeriesBuilder';
|
||||||
|
|
||||||
const createBaseProps = (
|
const createBaseProps = (
|
||||||
@@ -163,17 +168,17 @@ describe('UPlotSeriesBuilder', () => {
|
|||||||
expect(config.points?.show).toBe(pointsBuilder);
|
expect(config.points?.show).toBe(pointsBuilder);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('respects showPoints for point visibility when no custom pointsBuilder is given', () => {
|
it('respects VisibilityMode for point visibility when no custom pointsBuilder is given', () => {
|
||||||
const neverPointsBuilder = new UPlotSeriesBuilder(
|
const neverPointsBuilder = new UPlotSeriesBuilder(
|
||||||
createBaseProps({
|
createBaseProps({
|
||||||
drawStyle: DrawStyle.Line,
|
drawStyle: DrawStyle.Line,
|
||||||
showPoints: false,
|
showPoints: VisibilityMode.Never,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
const alwaysPointsBuilder = new UPlotSeriesBuilder(
|
const alwaysPointsBuilder = new UPlotSeriesBuilder(
|
||||||
createBaseProps({
|
createBaseProps({
|
||||||
drawStyle: DrawStyle.Line,
|
drawStyle: DrawStyle.Line,
|
||||||
showPoints: true,
|
showPoints: VisibilityMode.Always,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -122,6 +122,12 @@ export enum LineInterpolation {
|
|||||||
StepBefore = 'stepBefore',
|
StepBefore = 'stepBefore',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum VisibilityMode {
|
||||||
|
Always = 'always',
|
||||||
|
Auto = 'auto',
|
||||||
|
Never = 'never',
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Props for configuring lines
|
* Props for configuring lines
|
||||||
*/
|
*/
|
||||||
@@ -157,13 +163,7 @@ export interface BarConfig {
|
|||||||
export interface PointsConfig {
|
export interface PointsConfig {
|
||||||
pointColor?: string;
|
pointColor?: string;
|
||||||
pointSize?: number;
|
pointSize?: number;
|
||||||
showPoints?: boolean;
|
showPoints?: VisibilityMode;
|
||||||
}
|
|
||||||
|
|
||||||
export enum FillMode {
|
|
||||||
Solid = 'solid',
|
|
||||||
Gradient = 'gradient',
|
|
||||||
None = 'none',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SeriesProps extends LineConfig, PointsConfig, BarConfig {
|
export interface SeriesProps extends LineConfig, PointsConfig, BarConfig {
|
||||||
@@ -177,7 +177,6 @@ export interface SeriesProps extends LineConfig, PointsConfig, BarConfig {
|
|||||||
show?: boolean;
|
show?: boolean;
|
||||||
spanGaps?: boolean;
|
spanGaps?: boolean;
|
||||||
fillColor?: string;
|
fillColor?: string;
|
||||||
fillMode?: FillMode;
|
|
||||||
isDarkMode?: boolean;
|
isDarkMode?: boolean;
|
||||||
stepInterval?: number;
|
stepInterval?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user