Compare commits

..

1 Commits

Author SHA1 Message Date
Ishan Uniyal
5320138eb9 fix: added event propogation on enter press 2026-03-13 13:01:21 +05:30
1031 changed files with 23285 additions and 66838 deletions

View File

@@ -27,8 +27,8 @@ services:
- ${PWD}/fs/tmp/var/lib/clickhouse/user_scripts/:/var/lib/clickhouse/user_scripts/ - ${PWD}/fs/tmp/var/lib/clickhouse/user_scripts/:/var/lib/clickhouse/user_scripts/
- ${PWD}/../../../deploy/common/clickhouse/custom-function.xml:/etc/clickhouse-server/custom-function.xml - ${PWD}/../../../deploy/common/clickhouse/custom-function.xml:/etc/clickhouse-server/custom-function.xml
ports: ports:
- "127.0.0.1:8123:8123" - '127.0.0.1:8123:8123'
- "127.0.0.1:9000:9000" - '127.0.0.1:9000:9000'
tty: true tty: true
healthcheck: healthcheck:
test: test:
@@ -47,16 +47,13 @@ services:
condition: service_healthy condition: service_healthy
environment: environment:
- CLICKHOUSE_SKIP_USER_SETUP=1 - CLICKHOUSE_SKIP_USER_SETUP=1
networks:
- default
- signoz-devenv
zookeeper: zookeeper:
image: signoz/zookeeper:3.7.1 image: signoz/zookeeper:3.7.1
container_name: zookeeper container_name: zookeeper
volumes: volumes:
- ${PWD}/fs/tmp/zookeeper:/bitnami/zookeeper - ${PWD}/fs/tmp/zookeeper:/bitnami/zookeeper
ports: ports:
- "127.0.0.1:2181:2181" - '127.0.0.1:2181:2181'
environment: environment:
- ALLOW_ANONYMOUS_LOGIN=yes - ALLOW_ANONYMOUS_LOGIN=yes
healthcheck: healthcheck:
@@ -77,19 +74,12 @@ services:
entrypoint: entrypoint:
- /bin/sh - /bin/sh
command: command:
- -c - -c
- | - |
/signoz-otel-collector migrate bootstrap && /signoz-otel-collector migrate bootstrap &&
/signoz-otel-collector migrate sync up && /signoz-otel-collector migrate sync up &&
/signoz-otel-collector migrate async up /signoz-otel-collector migrate async up
depends_on: depends_on:
clickhouse: clickhouse:
condition: service_healthy condition: service_healthy
restart: on-failure restart: on-failure
networks:
- default
- signoz-devenv
networks:
signoz-devenv:
name: signoz-devenv

View File

@@ -3,7 +3,7 @@ services:
image: signoz/signoz-otel-collector:v0.142.0 image: signoz/signoz-otel-collector:v0.142.0
container_name: signoz-otel-collector-dev container_name: signoz-otel-collector-dev
entrypoint: entrypoint:
- /bin/sh - /bin/sh
command: command:
- -c - -c
- | - |
@@ -34,11 +34,4 @@ services:
retries: 3 retries: 3
restart: unless-stopped restart: unless-stopped
extra_hosts: extra_hosts:
- "host.docker.internal:host-gateway" - "host.docker.internal:host-gateway"
networks:
- default
- signoz-devenv
networks:
signoz-devenv:
name: signoz-devenv

View File

@@ -12,10 +12,10 @@ receivers:
scrape_configs: scrape_configs:
- job_name: otel-collector - job_name: otel-collector
static_configs: static_configs:
- targets: - targets:
- localhost:8888 - localhost:8888
labels: labels:
job_name: otel-collector job_name: otel-collector
processors: processors:
batch: batch:
@@ -29,26 +29,7 @@ processors:
signozspanmetrics/delta: signozspanmetrics/delta:
metrics_exporter: signozclickhousemetrics metrics_exporter: signozclickhousemetrics
metrics_flush_interval: 60s metrics_flush_interval: 60s
latency_histogram_buckets: latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ]
[
100us,
1ms,
2ms,
6ms,
10ms,
50ms,
100ms,
250ms,
500ms,
1000ms,
1400ms,
2000ms,
5s,
10s,
20s,
40s,
60s,
]
dimensions_cache_size: 100000 dimensions_cache_size: 100000
aggregation_temporality: AGGREGATION_TEMPORALITY_DELTA aggregation_temporality: AGGREGATION_TEMPORALITY_DELTA
enable_exp_histogram: true enable_exp_histogram: true
@@ -79,13 +60,13 @@ extensions:
exporters: exporters:
clickhousetraces: clickhousetraces:
datasource: tcp://clickhouse:9000/signoz_traces datasource: tcp://host.docker.internal:9000/signoz_traces
low_cardinal_exception_grouping: ${env:LOW_CARDINAL_EXCEPTION_GROUPING} low_cardinal_exception_grouping: ${env:LOW_CARDINAL_EXCEPTION_GROUPING}
use_new_schema: true use_new_schema: true
signozclickhousemetrics: signozclickhousemetrics:
dsn: tcp://clickhouse:9000/signoz_metrics dsn: tcp://host.docker.internal:9000/signoz_metrics
clickhouselogsexporter: clickhouselogsexporter:
dsn: tcp://clickhouse:9000/signoz_logs dsn: tcp://host.docker.internal:9000/signoz_logs
timeout: 10s timeout: 10s
use_new_schema: true use_new_schema: true
@@ -112,4 +93,4 @@ service:
logs: logs:
receivers: [otlp] receivers: [otlp]
processors: [batch] processors: [batch]
exporters: [clickhouselogsexporter] exporters: [clickhouselogsexporter]

21
.github/CODEOWNERS vendored
View File

@@ -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
@@ -86,8 +86,6 @@ go.mod @therealpandey
/pkg/types/alertmanagertypes @srikanthccv /pkg/types/alertmanagertypes @srikanthccv
/pkg/alertmanager/ @srikanthccv /pkg/alertmanager/ @srikanthccv
/pkg/ruler/ @srikanthccv /pkg/ruler/ @srikanthccv
/pkg/modules/rulestatehistory/ @srikanthccv
/pkg/types/rulestatehistorytypes/ @srikanthccv
# Correlation-adjacent # Correlation-adjacent
@@ -107,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
@@ -133,15 +127,12 @@ go.mod @therealpandey
/frontend/src/pages/DashboardsListPage/ @SigNoz/pulse-frontend /frontend/src/pages/DashboardsListPage/ @SigNoz/pulse-frontend
/frontend/src/container/ListOfDashboard/ @SigNoz/pulse-frontend /frontend/src/container/ListOfDashboard/ @SigNoz/pulse-frontend
# Dashboard Widget Page
/frontend/src/pages/DashboardWidget/ @SigNoz/pulse-frontend
/frontend/src/container/NewWidget/ @SigNoz/pulse-frontend
## Dashboard Page ## Dashboard Page
/frontend/src/pages/DashboardPage/ @SigNoz/pulse-frontend /frontend/src/pages/DashboardPage/ @SigNoz/pulse-frontend
/frontend/src/container/DashboardContainer/ @SigNoz/pulse-frontend /frontend/src/container/DashboardContainer/ @SigNoz/pulse-frontend
/frontend/src/container/GridCardLayout/ @SigNoz/pulse-frontend /frontend/src/container/GridCardLayout/ @SigNoz/pulse-frontend
/frontend/src/container/NewWidget/ @SigNoz/pulse-frontend
## Public Dashboard Page ## Public Dashboard Page

View File

@@ -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)

View File

@@ -29,7 +29,6 @@ jobs:
- name: fmt - name: fmt
run: | run: |
make py-fmt make py-fmt
git diff --exit-code -- tests/integration/
- name: lint - name: lint
run: | run: |
make py-lint make py-lint
@@ -50,8 +49,6 @@ jobs:
- ttl - ttl
- alerts - alerts
- ingestionkeys - ingestionkeys
- rootuser
- serviceaccount
sqlstore-provider: sqlstore-provider:
- postgres - postgres
- sqlite - sqlite

View File

@@ -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)

View File

@@ -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."

View File

@@ -6,14 +6,12 @@ linters:
- depguard - depguard
- errcheck - errcheck
- forbidigo - forbidigo
- godot
- govet - govet
- iface - iface
- ineffassign - ineffassign
- misspell - misspell
- nilnil - nilnil
- sloglint - sloglint
- staticcheck
- wastedassign - wastedassign
- unparam - unparam
- unused - unused
@@ -37,7 +35,7 @@ linters:
- identical - identical
sloglint: sloglint:
no-mixed-args: true no-mixed-args: true
attr-only: true kv-only: true
no-global: all no-global: all
context: all context: all
static-msg: true static-msg: true

View File

@@ -17,7 +17,5 @@
}, },
"[html]": { "[html]": {
"editor.defaultFormatter": "vscode.html-language-features" "editor.defaultFormatter": "vscode.html-language-features"
}, }
"python-envs.defaultEnvManager": "ms-python.python:system",
"python-envs.pythonProjects": []
} }

View File

@@ -4,16 +4,12 @@ import (
"context" "context"
"log/slog" "log/slog"
"github.com/spf13/cobra"
"github.com/SigNoz/signoz/cmd" "github.com/SigNoz/signoz/cmd"
"github.com/SigNoz/signoz/pkg/analytics" "github.com/SigNoz/signoz/pkg/analytics"
"github.com/SigNoz/signoz/pkg/authn" "github.com/SigNoz/signoz/pkg/authn"
"github.com/SigNoz/signoz/pkg/authz" "github.com/SigNoz/signoz/pkg/authz"
"github.com/SigNoz/signoz/pkg/authz/openfgaauthz" "github.com/SigNoz/signoz/pkg/authz/openfgaauthz"
"github.com/SigNoz/signoz/pkg/authz/openfgaschema" "github.com/SigNoz/signoz/pkg/authz/openfgaschema"
"github.com/SigNoz/signoz/pkg/authz/openfgaserver"
"github.com/SigNoz/signoz/pkg/errors"
"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"
@@ -32,17 +28,18 @@ import (
"github.com/SigNoz/signoz/pkg/version" "github.com/SigNoz/signoz/pkg/version"
"github.com/SigNoz/signoz/pkg/zeus" "github.com/SigNoz/signoz/pkg/zeus"
"github.com/SigNoz/signoz/pkg/zeus/noopzeus" "github.com/SigNoz/signoz/pkg/zeus/noopzeus"
"github.com/spf13/cobra"
) )
func registerServer(parentCmd *cobra.Command, logger *slog.Logger) { func registerServer(parentCmd *cobra.Command, logger *slog.Logger) {
var configFiles []string var flags signoz.DeprecatedFlags
serverCmd := &cobra.Command{ serverCmd := &cobra.Command{
Use: "server", Use: "server",
Short: "Run the SigNoz server", Short: "Run the SigNoz server",
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true}, FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
RunE: func(currCmd *cobra.Command, args []string) error { RunE: func(currCmd *cobra.Command, args []string) error {
config, err := cmd.NewSigNozConfig(currCmd.Context(), logger, configFiles) config, err := cmd.NewSigNozConfig(currCmd.Context(), logger, flags)
if err != nil { if err != nil {
return err return err
} }
@@ -51,7 +48,7 @@ func registerServer(parentCmd *cobra.Command, logger *slog.Logger) {
}, },
} }
serverCmd.Flags().StringArrayVar(&configFiles, "config", nil, "path to a YAML configuration file (can be specified multiple times, later files override earlier ones)") flags.RegisterFlags(serverCmd)
parentCmd.AddCommand(serverCmd) parentCmd.AddCommand(serverCmd)
} }
@@ -79,13 +76,8 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
func(ctx context.Context, providerSettings factory.ProviderSettings, store authtypes.AuthNStore, licensing licensing.Licensing) (map[authtypes.AuthNProvider]authn.AuthN, error) { func(ctx context.Context, providerSettings factory.ProviderSettings, store authtypes.AuthNStore, licensing licensing.Licensing) (map[authtypes.AuthNProvider]authn.AuthN, error) {
return signoz.NewAuthNs(ctx, providerSettings, store, licensing) return signoz.NewAuthNs(ctx, providerSettings, store, licensing)
}, },
func(ctx context.Context, sqlstore sqlstore.SQLStore, _ licensing.Licensing, _ dashboard.Module) (factory.ProviderFactory[authz.AuthZ, authz.Config], error) { func(ctx context.Context, sqlstore sqlstore.SQLStore, _ licensing.Licensing, _ dashboard.Module) factory.ProviderFactory[authz.AuthZ, authz.Config] {
openfgaDataStore, err := openfgaserver.NewSQLStore(sqlstore) return openfgaauthz.NewProviderFactory(sqlstore, openfgaschema.NewSchema().Get(ctx))
if err != nil {
return nil, err
}
return openfgaauthz.NewProviderFactory(sqlstore, openfgaschema.NewSchema().Get(ctx), openfgaDataStore), nil
}, },
func(store sqlstore.SQLStore, settings factory.ProviderSettings, analytics analytics.Analytics, orgGetter organization.Getter, queryParser queryparser.QueryParser, _ querier.Querier, _ licensing.Licensing) dashboard.Module { func(store sqlstore.SQLStore, settings factory.ProviderSettings, analytics analytics.Analytics, orgGetter organization.Getter, queryParser queryparser.QueryParser, _ querier.Querier, _ licensing.Licensing) dashboard.Module {
return impldashboard.NewModule(impldashboard.NewStore(store), settings, analytics, orgGetter, queryParser) return impldashboard.NewModule(impldashboard.NewStore(store), settings, analytics, orgGetter, queryParser)
@@ -98,37 +90,37 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
}, },
) )
if err != nil { if err != nil {
logger.ErrorContext(ctx, "failed to create signoz", errors.Attr(err)) logger.ErrorContext(ctx, "failed to create signoz", "error", err)
return err return err
} }
server, err := app.NewServer(config, signoz) server, err := app.NewServer(config, signoz)
if err != nil { if err != nil {
logger.ErrorContext(ctx, "failed to create server", errors.Attr(err)) logger.ErrorContext(ctx, "failed to create server", "error", err)
return err return err
} }
if err := server.Start(ctx); err != nil { if err := server.Start(ctx); err != nil {
logger.ErrorContext(ctx, "failed to start server", errors.Attr(err)) logger.ErrorContext(ctx, "failed to start server", "error", err)
return err return err
} }
signoz.Start(ctx) signoz.Start(ctx)
if err := signoz.Wait(ctx); err != nil { if err := signoz.Wait(ctx); err != nil {
logger.ErrorContext(ctx, "failed to start signoz", errors.Attr(err)) logger.ErrorContext(ctx, "failed to start signoz", "error", err)
return err return err
} }
err = server.Stop(ctx) err = server.Stop(ctx)
if err != nil { if err != nil {
logger.ErrorContext(ctx, "failed to stop server", errors.Attr(err)) logger.ErrorContext(ctx, "failed to stop server", "error", err)
return err return err
} }
err = signoz.Stop(ctx) err = signoz.Stop(ctx)
if err != nil { if err != nil {
logger.ErrorContext(ctx, "failed to stop signoz", errors.Attr(err)) logger.ErrorContext(ctx, "failed to stop signoz", "error", err)
return err return err
} }

View File

@@ -10,23 +10,18 @@ import (
"github.com/SigNoz/signoz/pkg/signoz" "github.com/SigNoz/signoz/pkg/signoz"
) )
func NewSigNozConfig(ctx context.Context, logger *slog.Logger, configFiles []string) (signoz.Config, error) { func NewSigNozConfig(ctx context.Context, logger *slog.Logger, flags signoz.DeprecatedFlags) (signoz.Config, error) {
uris := make([]string, 0, len(configFiles)+1)
for _, f := range configFiles {
uris = append(uris, "file:"+f)
}
uris = append(uris, "env:")
config, err := signoz.NewConfig( config, err := signoz.NewConfig(
ctx, ctx,
logger, logger,
config.ResolverConfig{ config.ResolverConfig{
Uris: uris, Uris: []string{"env:"},
ProviderFactories: []config.ProviderFactory{ ProviderFactories: []config.ProviderFactory{
envprovider.NewFactory(), envprovider.NewFactory(),
fileprovider.NewFactory(), fileprovider.NewFactory(),
}, },
}, },
flags,
) )
if err != nil { if err != nil {
return signoz.Config{}, err return signoz.Config{}, err

View File

@@ -1,86 +0,0 @@
package cmd
import (
"context"
"log/slog"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewSigNozConfig_NoConfigFiles(t *testing.T) {
logger := slog.New(slog.DiscardHandler)
config, err := NewSigNozConfig(context.Background(), logger, nil)
require.NoError(t, err)
assert.NotZero(t, config)
}
func TestNewSigNozConfig_SingleConfigFile(t *testing.T) {
dir := t.TempDir()
configPath := filepath.Join(dir, "config.yaml")
err := os.WriteFile(configPath, []byte(`
cache:
provider: "redis"
`), 0644)
require.NoError(t, err)
logger := slog.New(slog.DiscardHandler)
config, err := NewSigNozConfig(context.Background(), logger, []string{configPath})
require.NoError(t, err)
assert.Equal(t, "redis", config.Cache.Provider)
}
func TestNewSigNozConfig_MultipleConfigFiles_LaterOverridesEarlier(t *testing.T) {
dir := t.TempDir()
basePath := filepath.Join(dir, "base.yaml")
err := os.WriteFile(basePath, []byte(`
cache:
provider: "memory"
sqlstore:
provider: "sqlite"
`), 0644)
require.NoError(t, err)
overridePath := filepath.Join(dir, "override.yaml")
err = os.WriteFile(overridePath, []byte(`
cache:
provider: "redis"
`), 0644)
require.NoError(t, err)
logger := slog.New(slog.DiscardHandler)
config, err := NewSigNozConfig(context.Background(), logger, []string{basePath, overridePath})
require.NoError(t, err)
// Later file overrides earlier
assert.Equal(t, "redis", config.Cache.Provider)
// Value from base file that wasn't overridden persists
assert.Equal(t, "sqlite", config.SQLStore.Provider)
}
func TestNewSigNozConfig_EnvOverridesConfigFile(t *testing.T) {
dir := t.TempDir()
configPath := filepath.Join(dir, "config.yaml")
err := os.WriteFile(configPath, []byte(`
cache:
provider: "fromfile"
`), 0644)
require.NoError(t, err)
t.Setenv("SIGNOZ_CACHE_PROVIDER", "fromenv")
logger := slog.New(slog.DiscardHandler)
config, err := NewSigNozConfig(context.Background(), logger, []string{configPath})
require.NoError(t, err)
// Env should override file
assert.Equal(t, "fromenv", config.Cache.Provider)
}
func TestNewSigNozConfig_NonexistentFile(t *testing.T) {
logger := slog.New(slog.DiscardHandler)
_, err := NewSigNozConfig(context.Background(), logger, []string{"/nonexistent/config.yaml"})
assert.Error(t, err)
}

View File

@@ -5,19 +5,16 @@ import (
"log/slog" "log/slog"
"time" "time"
"github.com/spf13/cobra"
"github.com/SigNoz/signoz/cmd" "github.com/SigNoz/signoz/cmd"
"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/authz/openfgaserver"
"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/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"
@@ -26,7 +23,6 @@ import (
"github.com/SigNoz/signoz/pkg/analytics" "github.com/SigNoz/signoz/pkg/analytics"
"github.com/SigNoz/signoz/pkg/authn" "github.com/SigNoz/signoz/pkg/authn"
"github.com/SigNoz/signoz/pkg/authz" "github.com/SigNoz/signoz/pkg/authz"
"github.com/SigNoz/signoz/pkg/errors"
"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/licensing" "github.com/SigNoz/signoz/pkg/licensing"
@@ -42,17 +38,18 @@ import (
"github.com/SigNoz/signoz/pkg/types/authtypes" "github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/version" "github.com/SigNoz/signoz/pkg/version"
"github.com/SigNoz/signoz/pkg/zeus" "github.com/SigNoz/signoz/pkg/zeus"
"github.com/spf13/cobra"
) )
func registerServer(parentCmd *cobra.Command, logger *slog.Logger) { func registerServer(parentCmd *cobra.Command, logger *slog.Logger) {
var configFiles []string var flags signoz.DeprecatedFlags
serverCmd := &cobra.Command{ serverCmd := &cobra.Command{
Use: "server", Use: "server",
Short: "Run the SigNoz server", Short: "Run the SigNoz server",
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true}, FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
RunE: func(currCmd *cobra.Command, args []string) error { RunE: func(currCmd *cobra.Command, args []string) error {
config, err := cmd.NewSigNozConfig(currCmd.Context(), logger, configFiles) config, err := cmd.NewSigNozConfig(currCmd.Context(), logger, flags)
if err != nil { if err != nil {
return err return err
} }
@@ -61,7 +58,7 @@ func registerServer(parentCmd *cobra.Command, logger *slog.Logger) {
}, },
} }
serverCmd.Flags().StringArrayVar(&configFiles, "config", nil, "path to a YAML configuration file (can be specified multiple times, later files override earlier ones)") flags.RegisterFlags(serverCmd)
parentCmd.AddCommand(serverCmd) parentCmd.AddCommand(serverCmd)
} }
@@ -72,7 +69,7 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
// add enterprise sqlstore factories to the community sqlstore factories // add enterprise sqlstore factories to the community sqlstore factories
sqlstoreFactories := signoz.NewSQLStoreProviderFactories() sqlstoreFactories := signoz.NewSQLStoreProviderFactories()
if err := sqlstoreFactories.Add(postgressqlstore.NewFactory(sqlstorehook.NewLoggingFactory(), sqlstorehook.NewInstrumentationFactory())); err != nil { if err := sqlstoreFactories.Add(postgressqlstore.NewFactory(sqlstorehook.NewLoggingFactory(), sqlstorehook.NewInstrumentationFactory())); err != nil {
logger.ErrorContext(ctx, "failed to add postgressqlstore factory", errors.Attr(err)) logger.ErrorContext(ctx, "failed to add postgressqlstore factory", "error", err)
return err return err
} }
@@ -119,13 +116,8 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
return authNs, nil return authNs, nil
}, },
func(ctx context.Context, sqlstore sqlstore.SQLStore, licensing licensing.Licensing, dashboardModule dashboard.Module) (factory.ProviderFactory[authz.AuthZ, authz.Config], error) { func(ctx context.Context, sqlstore sqlstore.SQLStore, licensing licensing.Licensing, dashboardModule dashboard.Module) factory.ProviderFactory[authz.AuthZ, authz.Config] {
openfgaDataStore, err := openfgaserver.NewSQLStore(sqlstore) return openfgaauthz.NewProviderFactory(sqlstore, openfgaschema.NewSchema().Get(ctx), licensing, dashboardModule)
if err != nil {
return nil, err
}
return openfgaauthz.NewProviderFactory(sqlstore, openfgaschema.NewSchema().Get(ctx), openfgaDataStore, licensing, dashboardModule), nil
}, },
func(store sqlstore.SQLStore, settings factory.ProviderSettings, analytics analytics.Analytics, orgGetter organization.Getter, queryParser queryparser.QueryParser, querier querier.Querier, licensing licensing.Licensing) dashboard.Module { func(store sqlstore.SQLStore, settings factory.ProviderSettings, analytics analytics.Analytics, orgGetter organization.Getter, queryParser queryparser.QueryParser, querier querier.Querier, licensing licensing.Licensing) dashboard.Module {
return impldashboard.NewModule(pkgimpldashboard.NewStore(store), settings, analytics, orgGetter, queryParser, querier, licensing) return impldashboard.NewModule(pkgimpldashboard.NewStore(store), settings, analytics, orgGetter, queryParser, querier, licensing)
@@ -140,37 +132,37 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
) )
if err != nil { if err != nil {
logger.ErrorContext(ctx, "failed to create signoz", errors.Attr(err)) logger.ErrorContext(ctx, "failed to create signoz", "error", err)
return err return err
} }
server, err := enterpriseapp.NewServer(config, signoz) server, err := enterpriseapp.NewServer(config, signoz)
if err != nil { if err != nil {
logger.ErrorContext(ctx, "failed to create server", errors.Attr(err)) logger.ErrorContext(ctx, "failed to create server", "error", err)
return err return err
} }
if err := server.Start(ctx); err != nil { if err := server.Start(ctx); err != nil {
logger.ErrorContext(ctx, "failed to start server", errors.Attr(err)) logger.ErrorContext(ctx, "failed to start server", "error", err)
return err return err
} }
signoz.Start(ctx) signoz.Start(ctx)
if err := signoz.Wait(ctx); err != nil { if err := signoz.Wait(ctx); err != nil {
logger.ErrorContext(ctx, "failed to start signoz", errors.Attr(err)) logger.ErrorContext(ctx, "failed to start signoz", "error", err)
return err return err
} }
err = server.Stop(ctx) err = server.Stop(ctx)
if err != nil { if err != nil {
logger.ErrorContext(ctx, "failed to stop server", errors.Attr(err)) logger.ErrorContext(ctx, "failed to stop server", "error", err)
return err return err
} }
err = signoz.Stop(ctx) err = signoz.Stop(ctx)
if err != nil { if err != nil {
logger.ErrorContext(ctx, "failed to stop signoz", errors.Attr(err)) logger.ErrorContext(ctx, "failed to stop signoz", "error", err)
return err return err
} }

View File

@@ -4,10 +4,9 @@ import (
"log/slog" "log/slog"
"os" "os"
"github.com/spf13/cobra"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/version" "github.com/SigNoz/signoz/pkg/version"
"github.com/spf13/cobra"
"go.uber.org/zap" //nolint:depguard
) )
var RootCmd = &cobra.Command{ var RootCmd = &cobra.Command{
@@ -20,9 +19,15 @@ 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", errors.Attr(err)) logger.ErrorContext(RootCmd.Context(), "error running command", "error", err)
os.Exit(1) os.Exit(1)
} }
} }

110
cmd/zap.go Normal file
View 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)
}

4
conf/cache-config.yml Normal file
View File

@@ -0,0 +1,4 @@
provider: "inmemory"
inmemory:
ttl: 60m
cleanupInterval: 10m

View File

@@ -39,13 +39,6 @@ instrumentation:
host: "0.0.0.0" host: "0.0.0.0"
port: 9090 port: 9090
##################### PProf #####################
pprof:
# Whether to enable the pprof server.
enabled: true
# The address on which the pprof server listens.
address: 0.0.0.0:6060
##################### Web ##################### ##################### Web #####################
web: web:
# Whether to enable the web frontend # Whether to enable the web frontend
@@ -85,12 +78,10 @@ sqlstore:
sqlite: sqlite:
# The path to the SQLite database file. # The path to the SQLite database file.
path: /var/lib/signoz/signoz.db path: /var/lib/signoz/signoz.db
# The journal mode for the sqlite database. Supported values: delete, wal. # Mode is the mode to use for the sqlite database.
mode: delete mode: delete
# The timeout for the sqlite database to wait for a lock. # BusyTimeout is the timeout for the sqlite database to wait for a lock.
busy_timeout: 10s busy_timeout: 10s
# The default transaction locking behavior. Supported values: deferred, immediate, exclusive.
transaction_mode: deferred
##################### APIServer ##################### ##################### APIServer #####################
apiserver: apiserver:
@@ -146,8 +137,6 @@ telemetrystore:
##################### Prometheus ##################### ##################### Prometheus #####################
prometheus: prometheus:
# The maximum time a PromQL query is allowed to run before being aborted.
timeout: 2m
active_query_tracker: active_query_tracker:
# Whether to enable the active query tracker. # Whether to enable the active query tracker.
enabled: true enabled: true
@@ -319,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
@@ -335,32 +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 tokenizer identN
enabled: true
# headers to use for tokenizer identN resolver
headers:
- Authorization
- Sec-WebSocket-Protocol
apikey:
# toggle apikey identN
enabled: true
# headers to use for apikey identN resolver
headers:
- SIGNOZ-API-KEY
impersonation:
# toggle impersonation identN, when enabled, all requests will impersonate the root user
enabled: false
##################### Service Account #####################
serviceaccount:
email:
# email domain for the service account principal
domain: signozserviceaccount.com
analytics:
# toggle service account analytics
enabled: true

25
conf/prometheus.yml Normal file
View File

@@ -0,0 +1,25 @@
# my global config
global:
scrape_interval: 5s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
- 127.0.0.1:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
- 'alerts.yml'
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs: []
remote_read:
- url: tcp://localhost:9000/signoz_metrics

View File

@@ -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.117.1 image: signoz/signoz:v0.115.0
ports: ports:
- "8080:8080" # signoz port - "8080:8080" # signoz port
# - "6060:6060" # pprof port # - "6060:6060" # pprof port

View File

@@ -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.117.1 image: signoz/signoz:v0.115.0
ports: ports:
- "8080:8080" # signoz port - "8080:8080" # signoz port
volumes: volumes:

View 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

View 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

View 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]

View 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]

View File

@@ -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.117.1} image: signoz/signoz:${VERSION:-v0.115.0}
container_name: signoz container_name: signoz
ports: ports:
- "8080:8080" # signoz port - "8080:8080" # signoz port

View File

@@ -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.117.1} image: signoz/signoz:${VERSION:-v0.115.0}
container_name: signoz container_name: signoz
ports: ports:
- "8080:8080" # signoz port - "8080:8080" # signoz port

View 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

View 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

View 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]

File diff suppressed because it is too large Load Diff

View File

@@ -123,7 +123,6 @@ if err := router.Handle("/api/v1/things", handler.New(
Description: "This endpoint creates a thing", Description: "This endpoint creates a thing",
Request: new(types.PostableThing), Request: new(types.PostableThing),
RequestContentType: "application/json", RequestContentType: "application/json",
RequestQuery: new(types.QueryableThing),
Response: new(types.GettableThing), Response: new(types.GettableThing),
ResponseContentType: "application/json", ResponseContentType: "application/json",
SuccessStatusCode: http.StatusCreated, SuccessStatusCode: http.StatusCreated,
@@ -156,8 +155,6 @@ The `handler.New` function ties the HTTP handler to OpenAPI metadata via `OpenAP
- **Request / RequestContentType**: - **Request / RequestContentType**:
- `Request` is a Go type that describes the request body or form. - `Request` is a Go type that describes the request body or form.
- `RequestContentType` is usually `"application/json"` or `"application/x-www-form-urlencoded"` (for callbacks like SAML). - `RequestContentType` is usually `"application/json"` or `"application/x-www-form-urlencoded"` (for callbacks like SAML).
- **RequestQuery**:
- `RequestQuery` is a Go type that descirbes query url params.
- **RequestExamples**: An array of `handler.OpenAPIExample` that provide concrete request payloads in the generated spec. See [Adding request examples](#adding-request-examples) below. - **RequestExamples**: An array of `handler.OpenAPIExample` that provide concrete request payloads in the generated spec. See [Adding request examples](#adding-request-examples) below.
- **Response / ResponseContentType**: - **Response / ResponseContentType**:
- `Response` is the Go type for the successful response payload. - `Response` is the Go type for the successful response payload.

View File

@@ -273,7 +273,6 @@ Options can be simple (direct link) or nested (with another question):
- Place logo files in `public/Logos/` - Place logo files in `public/Logos/`
- Use SVG format - Use SVG format
- Reference as `"/Logos/your-logo.svg"` - Reference as `"/Logos/your-logo.svg"`
- **Fetching Icons**: New icons can be easily fetched from [OpenBrand](https://openbrand.sh/). Use the pattern `https://openbrand.sh/?url=<TARGET_URL>`, where `<TARGET_URL>` is the URL-encoded link to the service's website. For example, to get Render's logo, use [https://openbrand.sh/?url=https%3A%2F%2Frender.com](https://openbrand.sh/?url=https%3A%2F%2Frender.com).
- **Optimize new SVGs**: Run any newly downloaded SVGs through an optimizer like [SVGOMG (svgo)](https://svgomg.net/) or use `npx svgo public/Logos/your-logo.svg` to minimise their size before committing. - **Optimize new SVGs**: Run any newly downloaded SVGs through an optimizer like [SVGOMG (svgo)](https://svgomg.net/) or use `npx svgo public/Logos/your-logo.svg` to minimise their size before committing.
### 4. Links ### 4. Links

View File

@@ -16,7 +16,7 @@ func (hp *HourlyProvider) GetBaseSeasonalProvider() *BaseSeasonalProvider {
return &hp.BaseSeasonalProvider return &hp.BaseSeasonalProvider
} }
// NewHourlyProvider now uses the generic option type. // NewHourlyProvider now uses the generic option type
func NewHourlyProvider(opts ...GenericProviderOption[*HourlyProvider]) *HourlyProvider { func NewHourlyProvider(opts ...GenericProviderOption[*HourlyProvider]) *HourlyProvider {
hp := &HourlyProvider{ hp := &HourlyProvider{
BaseSeasonalProvider: BaseSeasonalProvider{}, BaseSeasonalProvider: BaseSeasonalProvider{},

View File

@@ -47,7 +47,7 @@ type AnomaliesResponse struct {
// | | // | |
// (rounded value for past peiod) + (seasonal growth) // (rounded value for past peiod) + (seasonal growth)
// //
// score = abs(value - prediction) / stddev (current_season_query). // score = abs(value - prediction) / stddev (current_season_query)
type anomalyQueryParams struct { type anomalyQueryParams struct {
// CurrentPeriodQuery is the query range params for period user is looking at or eval window // CurrentPeriodQuery is the query range params for period user is looking at or eval window
// Example: (now-5m, now), (now-30m, now), (now-1h, now) // Example: (now-5m, now), (now-30m, now), (now-1h, now)

View File

@@ -18,12 +18,12 @@ var (
movingAvgWindowSize = 7 movingAvgWindowSize = 7
) )
// BaseProvider is an interface that includes common methods for all provider types. // BaseProvider is an interface that includes common methods for all provider types
type BaseProvider interface { type BaseProvider interface {
GetBaseSeasonalProvider() *BaseSeasonalProvider GetBaseSeasonalProvider() *BaseSeasonalProvider
} }
// GenericProviderOption is a generic type for provider options. // GenericProviderOption is a generic type for provider options
type GenericProviderOption[T BaseProvider] func(T) type GenericProviderOption[T BaseProvider] func(T)
func WithQuerier[T BaseProvider](querier querier.Querier) GenericProviderOption[T] { func WithQuerier[T BaseProvider](querier querier.Querier) GenericProviderOption[T] {
@@ -74,37 +74,37 @@ func (p *BaseSeasonalProvider) getResults(ctx context.Context, orgID valuer.UUID
instrumentationtypes.CodeFunctionName: "getResults", instrumentationtypes.CodeFunctionName: "getResults",
}) })
// TODO(srikanthccv): parallelize this? // TODO(srikanthccv): parallelize this?
p.logger.InfoContext(ctx, "fetching results for current period", slog.Any("anomaly_current_period_query", params.CurrentPeriodQuery)) p.logger.InfoContext(ctx, "fetching results for current period", "anomaly_current_period_query", params.CurrentPeriodQuery)
currentPeriodResults, err := p.querier.QueryRange(ctx, orgID, &params.CurrentPeriodQuery) currentPeriodResults, err := p.querier.QueryRange(ctx, orgID, &params.CurrentPeriodQuery)
if err != nil { if err != nil {
return nil, err return nil, err
} }
p.logger.InfoContext(ctx, "fetching results for past period", slog.Any("anomaly_past_period_query", params.PastPeriodQuery)) p.logger.InfoContext(ctx, "fetching results for past period", "anomaly_past_period_query", params.PastPeriodQuery)
pastPeriodResults, err := p.querier.QueryRange(ctx, orgID, &params.PastPeriodQuery) pastPeriodResults, err := p.querier.QueryRange(ctx, orgID, &params.PastPeriodQuery)
if err != nil { if err != nil {
return nil, err return nil, err
} }
p.logger.InfoContext(ctx, "fetching results for current season", slog.Any("anomaly_current_season_query", params.CurrentSeasonQuery)) p.logger.InfoContext(ctx, "fetching results for current season", "anomaly_current_season_query", params.CurrentSeasonQuery)
currentSeasonResults, err := p.querier.QueryRange(ctx, orgID, &params.CurrentSeasonQuery) currentSeasonResults, err := p.querier.QueryRange(ctx, orgID, &params.CurrentSeasonQuery)
if err != nil { if err != nil {
return nil, err return nil, err
} }
p.logger.InfoContext(ctx, "fetching results for past season", slog.Any("anomaly_past_season_query", params.PastSeasonQuery)) p.logger.InfoContext(ctx, "fetching results for past season", "anomaly_past_season_query", params.PastSeasonQuery)
pastSeasonResults, err := p.querier.QueryRange(ctx, orgID, &params.PastSeasonQuery) pastSeasonResults, err := p.querier.QueryRange(ctx, orgID, &params.PastSeasonQuery)
if err != nil { if err != nil {
return nil, err return nil, err
} }
p.logger.InfoContext(ctx, "fetching results for past 2 season", slog.Any("anomaly_past_2season_query", params.Past2SeasonQuery)) p.logger.InfoContext(ctx, "fetching results for past 2 season", "anomaly_past_2season_query", params.Past2SeasonQuery)
past2SeasonResults, err := p.querier.QueryRange(ctx, orgID, &params.Past2SeasonQuery) past2SeasonResults, err := p.querier.QueryRange(ctx, orgID, &params.Past2SeasonQuery)
if err != nil { if err != nil {
return nil, err return nil, err
} }
p.logger.InfoContext(ctx, "fetching results for past 3 season", slog.Any("anomaly_past_3season_query", params.Past3SeasonQuery)) p.logger.InfoContext(ctx, "fetching results for past 3 season", "anomaly_past_3season_query", params.Past3SeasonQuery)
past3SeasonResults, err := p.querier.QueryRange(ctx, orgID, &params.Past3SeasonQuery) past3SeasonResults, err := p.querier.QueryRange(ctx, orgID, &params.Past3SeasonQuery)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -121,7 +121,7 @@ func (p *BaseSeasonalProvider) getResults(ctx context.Context, orgID valuer.UUID
} }
// getMatchingSeries gets the matching series from the query result // getMatchingSeries gets the matching series from the query result
// for the given series. // for the given series
func (p *BaseSeasonalProvider) getMatchingSeries(_ context.Context, queryResult *qbtypes.TimeSeriesData, series *qbtypes.TimeSeries) *qbtypes.TimeSeries { func (p *BaseSeasonalProvider) getMatchingSeries(_ context.Context, queryResult *qbtypes.TimeSeriesData, series *qbtypes.TimeSeries) *qbtypes.TimeSeries {
if queryResult == nil || len(queryResult.Aggregations) == 0 || len(queryResult.Aggregations[0].Series) == 0 { if queryResult == nil || len(queryResult.Aggregations) == 0 || len(queryResult.Aggregations[0].Series) == 0 {
return nil return nil
@@ -155,14 +155,13 @@ func (p *BaseSeasonalProvider) getStdDev(series *qbtypes.TimeSeries) float64 {
avg := p.getAvg(series) avg := p.getAvg(series)
var sum float64 var sum float64
for _, smpl := range series.Values { for _, smpl := range series.Values {
d := smpl.Value - avg sum += math.Pow(smpl.Value-avg, 2)
sum += d * d
} }
return math.Sqrt(sum / float64(len(series.Values))) return math.Sqrt(sum / float64(len(series.Values)))
} }
// getMovingAvg gets the moving average for the given series // getMovingAvg gets the moving average for the given series
// for the given window size and start index. // for the given window size and start index
func (p *BaseSeasonalProvider) getMovingAvg(series *qbtypes.TimeSeries, movingAvgWindowSize, startIdx int) float64 { func (p *BaseSeasonalProvider) getMovingAvg(series *qbtypes.TimeSeries, movingAvgWindowSize, startIdx int) float64 {
if series == nil || len(series.Values) == 0 { if series == nil || len(series.Values) == 0 {
return 0 return 0
@@ -213,17 +212,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
p.logger.WarnContext(ctx, "predicted value is less than 0 for series", slog.Float64("anomaly_predicted_value", predictedValue), slog.Any("anomaly_labels", series.Labels)) p.logger.WarnContext(ctx, "predicted value is less than 0 for series", "anomaly_predicted_value", predictedValue, "anomaly_labels", series.Labels)
predictedValue = p.getMovingAvg(prevSeries, movingAvgWindowSize, idx) predictedValue = p.getMovingAvg(prevSeries, movingAvgWindowSize, idx)
} }
p.logger.DebugContext(ctx, "predicted value for series", p.logger.DebugContext(ctx, "predicted value for series",
slog.Float64("anomaly_moving_avg", movingAvg), "anomaly_moving_avg", movingAvg,
slog.Float64("anomaly_avg", avg), "anomaly_avg", avg,
slog.Float64("anomaly_mean", mean), "anomaly_mean", mean,
slog.Any("anomaly_labels", series.Labels), "anomaly_labels", series.Labels,
slog.Float64("anomaly_predicted_value", predictedValue), "anomaly_predicted_value", predictedValue,
slog.Float64("anomaly_curr", curr.Value), "anomaly_curr", curr.Value,
) )
predictedSeries.Values = append(predictedSeries.Values, &qbtypes.TimeSeriesValue{ predictedSeries.Values = append(predictedSeries.Values, &qbtypes.TimeSeriesValue{
Timestamp: curr.Timestamp, Timestamp: curr.Timestamp,
@@ -237,7 +236,7 @@ func (p *BaseSeasonalProvider) getPredictedSeries(
// getBounds gets the upper and lower bounds for the given series // getBounds gets the upper and lower bounds for the given series
// for the given z score threshold // for the given z score threshold
// moving avg of the previous period series + z score threshold * std dev of the series // moving avg of the previous period series + z score threshold * std dev of the series
// moving avg of the previous period series - z score threshold * std dev of the series. // moving avg of the previous period series - z score threshold * std dev of the series
func (p *BaseSeasonalProvider) getBounds( func (p *BaseSeasonalProvider) getBounds(
series, predictedSeries, weekSeries *qbtypes.TimeSeries, series, predictedSeries, weekSeries *qbtypes.TimeSeries,
zScoreThreshold float64, zScoreThreshold float64,
@@ -270,7 +269,7 @@ func (p *BaseSeasonalProvider) getBounds(
// getExpectedValue gets the expected value for the given series // getExpectedValue gets the expected value for the given series
// for the given index // for the given index
// prevSeriesAvg + currentSeasonSeriesAvg - mean of past season series, past2 season series and past3 season series. // prevSeriesAvg + currentSeasonSeriesAvg - mean of past season series, past2 season series and past3 season series
func (p *BaseSeasonalProvider) getExpectedValue( func (p *BaseSeasonalProvider) getExpectedValue(
_, prevSeries, currentSeasonSeries, pastSeasonSeries, past2SeasonSeries, past3SeasonSeries *qbtypes.TimeSeries, idx int, _, prevSeries, currentSeasonSeries, pastSeasonSeries, past2SeasonSeries, past3SeasonSeries *qbtypes.TimeSeries, idx int,
) float64 { ) float64 {
@@ -284,7 +283,7 @@ func (p *BaseSeasonalProvider) getExpectedValue(
// getScore gets the anomaly score for the given series // getScore gets the anomaly score for the given series
// for the given index // for the given index
// (value - expectedValue) / std dev of the series. // (value - expectedValue) / std dev of the series
func (p *BaseSeasonalProvider) getScore( func (p *BaseSeasonalProvider) getScore(
series, prevSeries, weekSeries, weekPrevSeries, past2SeasonSeries, past3SeasonSeries *qbtypes.TimeSeries, value float64, idx int, series, prevSeries, weekSeries, weekPrevSeries, past2SeasonSeries, past3SeasonSeries *qbtypes.TimeSeries, value float64, idx int,
) float64 { ) float64 {
@@ -297,7 +296,7 @@ func (p *BaseSeasonalProvider) getScore(
// getAnomalyScores gets the anomaly scores for the given series // getAnomalyScores gets the anomaly scores for the given series
// for the given index // for the given index
// (value - expectedValue) / std dev of the series. // (value - expectedValue) / std dev of the series
func (p *BaseSeasonalProvider) getAnomalyScores( func (p *BaseSeasonalProvider) getAnomalyScores(
series, prevSeries, currentSeasonSeries, pastSeasonSeries, past2SeasonSeries, past3SeasonSeries *qbtypes.TimeSeries, series, prevSeries, currentSeasonSeries, pastSeasonSeries, past2SeasonSeries, past3SeasonSeries *qbtypes.TimeSeries,
) *qbtypes.TimeSeries { ) *qbtypes.TimeSeries {
@@ -413,7 +412,7 @@ func (p *BaseSeasonalProvider) getAnomalies(ctx context.Context, orgID valuer.UU
past3SeasonSeries := p.getMatchingSeries(ctx, past3SeasonResult, series) past3SeasonSeries := p.getMatchingSeries(ctx, past3SeasonResult, series)
stdDev := p.getStdDev(currentSeasonSeries) stdDev := p.getStdDev(currentSeasonSeries)
p.logger.InfoContext(ctx, "calculated standard deviation for series", slog.Float64("anomaly_std_dev", stdDev), slog.Any("anomaly_labels", series.Labels)) p.logger.InfoContext(ctx, "calculated standard deviation for series", "anomaly_std_dev", stdDev, "anomaly_labels", series.Labels)
prevSeriesAvg := p.getAvg(pastPeriodSeries) prevSeriesAvg := p.getAvg(pastPeriodSeries)
currentSeasonSeriesAvg := p.getAvg(currentSeasonSeries) currentSeasonSeriesAvg := p.getAvg(currentSeasonSeries)
@@ -421,12 +420,12 @@ func (p *BaseSeasonalProvider) getAnomalies(ctx context.Context, orgID valuer.UU
past2SeasonSeriesAvg := p.getAvg(past2SeasonSeries) past2SeasonSeriesAvg := p.getAvg(past2SeasonSeries)
past3SeasonSeriesAvg := p.getAvg(past3SeasonSeries) past3SeasonSeriesAvg := p.getAvg(past3SeasonSeries)
p.logger.InfoContext(ctx, "calculated mean for series", p.logger.InfoContext(ctx, "calculated mean for series",
slog.Float64("anomaly_prev_series_avg", prevSeriesAvg), "anomaly_prev_series_avg", prevSeriesAvg,
slog.Float64("anomaly_current_season_series_avg", currentSeasonSeriesAvg), "anomaly_current_season_series_avg", currentSeasonSeriesAvg,
slog.Float64("anomaly_past_season_series_avg", pastSeasonSeriesAvg), "anomaly_past_season_series_avg", pastSeasonSeriesAvg,
slog.Float64("anomaly_past_2season_series_avg", past2SeasonSeriesAvg), "anomaly_past_2season_series_avg", past2SeasonSeriesAvg,
slog.Float64("anomaly_past_3season_series_avg", past3SeasonSeriesAvg), "anomaly_past_3season_series_avg", past3SeasonSeriesAvg,
slog.Any("anomaly_labels", series.Labels), "anomaly_labels", series.Labels,
) )
predictedSeries := p.getPredictedSeries( predictedSeries := p.getPredictedSeries(

View File

@@ -1,143 +0,0 @@
package otlphttpauditor
import (
"bytes"
"context"
"io"
"log/slog"
"net/http"
"github.com/SigNoz/signoz/pkg/auditor"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/types/audittypes"
collogspb "go.opentelemetry.io/proto/otlp/collector/logs/v1"
"google.golang.org/protobuf/proto"
spb "google.golang.org/genproto/googleapis/rpc/status"
)
const (
maxHTTPResponseReadBytes int64 = 64 * 1024
protobufContentType string = "application/x-protobuf"
)
func (provider *provider) export(ctx context.Context, events []audittypes.AuditEvent) error {
logs := audittypes.NewPLogsFromAuditEvents(events, "signoz", provider.build.Version(), "signoz.audit")
request, err := provider.marshaler.MarshalLogs(logs)
if err != nil {
return errors.Wrapf(err, errors.TypeInternal, auditor.ErrCodeAuditExportFailed, "failed to marshal audit logs")
}
if err := provider.send(ctx, request); err != nil {
provider.settings.Logger().ErrorContext(ctx, "audit export failed", errors.Attr(err), slog.Int("dropped_log_records", len(events)))
return err
}
return nil
}
// Posts a protobuf-encoded OTLP request to the configured endpoint.
// Retries are handled by the underlying heimdall HTTP client.
// Ref: https://github.com/open-telemetry/opentelemetry-collector/blob/main/exporter/otlphttpexporter/otlp.go
func (provider *provider) send(ctx context.Context, body []byte) error {
req, err := http.NewRequestWithContext(ctx, http.MethodPost, provider.config.OTLPHTTP.Endpoint.String(), bytes.NewReader(body))
if err != nil {
return err
}
req.Header.Set("Content-Type", protobufContentType)
res, err := provider.httpClient.Do(req)
if err != nil {
return err
}
defer func() {
_, _ = io.CopyN(io.Discard, res.Body, maxHTTPResponseReadBytes)
res.Body.Close()
}()
if res.StatusCode >= 200 && res.StatusCode <= 299 {
provider.onSuccess(ctx, res)
return nil
}
return provider.onErr(res)
}
// Ref: https://github.com/open-telemetry/opentelemetry-collector/blob/01b07fcbb7a253bd996c290dcae6166e71d13732/exporter/otlphttpexporter/otlp.go#L403.
func (provider *provider) onSuccess(ctx context.Context, res *http.Response) {
resBytes, err := readResponseBody(res)
if err != nil || resBytes == nil {
return
}
exportResponse := &collogspb.ExportLogsServiceResponse{}
if err := proto.Unmarshal(resBytes, exportResponse); err != nil {
return
}
ps := exportResponse.GetPartialSuccess()
if ps == nil {
return
}
if ps.GetErrorMessage() != "" || ps.GetRejectedLogRecords() != 0 {
provider.settings.Logger().WarnContext(ctx, "partial success response", slog.String("message", ps.GetErrorMessage()), slog.Int64("dropped_log_records", ps.GetRejectedLogRecords()))
}
}
func (provider *provider) onErr(res *http.Response) error {
status := readResponseStatus(res)
if status != nil {
return errors.Newf(errors.TypeInternal, auditor.ErrCodeAuditExportFailed, "request to %s responded with status code %d, Message=%s, Details=%v", provider.config.OTLPHTTP.Endpoint.String(), res.StatusCode, status.Message, status.Details)
}
return errors.Newf(errors.TypeInternal, auditor.ErrCodeAuditExportFailed, "request to %s responded with status code %d", provider.config.OTLPHTTP.Endpoint.String(), res.StatusCode)
}
// Reads at most maxHTTPResponseReadBytes from the response body.
// Ref: https://github.com/open-telemetry/opentelemetry-collector/blob/01b07fcbb7a253bd996c290dcae6166e71d13732/exporter/otlphttpexporter/otlp.go#L275.
func readResponseBody(resp *http.Response) ([]byte, error) {
if resp.ContentLength == 0 {
return nil, nil
}
maxRead := resp.ContentLength
if maxRead == -1 || maxRead > maxHTTPResponseReadBytes {
maxRead = maxHTTPResponseReadBytes
}
protoBytes := make([]byte, maxRead)
n, err := io.ReadFull(resp.Body, protoBytes)
if n == 0 && (err == nil || errors.Is(err, io.EOF)) {
return nil, nil
}
if err != nil && !errors.Is(err, io.ErrUnexpectedEOF) {
return nil, err
}
return protoBytes[:n], nil
}
// Decodes a protobuf-encoded Status from 4xx/5xx response bodies. Returns nil if the response is empty or cannot be decoded.
// Ref: https://github.com/open-telemetry/opentelemetry-collector/blob/01b07fcbb7a253bd996c290dcae6166e71d13732/exporter/otlphttpexporter/otlp.go#L310.
func readResponseStatus(resp *http.Response) *spb.Status {
if resp.StatusCode < 400 || resp.StatusCode > 599 {
return nil
}
respBytes, err := readResponseBody(resp)
if err != nil || respBytes == nil {
return nil
}
respStatus := &spb.Status{}
if err := proto.Unmarshal(respBytes, respStatus); err != nil {
return nil
}
return respStatus
}

View File

@@ -1,97 +0,0 @@
package otlphttpauditor
import (
"context"
"github.com/SigNoz/signoz/pkg/auditor"
"github.com/SigNoz/signoz/pkg/auditor/auditorserver"
"github.com/SigNoz/signoz/pkg/factory"
client "github.com/SigNoz/signoz/pkg/http/client"
"github.com/SigNoz/signoz/pkg/licensing"
"github.com/SigNoz/signoz/pkg/types/audittypes"
"github.com/SigNoz/signoz/pkg/version"
"go.opentelemetry.io/collector/pdata/plog"
)
var _ auditor.Auditor = (*provider)(nil)
type provider struct {
settings factory.ScopedProviderSettings
config auditor.Config
licensing licensing.Licensing
build version.Build
server *auditorserver.Server
marshaler plog.ProtoMarshaler
httpClient *client.Client
}
func NewFactory(licensing licensing.Licensing, build version.Build) factory.ProviderFactory[auditor.Auditor, auditor.Config] {
return factory.NewProviderFactory(factory.MustNewName("otlphttp"), func(ctx context.Context, providerSettings factory.ProviderSettings, config auditor.Config) (auditor.Auditor, error) {
return newProvider(ctx, providerSettings, config, licensing, build)
})
}
func newProvider(_ context.Context, providerSettings factory.ProviderSettings, config auditor.Config, licensing licensing.Licensing, build version.Build) (auditor.Auditor, error) {
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/ee/auditor/otlphttpauditor")
httpClient, err := client.New(
settings.Logger(),
providerSettings.TracerProvider,
providerSettings.MeterProvider,
client.WithTimeout(config.OTLPHTTP.Timeout),
client.WithRetryCount(retryCountFromConfig(config.OTLPHTTP.Retry)),
retrierOption(config.OTLPHTTP.Retry),
)
if err != nil {
return nil, err
}
provider := &provider{
settings: settings,
config: config,
licensing: licensing,
build: build,
marshaler: plog.ProtoMarshaler{},
httpClient: httpClient,
}
server, err := auditorserver.New(settings,
auditorserver.Config{
BufferSize: config.BufferSize,
BatchSize: config.BatchSize,
FlushInterval: config.FlushInterval,
},
provider.export,
)
if err != nil {
return nil, err
}
provider.server = server
return provider, nil
}
func (provider *provider) Start(ctx context.Context) error {
return provider.server.Start(ctx)
}
func (provider *provider) Audit(ctx context.Context, event audittypes.AuditEvent) {
if event.PrincipalOrgID.IsZero() {
provider.settings.Logger().WarnContext(ctx, "audit event dropped as org_id is zero")
return
}
if _, err := provider.licensing.GetActive(ctx, event.PrincipalOrgID); err != nil {
return
}
provider.server.Add(ctx, event)
}
func (provider *provider) Healthy() <-chan struct{} {
return provider.server.Healthy()
}
func (provider *provider) Stop(ctx context.Context) error {
return provider.server.Stop(ctx)
}

View File

@@ -1,52 +0,0 @@
package otlphttpauditor
import (
"time"
"github.com/SigNoz/signoz/pkg/auditor"
client "github.com/SigNoz/signoz/pkg/http/client"
)
// retrier implements client.Retriable with exponential backoff
// derived from auditor.RetryConfig.
type retrier struct {
initialInterval time.Duration
maxInterval time.Duration
}
func newRetrier(cfg auditor.RetryConfig) *retrier {
return &retrier{
initialInterval: cfg.InitialInterval,
maxInterval: cfg.MaxInterval,
}
}
// NextInterval returns the backoff duration for the given retry attempt.
// Uses exponential backoff: initialInterval * 2^retry, capped at maxInterval.
func (r *retrier) NextInterval(retry int) time.Duration {
interval := r.initialInterval
for range retry {
interval *= 2
}
return min(interval, r.maxInterval)
}
func retrierOption(cfg auditor.RetryConfig) client.Option {
return client.WithRetriable(newRetrier(cfg))
}
func retryCountFromConfig(cfg auditor.RetryConfig) int {
if !cfg.Enabled || cfg.MaxElapsedTime <= 0 {
return 0
}
count := 0
elapsed := time.Duration(0)
interval := cfg.InitialInterval
for elapsed < cfg.MaxElapsedTime {
elapsed += interval
interval = min(interval*2, cfg.MaxInterval)
count++
}
return count
}

View File

@@ -3,7 +3,6 @@ package oidccallbackauthn
import ( import (
"context" "context"
"fmt" "fmt"
"log/slog"
"net/url" "net/url"
"github.com/SigNoz/signoz/pkg/authn" "github.com/SigNoz/signoz/pkg/authn"
@@ -151,7 +150,7 @@ func (a *AuthN) HandleCallback(ctx context.Context, query url.Values) (*authtype
// Some IDPs return a single group as a string instead of an array // Some IDPs return a single group as a string instead of an array
groups = append(groups, g) groups = append(groups, g)
default: default:
a.settings.Logger().WarnContext(ctx, "oidc: unsupported groups type", slog.String("type", fmt.Sprintf("%T", claimValue))) a.settings.Logger().WarnContext(ctx, "oidc: unsupported groups type", "type", fmt.Sprintf("%T", claimValue))
} }
} }
} }

View File

@@ -13,28 +13,28 @@ 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"
"github.com/openfga/openfga/pkg/storage"
) )
type provider struct { 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
} }
func NewProviderFactory(sqlstore sqlstore.SQLStore, openfgaSchema []openfgapkgtransformer.ModuleFile, openfgaDataStore storage.OpenFGADatastore, licensing licensing.Licensing, registry ...authz.RegisterTypeable) factory.ProviderFactory[authz.AuthZ, authz.Config] { func NewProviderFactory(sqlstore sqlstore.SQLStore, openfgaSchema []openfgapkgtransformer.ModuleFile, licensing licensing.Licensing, registry ...authz.RegisterTypeable) factory.ProviderFactory[authz.AuthZ, authz.Config] {
return factory.NewProviderFactory(factory.MustNewName("openfga"), func(ctx context.Context, ps factory.ProviderSettings, config authz.Config) (authz.AuthZ, error) { return factory.NewProviderFactory(factory.MustNewName("openfga"), func(ctx context.Context, ps factory.ProviderSettings, config authz.Config) (authz.AuthZ, error) {
return newOpenfgaProvider(ctx, ps, config, sqlstore, openfgaSchema, openfgaDataStore, licensing, registry) return newOpenfgaProvider(ctx, ps, config, sqlstore, openfgaSchema, licensing, registry)
}) })
} }
func newOpenfgaProvider(ctx context.Context, settings factory.ProviderSettings, config authz.Config, sqlstore sqlstore.SQLStore, openfgaSchema []openfgapkgtransformer.ModuleFile, openfgaDataStore storage.OpenFGADatastore, licensing licensing.Licensing, registry []authz.RegisterTypeable) (authz.AuthZ, error) { func newOpenfgaProvider(ctx context.Context, settings factory.ProviderSettings, config authz.Config, sqlstore sqlstore.SQLStore, openfgaSchema []openfgapkgtransformer.ModuleFile, licensing licensing.Licensing, registry []authz.RegisterTypeable) (authz.AuthZ, error) {
pkgOpenfgaAuthzProvider := pkgopenfgaauthz.NewProviderFactory(sqlstore, openfgaSchema, openfgaDataStore) pkgOpenfgaAuthzProvider := pkgopenfgaauthz.NewProviderFactory(sqlstore, openfgaSchema)
pkgAuthzService, err := pkgOpenfgaAuthzProvider.New(ctx, settings, config) pkgAuthzService, err := pkgOpenfgaAuthzProvider.New(ctx, settings, config)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -58,10 +58,6 @@ func (provider *provider) Start(ctx context.Context) error {
return provider.openfgaServer.Start(ctx) return provider.openfgaServer.Start(ctx)
} }
func (provider *provider) Healthy() <-chan struct{} {
return provider.openfgaServer.Healthy()
}
func (provider *provider) Stop(ctx context.Context) error { func (provider *provider) Stop(ctx context.Context) error {
return provider.openfgaServer.Stop(ctx) return provider.openfgaServer.Stop(ctx)
} }
@@ -86,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)
} }
@@ -118,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)
} }
@@ -140,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())
@@ -163,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
} }
@@ -221,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 {
@@ -236,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
} }
@@ -265,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
@@ -275,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) {
@@ -287,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,
) )
@@ -302,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,
) )

View File

@@ -16,6 +16,7 @@ type Server struct {
} }
func NewOpenfgaServer(ctx context.Context, pkgAuthzService authz.AuthZ) (*Server, error) { func NewOpenfgaServer(ctx context.Context, pkgAuthzService authz.AuthZ) (*Server, error) {
return &Server{ return &Server{
pkgAuthzService: pkgAuthzService, pkgAuthzService: pkgAuthzService,
}, nil }, nil
@@ -25,31 +26,14 @@ func (server *Server) Start(ctx context.Context) error {
return server.pkgAuthzService.Start(ctx) return server.pkgAuthzService.Start(ctx)
} }
func (server *Server) Healthy() <-chan struct{} {
return server.pkgAuthzService.Healthy()
}
func (server *Server) Stop(ctx context.Context) error { func (server *Server) Stop(ctx context.Context) error {
return server.pkgAuthzService.Stop(ctx) return server.pkgAuthzService.Stop(ctx)
} }
func (server *Server) CheckWithTupleCreation(ctx context.Context, claims authtypes.Claims, orgID valuer.UUID, relation authtypes.Relation, typeable authtypes.Typeable, selectors []authtypes.Selector, _ []authtypes.Selector) error { func (server *Server) CheckWithTupleCreation(ctx context.Context, claims authtypes.Claims, orgID valuer.UUID, relation authtypes.Relation, typeable authtypes.Typeable, selectors []authtypes.Selector, _ []authtypes.Selector) error {
subject := "" subject, err := authtypes.NewSubject(authtypes.TypeableUser, claims.UserID, orgID, nil)
switch claims.Principal { if err != nil {
case authtypes.PrincipalUser: return err
user, err := authtypes.NewSubject(authtypes.TypeableUser, claims.UserID, orgID, nil)
if err != nil {
return err
}
subject = user
case authtypes.PrincipalServiceAccount:
serviceAccount, err := authtypes.NewSubject(authtypes.TypeableServiceAccount, claims.ServiceAccountID, orgID, nil)
if err != nil {
return err
}
subject = serviceAccount
} }
tupleSlice, err := typeable.Tuples(subject, relation, selectors, orgID) tupleSlice, err := typeable.Tuples(subject, relation, selectors, orgID)

View File

@@ -1,32 +0,0 @@
package openfgaserver
import (
"github.com/SigNoz/signoz/ee/sqlstore/postgressqlstore"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/openfga/openfga/pkg/storage"
"github.com/openfga/openfga/pkg/storage/postgres"
"github.com/openfga/openfga/pkg/storage/sqlcommon"
"github.com/openfga/openfga/pkg/storage/sqlite"
)
func NewSQLStore(store sqlstore.SQLStore) (storage.OpenFGADatastore, error) {
switch store.BunDB().Dialect().Name().String() {
case "sqlite":
return sqlite.NewWithDB(store.SQLDB(), &sqlcommon.Config{
MaxTuplesPerWriteField: 100,
MaxTypesPerModelField: 100,
})
case "pg":
pgStore, ok := store.(postgressqlstore.Pooler)
if !ok {
panic(errors.New(errors.TypeInternal, errors.CodeInternal, "postgressqlstore should implement Pooler"))
}
return postgres.NewWithDB(pgStore.Pool(), nil, &sqlcommon.Config{
MaxTuplesPerWriteField: 100,
MaxTypesPerModelField: 100,
})
}
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "invalid store type: %s", store.BunDB().Dialect().Name().String())
}

View File

@@ -13,7 +13,7 @@ var (
once sync.Once once sync.Once
) )
// Config initializes the licensing configuration. // initializes the licensing configuration
func Config(pollInterval time.Duration, failureThreshold int) licensing.Config { func Config(pollInterval time.Duration, failureThreshold int) licensing.Config {
once.Do(func() { once.Do(func() {
config = licensing.Config{PollInterval: pollInterval, FailureThreshold: failureThreshold} config = licensing.Config{PollInterval: pollInterval, FailureThreshold: failureThreshold}

View File

@@ -3,11 +3,8 @@ package httplicensing
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"log/slog"
"time" "time"
"github.com/tidwall/gjson"
"github.com/SigNoz/signoz/ee/licensing/licensingstore/sqllicensingstore" "github.com/SigNoz/signoz/ee/licensing/licensingstore/sqllicensingstore"
"github.com/SigNoz/signoz/pkg/analytics" "github.com/SigNoz/signoz/pkg/analytics"
"github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/errors"
@@ -19,6 +16,7 @@ import (
"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"
"github.com/SigNoz/signoz/pkg/zeus" "github.com/SigNoz/signoz/pkg/zeus"
"github.com/tidwall/gjson"
) )
type provider struct { type provider struct {
@@ -57,7 +55,7 @@ func (provider *provider) Start(ctx context.Context) error {
err := provider.Validate(ctx) err := provider.Validate(ctx)
if err != nil { if err != nil {
provider.settings.Logger().ErrorContext(ctx, "failed to validate license from upstream server", errors.Attr(err)) provider.settings.Logger().ErrorContext(ctx, "failed to validate license from upstream server", "error", err)
} }
for { for {
@@ -67,7 +65,7 @@ func (provider *provider) Start(ctx context.Context) error {
case <-tick.C: case <-tick.C:
err := provider.Validate(ctx) err := provider.Validate(ctx)
if err != nil { if err != nil {
provider.settings.Logger().ErrorContext(ctx, "failed to validate license from upstream server", errors.Attr(err)) provider.settings.Logger().ErrorContext(ctx, "failed to validate license from upstream server", "error", err)
} }
} }
} }
@@ -135,7 +133,7 @@ func (provider *provider) Refresh(ctx context.Context, organizationID valuer.UUI
if errors.Ast(err, errors.TypeNotFound) { if errors.Ast(err, errors.TypeNotFound) {
return nil return nil
} }
provider.settings.Logger().ErrorContext(ctx, "license validation failed", slog.String("org_id", organizationID.StringValue())) provider.settings.Logger().ErrorContext(ctx, "license validation failed", "org_id", organizationID.StringValue())
return err return err
} }
@@ -200,10 +198,7 @@ func (provider *provider) Checkout(ctx context.Context, organizationID valuer.UU
response, err := provider.zeus.GetCheckoutURL(ctx, activeLicense.Key, body) response, err := provider.zeus.GetCheckoutURL(ctx, activeLicense.Key, body)
if err != nil { if err != nil {
if errors.Ast(err, errors.TypeAlreadyExists) { return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to generate checkout session")
return nil, errors.WithAdditionalf(err, "checkout has already been completed for this account. Please click 'Refresh Status' to sync your subscription")
}
return nil, err
} }
return &licensetypes.GettableSubscription{RedirectURL: gjson.GetBytes(response, "url").String()}, nil return &licensetypes.GettableSubscription{RedirectURL: gjson.GetBytes(response, "url").String()}, nil
@@ -222,7 +217,7 @@ func (provider *provider) Portal(ctx context.Context, organizationID valuer.UUID
response, err := provider.zeus.GetPortalURL(ctx, activeLicense.Key, body) response, err := provider.zeus.GetPortalURL(ctx, activeLicense.Key, body)
if err != nil { if err != nil {
return nil, err return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to generate portal session")
} }
return &licensetypes.GettableSubscription{RedirectURL: gjson.GetBytes(response, "url").String()}, nil return &licensetypes.GettableSubscription{RedirectURL: gjson.GetBytes(response, "url").String()}, nil

View File

@@ -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"
) )
@@ -213,8 +214,8 @@ func (module *module) Update(ctx context.Context, orgID valuer.UUID, id valuer.U
return module.pkgDashboardModule.Update(ctx, orgID, id, updatedBy, data, diff) return module.pkgDashboardModule.Update(ctx, orgID, id, updatedBy, data, diff)
} }
func (module *module) LockUnlock(ctx context.Context, orgID valuer.UUID, id valuer.UUID, updatedBy string, isAdmin bool, lock bool) error { func (module *module) LockUnlock(ctx context.Context, orgID valuer.UUID, id valuer.UUID, updatedBy string, role types.Role, lock bool) error {
return module.pkgDashboardModule.LockUnlock(ctx, orgID, id, updatedBy, isAdmin, lock) return module.pkgDashboardModule.LockUnlock(ctx, orgID, id, updatedBy, role, lock)
} }
func (module *module) MustGetTypeables() []authtypes.Typeable { func (module *module) MustGetTypeables() []authtypes.Typeable {
@@ -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,

View File

@@ -6,6 +6,7 @@ import (
"encoding/json" "encoding/json"
"io" "io"
"net/http" "net/http"
"runtime/debug"
anomalyV2 "github.com/SigNoz/signoz/ee/anomaly" anomalyV2 "github.com/SigNoz/signoz/ee/anomaly"
"github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/errors"
@@ -53,6 +54,26 @@ func (h *handler) QueryRange(rw http.ResponseWriter, req *http.Request) {
return return
} }
defer func() {
if r := recover(); r != nil {
stackTrace := string(debug.Stack())
queryJSON, _ := json.Marshal(queryRangeRequest)
h.set.Logger.ErrorContext(ctx, "panic in QueryRange",
"error", r,
"user", claims.UserID,
"payload", string(queryJSON),
"stacktrace", stackTrace,
)
render.Error(rw, errors.NewInternalf(
errors.CodeInternal,
"Something went wrong on our end. It's not you, it's us. Our team is notified about it. Reach out to support if issue persists.",
))
}
}()
if err := queryRangeRequest.Validate(); err != nil { if err := queryRangeRequest.Validate(); err != nil {
render.Error(rw, err) render.Error(rw, err)
return return
@@ -79,7 +100,7 @@ func (h *handler) QueryRange(rw http.ResponseWriter, req *http.Request) {
// Build step intervals from the anomaly query // Build step intervals from the anomaly query
stepIntervals := make(map[string]uint64) stepIntervals := make(map[string]uint64)
if anomalyQuery.StepInterval.Duration > 0 { if anomalyQuery.StepInterval.Duration > 0 {
stepIntervals[anomalyQuery.Name] = uint64(anomalyQuery.StepInterval.Seconds()) stepIntervals[anomalyQuery.Name] = uint64(anomalyQuery.StepInterval.Duration.Seconds())
} }
finalResp := &qbtypes.QueryRangeResponse{ finalResp := &qbtypes.QueryRangeResponse{

View File

@@ -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,

View File

@@ -10,15 +10,15 @@ import (
"strings" "strings"
"time" "time"
"log/slog"
"github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/http/render" "github.com/SigNoz/signoz/pkg/http/render"
"github.com/SigNoz/signoz/pkg/modules/user"
basemodel "github.com/SigNoz/signoz/pkg/query-service/model" basemodel "github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/authtypes" "github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/types/serviceaccounttypes"
"github.com/SigNoz/signoz/pkg/valuer" "github.com/SigNoz/signoz/pkg/valuer"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"go.uber.org/zap"
) )
type CloudIntegrationConnectionParamsResponse struct { type CloudIntegrationConnectionParamsResponse struct {
@@ -49,7 +49,7 @@ func (ah *APIHandler) CloudIntegrationsGenerateConnectionParams(w http.ResponseW
return return
} }
apiKey, apiErr := ah.getOrCreateCloudIntegrationFactorAPIKey(r.Context(), valuer.MustNewUUID(claims.OrgID), cloudProvider) apiKey, apiErr := ah.getOrCreateCloudIntegrationPAT(r.Context(), claims.OrgID, cloudProvider)
if apiErr != nil { if apiErr != nil {
RespondError(w, basemodel.WrapApiError( RespondError(w, basemodel.WrapApiError(
apiErr, "couldn't provision PAT for cloud integration:", apiErr, "couldn't provision PAT for cloud integration:",
@@ -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,50 +103,86 @@ 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)
} }
func (ah *APIHandler) getOrCreateCloudIntegrationFactorAPIKey(ctx context.Context, orgID valuer.UUID, cloudProvider string) ( func (ah *APIHandler) getOrCreateCloudIntegrationPAT(ctx context.Context, orgId string, cloudProvider string) (
string, *basemodel.ApiError, string, *basemodel.ApiError,
) { ) {
integrationPATName := fmt.Sprintf("%s", cloudProvider) integrationPATName := fmt.Sprintf("%s integration", cloudProvider)
serviceAccount, apiErr := ah.getOrCreateCloudIntegrationServiceAccount(ctx, orgID)
integrationUser, apiErr := ah.getOrCreateCloudIntegrationUser(ctx, orgId, cloudProvider)
if apiErr != nil { if apiErr != nil {
return "", apiErr return "", apiErr
} }
factorAPIKey, err := serviceAccount.NewFactorAPIKey(integrationPATName, 0) orgIdUUID, err := valuer.NewUUID(orgId)
if err != nil {
return "", basemodel.InternalError(fmt.Errorf(
"couldn't parse orgId: %w", err,
))
}
allPats, err := ah.Signoz.Modules.User.ListAPIKeys(ctx, orgIdUUID)
if err != nil {
return "", basemodel.InternalError(fmt.Errorf(
"couldn't list PATs: %w", err,
))
}
for _, p := range allPats {
if p.UserID == integrationUser.ID && p.Name == integrationPATName {
return p.Token, nil
}
}
zap.L().Info(
"no PAT found for cloud integration, creating a new one",
zap.String("cloudProvider", cloudProvider),
)
newPAT, err := types.NewStorableAPIKey(
integrationPATName,
integrationUser.ID,
types.RoleViewer,
0,
)
if err != nil { if err != nil {
return "", basemodel.InternalError(fmt.Errorf( return "", basemodel.InternalError(fmt.Errorf(
"couldn't create cloud integration PAT: %w", err, "couldn't create cloud integration PAT: %w", err,
)) ))
} }
factorAPIKey, err = ah.Signoz.Modules.ServiceAccount.GetOrCreateFactorAPIKey(ctx, factorAPIKey) err = ah.Signoz.Modules.User.CreateAPIKey(ctx, newPAT)
if err != nil { if err != nil {
return "", basemodel.InternalError(fmt.Errorf( return "", basemodel.InternalError(fmt.Errorf(
"couldn't create cloud integration PAT: %w", err, "couldn't create cloud integration PAT: %w", err,
)) ))
} }
return factorAPIKey.Key, nil return newPAT.Token, nil
} }
func (ah *APIHandler) getOrCreateCloudIntegrationServiceAccount(ctx context.Context, orgId valuer.UUID) (*serviceaccounttypes.ServiceAccount, *basemodel.ApiError) { func (ah *APIHandler) getOrCreateCloudIntegrationUser(
domain := ah.Signoz.Modules.ServiceAccount.Config().Email.Domain ctx context.Context, orgId string, cloudProvider string,
cloudIntegrationServiceAccount := serviceaccounttypes.NewServiceAccount("integration", domain, serviceaccounttypes.ServiceAccountStatusActive, orgId) ) (*types.User, *basemodel.ApiError) {
cloudIntegrationServiceAccount, err := ah.Signoz.Modules.ServiceAccount.GetOrCreate(ctx, orgId, cloudIntegrationServiceAccount) cloudIntegrationUserName := fmt.Sprintf("%s-integration", cloudProvider)
email := valuer.MustNewEmail(fmt.Sprintf("%s@signoz.io", cloudIntegrationUserName))
cloudIntegrationUser, err := types.NewUser(cloudIntegrationUserName, email, types.RoleViewer, valuer.MustNewUUID(orgId), types.UserStatusActive)
if err != nil { if err != nil {
return nil, basemodel.InternalError(fmt.Errorf("couldn't create cloud integration service account: %w", err)) return nil, basemodel.InternalError(fmt.Errorf("couldn't create cloud integration user: %w", err))
}
err = ah.Signoz.Modules.ServiceAccount.SetRoleByName(ctx, orgId, cloudIntegrationServiceAccount.ID, authtypes.SigNozViewerRoleName)
if err != nil {
return nil, basemodel.InternalError(fmt.Errorf("couldn't create cloud integration service account: %w", err))
} }
return cloudIntegrationServiceAccount, nil password := types.MustGenerateFactorPassword(cloudIntegrationUser.ID.StringValue())
cloudIntegrationUser, err = ah.Signoz.Modules.User.GetOrCreateUser(ctx, cloudIntegrationUser, user.WithFactorPassword(password))
if err != nil {
return nil, basemodel.InternalError(fmt.Errorf("couldn't look for integration user: %w", err))
}
return cloudIntegrationUser, nil
} }
func (ah *APIHandler) getIngestionUrlAndSigNozAPIUrl(ctx context.Context, licenseKey string) ( func (ah *APIHandler) getIngestionUrlAndSigNozAPIUrl(ctx context.Context, licenseKey string) (
@@ -251,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",

View File

@@ -4,15 +4,10 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"time" "time"
signozerrors "github.com/SigNoz/signoz/pkg/errors"
"log/slog"
"github.com/SigNoz/signoz/ee/query-service/constants" "github.com/SigNoz/signoz/ee/query-service/constants"
"github.com/SigNoz/signoz/pkg/flagger" "github.com/SigNoz/signoz/pkg/flagger"
"github.com/SigNoz/signoz/pkg/http/render" "github.com/SigNoz/signoz/pkg/http/render"
@@ -20,6 +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"
"go.uber.org/zap"
) )
func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) { func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
@@ -39,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", signozerrors.Attr(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", signozerrors.Attr(err)) zap.L().Error("failed to fetch zeus features", zap.Error(err))
} }
} }
} }

View File

@@ -7,7 +7,6 @@ import (
"net/http" "net/http"
"github.com/SigNoz/signoz/ee/query-service/anomaly" "github.com/SigNoz/signoz/ee/query-service/anomaly"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/http/render" "github.com/SigNoz/signoz/pkg/http/render"
baseapp "github.com/SigNoz/signoz/pkg/query-service/app" baseapp "github.com/SigNoz/signoz/pkg/query-service/app"
"github.com/SigNoz/signoz/pkg/query-service/app/queryBuilder" "github.com/SigNoz/signoz/pkg/query-service/app/queryBuilder"
@@ -15,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) {
@@ -36,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", errors.Attr(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
} }
@@ -45,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", errors.Attr(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
} }

View File

@@ -5,23 +5,19 @@ import (
"fmt" "fmt"
"net" "net"
"net/http" "net/http"
_ "net/http/pprof" // http profiler
"slices" "slices"
"go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux"
"go.opentelemetry.io/otel/propagation"
"github.com/SigNoz/signoz/pkg/cache/memorycache" "github.com/SigNoz/signoz/pkg/cache/memorycache"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/factory" "github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/queryparser" "github.com/SigNoz/signoz/pkg/queryparser"
"github.com/SigNoz/signoz/pkg/ruler/rulestore/sqlrulestore" "github.com/SigNoz/signoz/pkg/ruler/rulestore/sqlrulestore"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes" "github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux"
"go.opentelemetry.io/otel/propagation"
"github.com/gorilla/handlers" "github.com/gorilla/handlers"
"github.com/rs/cors"
"github.com/soheilhy/cmux"
"github.com/SigNoz/signoz/ee/query-service/app/api" "github.com/SigNoz/signoz/ee/query-service/app/api"
"github.com/SigNoz/signoz/ee/query-service/rules" "github.com/SigNoz/signoz/ee/query-service/rules"
"github.com/SigNoz/signoz/ee/query-service/usage" "github.com/SigNoz/signoz/ee/query-service/usage"
@@ -29,15 +25,14 @@ import (
"github.com/SigNoz/signoz/pkg/cache" "github.com/SigNoz/signoz/pkg/cache"
"github.com/SigNoz/signoz/pkg/http/middleware" "github.com/SigNoz/signoz/pkg/http/middleware"
"github.com/SigNoz/signoz/pkg/modules/organization" "github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/modules/rulestatehistory"
"github.com/SigNoz/signoz/pkg/prometheus" "github.com/SigNoz/signoz/pkg/prometheus"
"github.com/SigNoz/signoz/pkg/querier" "github.com/SigNoz/signoz/pkg/querier"
"github.com/SigNoz/signoz/pkg/signoz" "github.com/SigNoz/signoz/pkg/signoz"
"github.com/SigNoz/signoz/pkg/sqlstore" "github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/telemetrystore" "github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/web" "github.com/SigNoz/signoz/pkg/web"
"github.com/rs/cors"
"log/slog" "github.com/soheilhy/cmux"
"github.com/SigNoz/signoz/pkg/query-service/agentConf" "github.com/SigNoz/signoz/pkg/query-service/agentConf"
baseapp "github.com/SigNoz/signoz/pkg/query-service/app" baseapp "github.com/SigNoz/signoz/pkg/query-service/app"
@@ -52,6 +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"
"go.uber.org/zap"
) )
// Server runs HTTP, Mux and a grpc server // Server runs HTTP, Mux and a grpc server
@@ -87,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,
@@ -107,7 +102,6 @@ func NewServer(config signoz.Config, signoz *signoz.SigNoz) (*Server, error) {
signoz.TelemetryMetadataStore, signoz.TelemetryMetadataStore,
signoz.Prometheus, signoz.Prometheus,
signoz.Modules.OrgGetter, signoz.Modules.OrgGetter,
signoz.Modules.RuleStateHistory,
signoz.Querier, signoz.Querier,
signoz.Instrumentation.ToProviderSettings(), signoz.Instrumentation.ToProviderSettings(),
signoz.QueryParser, signoz.QueryParser,
@@ -138,7 +132,6 @@ func NewServer(config signoz.Config, signoz *signoz.SigNoz) (*Server, error) {
logParsingPipelineController, err := logparsingpipeline.NewLogParsingPipelinesController( logParsingPipelineController, err := logparsingpipeline.NewLogParsingPipelinesController(
signoz.SQLStore, signoz.SQLStore,
integrationsController.GetPipelinesForInstalledIntegrations, integrationsController.GetPipelinesForInstalledIntegrations,
reader,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@@ -213,7 +206,6 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*h
r := baseapp.NewRouter() r := baseapp.NewRouter()
am := middleware.NewAuthZ(s.signoz.Instrumentation.Logger(), s.signoz.Modules.OrgGetter, s.signoz.Authz) am := middleware.NewAuthZ(s.signoz.Instrumentation.Logger(), s.signoz.Modules.OrgGetter, s.signoz.Authz)
r.Use(middleware.NewRecovery(s.signoz.Instrumentation.Logger()).Wrap)
r.Use(otelmux.Middleware( r.Use(otelmux.Middleware(
"apiserver", "apiserver",
otelmux.WithMeterProvider(s.signoz.Instrumentation.MeterProvider()), otelmux.WithMeterProvider(s.signoz.Instrumentation.MeterProvider()),
@@ -222,8 +214,10 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*h
otelmux.WithFilter(func(r *http.Request) bool { otelmux.WithFilter(func(r *http.Request) bool {
return !slices.Contains([]string{"/api/v1/health"}, r.URL.Path) return !slices.Contains([]string{"/api/v1/health"}, r.URL.Path)
}), }),
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,
@@ -242,6 +236,7 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*h
apiHandler.RegisterWebSocketPaths(r, am) apiHandler.RegisterWebSocketPaths(r, am)
apiHandler.RegisterMessagingQueuesRoutes(r, am) apiHandler.RegisterMessagingQueuesRoutes(r, am)
apiHandler.RegisterThirdPartyApiRoutes(r, am) apiHandler.RegisterThirdPartyApiRoutes(r, am)
apiHandler.MetricExplorerRoutes(r, am)
apiHandler.RegisterTraceFunnelsRoutes(r, am) apiHandler.RegisterTraceFunnelsRoutes(r, am)
err := s.signoz.APIServer.AddToRouter(r) err := s.signoz.APIServer.AddToRouter(r)
@@ -283,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
} }
@@ -303,22 +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", errors.Attr(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 OpAmp Websocket server", "addr", baseconst.OpAmpWsEndpoint) zap.L().Info("Starting pprof server", zap.String("addr", baseconst.DebugHttpPort))
err = http.ListenAndServe(baseconst.DebugHttpPort, nil)
if err != nil {
zap.L().Error("Could not start pprof server", zap.Error(err))
}
}()
go func() {
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", errors.Attr(err)) zap.L().Error("opamp ws server failed to start", zap.Error(err))
s.unavailableChannel <- healthcheck.Unavailable s.unavailableChannel <- healthcheck.Unavailable
} }
}() }()
@@ -345,29 +349,29 @@ func (s *Server) Stop(ctx context.Context) error {
return nil return nil
} }
func makeRulesManager(ch baseint.Reader, cache cache.Cache, alertmanager alertmanager.Alertmanager, sqlstore sqlstore.SQLStore, telemetryStore telemetrystore.TelemetryStore, metadataStore telemetrytypes.MetadataStore, prometheus prometheus.Prometheus, orgGetter organization.Getter, ruleStateHistoryModule rulestatehistory.Module, querier querier.Querier, providerSettings factory.ProviderSettings, queryParser queryparser.QueryParser) (*baserules.Manager, error) { func makeRulesManager(ch baseint.Reader, cache cache.Cache, alertmanager alertmanager.Alertmanager, sqlstore sqlstore.SQLStore, telemetryStore telemetrystore.TelemetryStore, metadataStore telemetrytypes.MetadataStore, prometheus prometheus.Prometheus, orgGetter organization.Getter, querier querier.Querier, providerSettings factory.ProviderSettings, queryParser queryparser.QueryParser) (*baserules.Manager, error) {
ruleStore := sqlrulestore.NewRuleStore(sqlstore, queryParser, providerSettings) ruleStore := sqlrulestore.NewRuleStore(sqlstore, queryParser, providerSettings)
maintenanceStore := sqlrulestore.NewMaintenanceStore(sqlstore) maintenanceStore := sqlrulestore.NewMaintenanceStore(sqlstore)
// create manager opts // create manager opts
managerOpts := &baserules.ManagerOptions{ managerOpts := &baserules.ManagerOptions{
TelemetryStore: telemetryStore, TelemetryStore: telemetryStore,
MetadataStore: metadataStore, MetadataStore: metadataStore,
Prometheus: prometheus, Prometheus: prometheus,
Context: context.Background(), Context: context.Background(),
Reader: ch, Logger: zap.L(),
Querier: querier, Reader: ch,
Logger: providerSettings.Logger, Querier: querier,
Cache: cache, SLogger: providerSettings.Logger,
EvalDelay: baseconst.GetEvalDelay(), Cache: cache,
PrepareTaskFunc: rules.PrepareTaskFunc, EvalDelay: baseconst.GetEvalDelay(),
PrepareTestRuleFunc: rules.TestNotification, PrepareTaskFunc: rules.PrepareTaskFunc,
Alertmanager: alertmanager, PrepareTestRuleFunc: rules.TestNotification,
OrgGetter: orgGetter, Alertmanager: alertmanager,
RuleStore: ruleStore, OrgGetter: orgGetter,
MaintenanceStore: maintenanceStore, RuleStore: ruleStore,
SqlStore: sqlstore, MaintenanceStore: maintenanceStore,
QueryParser: queryParser, SqlStore: sqlstore,
RuleStateHistoryModule: ruleStateHistoryModule, QueryParser: queryParser,
} }
// create Manager // create Manager
@@ -376,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
} }

View File

@@ -12,7 +12,6 @@ import (
"github.com/SigNoz/signoz/ee/query-service/anomaly" "github.com/SigNoz/signoz/ee/query-service/anomaly"
"github.com/SigNoz/signoz/pkg/cache" "github.com/SigNoz/signoz/pkg/cache"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/query-service/common" "github.com/SigNoz/signoz/pkg/query-service/common"
"github.com/SigNoz/signoz/pkg/query-service/model" "github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/transition" "github.com/SigNoz/signoz/pkg/transition"
@@ -309,7 +308,7 @@ func (r *AnomalyRule) buildAndRunQueryV5(ctx context.Context, orgID valuer.UUID,
filteredSeries, filterErr := r.BaseRule.FilterNewSeries(ctx, ts, seriesToProcess) filteredSeries, filterErr := r.BaseRule.FilterNewSeries(ctx, ts, seriesToProcess)
// In case of error we log the error and continue with the original series // In case of error we log the error and continue with the original series
if filterErr != nil { if filterErr != nil {
r.logger.ErrorContext(ctx, "Error filtering new series, ", errors.Attr(filterErr), "rule_name", r.Name()) r.logger.ErrorContext(ctx, "Error filtering new series, ", "error", filterErr, "rule_name", r.Name())
} else { } else {
seriesToProcess = filteredSeries seriesToProcess = filteredSeries
} }
@@ -392,7 +391,7 @@ func (r *AnomalyRule) Eval(ctx context.Context, ts time.Time) (int, error) {
result, err := tmpl.Expand() result, err := tmpl.Expand()
if err != nil { if err != nil {
result = fmt.Sprintf("<error expanding template: %s>", err) result = fmt.Sprintf("<error expanding template: %s>", err)
r.logger.ErrorContext(ctx, "Expanding alert template failed", errors.Attr(err), "data", tmplData, "rule_name", r.Name()) r.logger.ErrorContext(ctx, "Expanding alert template failed", "error", err, "data", tmplData, "rule_name", r.Name())
} }
return result return result
} }
@@ -468,7 +467,7 @@ func (r *AnomalyRule) Eval(ctx context.Context, ts time.Time) (int, error) {
for fp, a := range r.Active { for fp, a := range r.Active {
labelsJSON, err := json.Marshal(a.QueryResultLables) labelsJSON, err := json.Marshal(a.QueryResultLables)
if err != nil { if err != nil {
r.logger.ErrorContext(ctx, "error marshaling labels", errors.Attr(err), "labels", a.Labels) r.logger.ErrorContext(ctx, "error marshaling labels", "error", err, "labels", a.Labels)
} }
if _, ok := resultFPs[fp]; !ok { if _, ok := resultFPs[fp]; !ok {
// If the alert was previously firing, keep it around for a given // If the alert was previously firing, keep it around for a given

View File

@@ -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)

View File

@@ -6,16 +6,14 @@ import (
"time" "time"
"log/slog"
"github.com/google/uuid"
"github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/errors"
basemodel "github.com/SigNoz/signoz/pkg/query-service/model" basemodel "github.com/SigNoz/signoz/pkg/query-service/model"
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/labels" "github.com/SigNoz/signoz/pkg/query-service/utils/labels"
"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"
"go.uber.org/zap"
) )
func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error) { func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error) {
@@ -28,7 +26,6 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error)
if err != nil { if err != nil {
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "evaluation is invalid: %v", err) return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "evaluation is invalid: %v", err)
} }
if opts.Rule.RuleType == ruletypes.RuleTypeThreshold { if opts.Rule.RuleType == ruletypes.RuleTypeThreshold {
// create a threshold rule // create a threshold rule
tr, err := baserules.NewThresholdRule( tr, err := baserules.NewThresholdRule(
@@ -37,12 +34,11 @@ 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),
baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore), baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore),
baserules.WithRuleStateHistoryModule(opts.ManagerOpts.RuleStateHistoryModule),
) )
if err != nil { if err != nil {
@@ -61,13 +57,12 @@ 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),
baserules.WithQueryParser(opts.ManagerOpts.QueryParser), baserules.WithQueryParser(opts.ManagerOpts.QueryParser),
baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore), baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore),
baserules.WithRuleStateHistoryModule(opts.ManagerOpts.RuleStateHistoryModule),
) )
if err != nil { if err != nil {
@@ -87,13 +82,12 @@ 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),
baserules.WithQueryParser(opts.ManagerOpts.QueryParser), baserules.WithQueryParser(opts.ManagerOpts.QueryParser),
baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore), baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore),
baserules.WithRuleStateHistoryModule(opts.ManagerOpts.RuleStateHistoryModule),
) )
if err != nil { if err != nil {
return task, err return task, err
@@ -148,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),
@@ -157,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, errors.Attr(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)
} }
@@ -168,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(),
@@ -179,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, errors.Attr(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 {
@@ -190,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(),
@@ -199,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, errors.Attr(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 {
@@ -211,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(), errors.Attr(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)

View File

@@ -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()
@@ -257,7 +242,7 @@ func TestManager_TestNotification_SendUnmatched_PromRule(t *testing.T) {
WillReturnRows(samplesRows) WillReturnRows(samplesRows)
// Create Prometheus provider for this test // Create Prometheus provider for this test
promProvider = prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{Timeout: 2 * time.Minute}, store) promProvider = prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, store)
}, },
ManagerOptionsHook: func(opts *rules.ManagerOptions) { ManagerOptionsHook: func(opts *rules.ManagerOptions) {
// Set Prometheus provider for PromQL queries // Set Prometheus provider for PromQL queries

View File

@@ -8,14 +8,13 @@ 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/errors"
"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"
"github.com/SigNoz/signoz/pkg/query-service/utils/encryption" "github.com/SigNoz/signoz/pkg/query-service/utils/encryption"
@@ -77,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", errors.Attr(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", errors.Attr(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
} }
@@ -116,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", errors.Attr(err)) zap.L().Error("failed to get usage from clickhouse: %v", zap.Error(err))
return return
} }
for _, u := range dbusages { for _, u := range dbusages {
@@ -126,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", errors.Attr(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", errors.Attr(err)) zap.L().Error("error while unmarshalling usage data: %v", zap.Error(err))
return return
} }
@@ -164,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", errors.Attr(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", errors.Attr(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
} }
@@ -180,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)

View File

@@ -4,12 +4,10 @@ import (
"context" "context"
"database/sql" "database/sql"
"github.com/uptrace/bun"
"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"
"github.com/uptrace/bun"
) )
type provider struct { type provider struct {
@@ -34,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
} }
@@ -74,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)
@@ -114,7 +111,7 @@ WHERE
defer func() { defer func() {
if err := constraintsRows.Close(); err != nil { if err := constraintsRows.Close(); err != nil {
provider.settings.Logger().ErrorContext(ctx, "error closing rows", errors.Attr(err)) provider.settings.Logger().ErrorContext(ctx, "error closing rows", "error", err)
} }
}() }()
@@ -175,7 +172,7 @@ WHERE
defer func() { defer func() {
if err := foreignKeyConstraintsRows.Close(); err != nil { if err := foreignKeyConstraintsRows.Close(); err != nil {
provider.settings.Logger().ErrorContext(ctx, "error closing rows", errors.Attr(err)) provider.settings.Logger().ErrorContext(ctx, "error closing rows", "error", err)
} }
}() }()
@@ -223,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
@@ -236,24 +231,18 @@ 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() {
if err := rows.Close(); err != nil { if err := rows.Close(); err != nil {
provider.settings.Logger().ErrorContext(ctx, "error closing rows", errors.Attr(err)) provider.settings.Logger().ErrorContext(ctx, "error closing rows", "error", err)
} }
}() }()
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
@@ -261,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

View File

@@ -336,10 +336,9 @@ func (dialect *dialect) UpdatePrimaryKey(ctx context.Context, bun bun.IDB, oldMo
} }
fkReference := "" fkReference := ""
switch reference { if reference == Org {
case Org:
fkReference = OrgReference fkReference = OrgReference
case User: } else if reference == User {
fkReference = UserReference fkReference = UserReference
} }
@@ -393,10 +392,9 @@ func (dialect *dialect) AddPrimaryKey(ctx context.Context, bun bun.IDB, oldModel
} }
fkReference := "" fkReference := ""
switch reference { if reference == Org {
case Org:
fkReference = OrgReference fkReference = OrgReference
case User: } else if reference == User {
fkReference = UserReference fkReference = UserReference
} }

View File

@@ -14,21 +14,14 @@ import (
"github.com/uptrace/bun/dialect/pgdialect" "github.com/uptrace/bun/dialect/pgdialect"
) )
var _ Pooler = new(provider)
type provider struct { type provider struct {
settings factory.ScopedProviderSettings settings factory.ScopedProviderSettings
sqldb *sql.DB sqldb *sql.DB
bundb *sqlstore.BunDB bundb *sqlstore.BunDB
pgxPool *pgxpool.Pool
dialect *dialect dialect *dialect
formatter sqlstore.SQLFormatter formatter sqlstore.SQLFormatter
} }
type Pooler interface {
Pool() *pgxpool.Pool
}
func NewFactory(hookFactories ...factory.ProviderFactory[sqlstore.SQLStoreHook, sqlstore.Config]) factory.ProviderFactory[sqlstore.SQLStore, sqlstore.Config] { func NewFactory(hookFactories ...factory.ProviderFactory[sqlstore.SQLStoreHook, sqlstore.Config]) factory.ProviderFactory[sqlstore.SQLStore, sqlstore.Config] {
return factory.NewProviderFactory(factory.MustNewName("postgres"), func(ctx context.Context, providerSettings factory.ProviderSettings, config sqlstore.Config) (sqlstore.SQLStore, error) { return factory.NewProviderFactory(factory.MustNewName("postgres"), func(ctx context.Context, providerSettings factory.ProviderSettings, config sqlstore.Config) (sqlstore.SQLStore, error) {
hooks := make([]sqlstore.SQLStoreHook, len(hookFactories)) hooks := make([]sqlstore.SQLStoreHook, len(hookFactories))
@@ -69,7 +62,6 @@ func New(ctx context.Context, providerSettings factory.ProviderSettings, config
settings: settings, settings: settings,
sqldb: sqldb, sqldb: sqldb,
bundb: bunDB, bundb: bunDB,
pgxPool: pool,
dialect: new(dialect), dialect: new(dialect),
formatter: newFormatter(bunDB.Dialect()), formatter: newFormatter(bunDB.Dialect()),
}, nil }, nil
@@ -83,10 +75,6 @@ func (provider *provider) SQLDB() *sql.DB {
return provider.sqldb return provider.sqldb
} }
func (provider *provider) Pool() *pgxpool.Pool {
return provider.pgxPool
}
func (provider *provider) Dialect() sqlstore.SQLDialect { func (provider *provider) Dialect() sqlstore.SQLDialect {
return provider.dialect return provider.dialect
} }
@@ -113,7 +101,7 @@ func (provider *provider) WrapNotFoundErrf(err error, code errors.Code, format s
func (provider *provider) WrapAlreadyExistsErrf(err error, code errors.Code, format string, args ...any) error { func (provider *provider) WrapAlreadyExistsErrf(err error, code errors.Code, format string, args ...any) error {
var pgErr *pgconn.PgError var pgErr *pgconn.PgError
if errors.As(err, &pgErr) && (pgErr.Code == "23505" || pgErr.Code == "23503") { if errors.As(err, &pgErr) && pgErr.Code == "23505" {
return errors.Wrapf(err, errors.TypeAlreadyExists, code, format, args...) return errors.Wrapf(err, errors.TypeAlreadyExists, code, format, args...)
} }

View File

@@ -19,7 +19,7 @@ var (
once sync.Once once sync.Once
) )
// initializes the Zeus configuration. // initializes the Zeus configuration
func Config() zeus.Config { func Config() zeus.Config {
once.Do(func() { once.Do(func() {
parsedURL, err := neturl.Parse(url) parsedURL, err := neturl.Parse(url)

View File

@@ -189,7 +189,7 @@ func (provider *Provider) do(ctx context.Context, url *url.URL, method string, k
return nil, provider.errFromStatusCode(response.StatusCode, errorMessage) return nil, provider.errFromStatusCode(response.StatusCode, errorMessage)
} }
// This can be taken down to the client package. // This can be taken down to the client package
func (provider *Provider) errFromStatusCode(statusCode int, errorMessage string) error { func (provider *Provider) errFromStatusCode(statusCode int, errorMessage string) error {
switch statusCode { switch statusCode {
case http.StatusBadRequest: case http.StatusBadRequest:

View File

@@ -1,97 +0,0 @@
---
globs: **/*.store.ts
alwaysApply: false
---
# State Management: React Query, nuqs, Zustand
Use the following stack. Do **not** introduce or recommend Redux or React Context for shared/global state.
## Server state → React Query
- **Use for:** API responses, time-series data, caching, background refetch, retries, stale/refresh.
- **Do not use Redux/Context** to store or mirror data that comes from React Query (e.g. do not dispatch API results into Redux).
- Prefer generated React Query hooks from `frontend/src/api/generated` when available.
- Keep server state in React Query; expose it via hooks that return the query result (and optionally memoized derived values). Do not duplicate it in Redux or Context.
```tsx
// ✅ GOOD: single source of truth from React Query
export function useAppStateHook() {
const { data, isError } = useQuery(...)
const memoizedConfigs = useMemo(() => ({ ... }), [data?.configs])
return { configs: memoizedConfigs, isError, ... }
}
// ❌ BAD: copying React Query result into Redux
dispatch({ type: UPDATE_LATEST_VERSION, payload: queryResponse.data })
```
## URL state → nuqs
- **Use for:** shareable state, filters, time range, selected values, pagination, view state that belongs in the URL.
- **Do not use Redux/Context** for state that should be shareable or reflected in the URL.
- Use [nuqs](https://nuqs.dev/docs/basic-usage) for typed, type-safe URL search params. Avoid ad-hoc `useSearchParams` encoding/decoding.
- Keep URL payload small; respect browser URL length limits (e.g. Chrome ~2k chars). Do not put large datasets or sensitive data in query params.
```tsx
// ✅ GOOD: nuqs for filters / time range / selection
const [timeRange, setTimeRange] = useQueryState('timeRange', parseAsString.withDefault('1h'))
const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1))
// ❌ BAD: Redux/Context for shareable or URL-synced state
const { timeRange } = useContext(SomeContext)
```
## Client state → Zustand
- **Use for:** global/client state, cross-component state, feature flags, complex or large client objects (e.g. dashboard state, query builder state).
- **Do not use Redux or React Context** for global or feature-level client state.
- Prefer small, domain-scoped stores (e.g. DashboardStore, QueryBuilderStore).
### Zustand best practices (align with eslint-plugin-zustand-rules)
- **One store per module.** Do not define multiple `create()` calls in the same file; use one store per module (or compose slices into one store).
- **Always use selectors.** Call the store hook with a selector so only the used slice triggers re-renders. Never use `useStore()` with no selector.
```tsx
// ✅ GOOD: selector — re-renders only when isDashboardLocked changes
const isLocked = useDashboardStore(state => state.isDashboardLocked)
// ❌ BAD: no selector — re-renders on any store change
const state = useDashboardStore()
```
- **Never mutate state directly.** Update only via `set` or `setState` (or `getState()` + `set` for reads). No `state.foo = x` or `state.bears += 1` inside actions.
```tsx
// ✅ GOOD: use set
increment: () => set(state => ({ bears: state.bears + 1 }))
// ❌ BAD: direct mutation
increment: () => { state.bears += 1 }
```
- **State properties before actions.** In the store object, list all state fields first, then action functions.
- **Split into slices when state is large.** If a store has many top-level properties (e.g. more than 510), split into slice factories and combine with one `create()`.
```tsx
// ✅ GOOD: slices for large state
const createBearSlice = set => ({ bears: 0, addBear: () => set(s => ({ bears: s.bears + 1 })) })
const createFishSlice = set => ({ fish: 0, addFish: () => set(s => ({ fish: s.fish + 1 })) })
const useStore = create(set => ({ ...createBearSlice(set), ...createFishSlice(set) }))
```
- **In projects using Zustand:** add `eslint-plugin-zustand-rules` and extend `plugin:zustand-rules/recommended` to enforce these rules automatically.
## Local state → React state only
- **Use useState/useReducer** for: component-local UI state, form inputs, toggles, hover state, data that never leaves the component.
- Do not use Zustand, Redux, or Context for state that is purely local to one component or a small subtree.
## Summary
| State type | Use | Avoid |
|-------------------|------------------|--------------------|
| Server / API | React Query | Redux, Context |
| URL / shareable | nuqs | Redux, Context |
| Global client | Zustand | Redux, Context |
| Local UI | useState/useReducer | Zustand, Redux, Context |

View File

@@ -1,150 +0,0 @@
---
name: migrate-state-management
description: Migrate Redux or React Context to the correct state option (React Query for server state, nuqs for URL/shareable state, Zustand for global client state). Use when refactoring away from Redux/Context, moving state to the right store, or when the user asks to migrate state management.
---
# Migrate State: Redux/Context → React Query, nuqs, Zustand
Do **not** introduce or recommend Redux or React Context. Migrate existing usage to the stack below.
## 1. Classify the state
Before changing code, classify what the state represents:
| If the state is… | Migrate to | Do not use |
|------------------|------------|------------|
| From API / server (versions, configs, fetched lists, time-series) | **React Query** | Redux, Context |
| Shareable via URL (filters, time range, page, selected ids) | **nuqs** | Redux, Context |
| Global/client UI (dashboard lock, query builder, feature flags, large client objects) | **Zustand** | Redux, Context |
| Local to one component (inputs, toggles, hover) | **useState / useReducer** | Zustand, Redux, Context |
If one slice mixes concerns (e.g. Redux has both API data and pagination), split: API → React Query, pagination → nuqs, rest → Zustand or local state.
## 2. Migrate to React Query (server state)
**When:** State comes from or mirrors an API response (e.g. `currentVersion`, `latestVersion`, `configs`, lists).
**Steps:**
1. Find where the data is fetched (existing `useQuery`/API call) and where it is dispatched or set in Context/Redux.
2. Remove the dispatch/set that writes API results into Redux/Context.
3. Expose a single hook that uses the query and returns the same shape consumers expect (use `useMemo` for derived objects like `configs` to avoid unnecessary re-renders).
4. Replace Redux/Context consumption with the new hook. Prefer generated React Query hooks from `frontend/src/api/generated` when available.
5. Configure cache/refetch (e.g. `refetchOnMount: false`, `staleTime`) so behavior matches previous “single source” expectations.
**Before (Redux mirroring React Query):**
```tsx
if (getUserLatestVersionResponse.isFetched && getUserLatestVersionResponse.isSuccess && getUserLatestVersionResponse.data?.payload) {
dispatch({ type: UPDATE_LATEST_VERSION, payload: { latestVersion: getUserLatestVersionResponse.data.payload.tag_name } })
}
```
**After (single source in React Query):**
```tsx
export function useAppStateHook() {
const { data, isError } = useQuery(...)
const memoizedConfigs = useMemo(() => ({ ... }), [data?.configs])
return {
latestVersion: data?.payload?.tag_name,
configs: memoizedConfigs,
isError,
}
}
```
Consumers use `useAppStateHook()` instead of `useSelector` or Context. Do not copy React Query result into Redux or Context.
## 3. Migrate to nuqs (URL / shareable state)
**When:** State should be in the URL: filters, time range, pagination, selected values, view state. Keep payload small (e.g. Chrome ~2k chars); no large datasets or sensitive data.
**Steps:**
1. Identify which Redux/Context fields are shareable or already reflected in the URL (e.g. `currentPage`, `timeRange`, `selectedFilter`).
2. Add nuqs (or use existing): `useQueryState('param', parseAsString.withDefault('…'))` (or `parseAsInteger`, etc.).
3. Replace reads/writes of those fields with nuqs hooks. Use typed parsers; avoid ad-hoc `useSearchParams` encoding/decoding.
4. Remove the same fields from Redux/Context and their reducers/providers.
**Before (Context/Redux):**
```tsx
const { timeRange } = useContext(SomeContext)
const [page, setPage] = useDispatch(...)
```
**After (nuqs):**
```tsx
const [timeRange, setTimeRange] = useQueryState('timeRange', parseAsString.withDefault('1h'))
const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1))
```
## 4. Migrate to Zustand (global client state)
**When:** State is global or cross-component client state: feature flags, dashboard state, query builder state, complex/large client objects (e.g. up to ~1.52MB). Not for server cache or local-only UI.
**Steps:**
1. Create one store per domain (e.g. `DashboardStore`, `QueryBuilderStore`). One `create()` per module; for large state use slice factories and combine.
2. Put state properties first, then actions. Use `set` (or `setState` / `getState()` + `set`) for updates; never mutate state directly.
3. Replace Context/Redux consumption with the store hook **and a selector** so only the used slice triggers re-renders.
4. Remove the old Context provider / Redux slice and related dispatches.
**Selector (required):**
```tsx
const isLocked = useDashboardStore(state => state.isDashboardLocked)
```
Never use `useStore()` with no selector. Never do `state.foo = x` inside actions; use `set(state => ({ ... }))`.
**Before (Context/Redux):**
```tsx
const { isDashboardLocked, setLocked } = useContext(DashboardContext)
```
**After (Zustand):**
```tsx
const isLocked = useDashboardStore(state => state.isDashboardLocked)
const setLocked = useDashboardStore(state => state.setLocked)
```
For large stores (many top-level fields), split into slices and combine:
```tsx
const createBearSlice = set => ({ bears: 0, addBear: () => set(s => ({ bears: s.bears + 1 })) })
const useStore = create(set => ({ ...createBearSlice(set), ...createFishSlice(set) }))
```
Add `eslint-plugin-zustand-rules` with `plugin:zustand-rules/recommended` to enforce selectors and no direct mutation.
## 5. Migrate to local state (useState / useReducer)
**When:** State is used only inside one component or a small subtree (form inputs, toggles, hover, panel selection). No URL sync, no cross-feature sharing.
**Steps:**
1. Move the state into the component that owns it (or the smallest common parent).
2. Use `useState` or `useReducer` (useReducer when multiple related fields change together).
3. Remove from Redux/Context and any provider/slice.
Do not use Zustand, Redux, or Context for purely local UI state.
## 6. Migration checklist
- [ ] Classify each piece of state (server / URL / global client / local).
- [ ] Server state: move to React Query; expose via hook; remove Redux/Context mirroring.
- [ ] URL state: move to nuqs; remove from Redux/Context; keep URL payload small.
- [ ] Global client state: move to Zustand with selectors and immutable updates; one store per domain.
- [ ] Local state: move to useState/useReducer in the owning component.
- [ ] Remove old Redux slices / Context providers and all dispatches/consumers for migrated state.
- [ ] Do not duplicate the same data in multiple places (e.g. React Query + Redux).
## Additional resources
- Project rule: [.cursor/rules/state-management.mdc](../../rules/state-management.mdc)
- Detailed patterns and rationale: [reference.md](reference.md)

View File

@@ -1,50 +0,0 @@
# State migration reference
## Why migrate
- **Context:** Re-renders all consumers on any change; no granular subscriptions; becomes brittle at scale.
- **Redux:** Heavy boilerplate (actions, reducers, selectors, Provider); slower onboarding; often used to mirror React Query or URL state.
- **Goal:** Fewer mechanisms, domain isolation, granular subscriptions, single source of truth per state type.
## React Query migration (server state)
Typical anti-pattern: API is called via React Query, then result is dispatched to Redux. Flow becomes: Component → useQueries → API → dispatch → Reducer → Redux state → useSelector.
Correct flow: Component → useQuery (or custom hook wrapping it) → same component reads from hook. No Redux/Context in between.
- Prefer generated hooks from `frontend/src/api/generated`.
- For “app state” that is just API data (versions, configs), one hook that returns `{ ...data, configs: useMemo(...) }` is enough. No selectors needed for plain data; useMemo only where the value is used as dependency (e.g. in useState).
- Set `staleTime` / `refetchOnMount` etc. so refetch behavior matches previous expectations.
## nuqs migration (URL state)
Redux/Context often hold pagination, filters, time range, selected values that are shareable. Those belong in the URL.
- Use [nuqs](https://nuqs.dev/docs/basic-usage) for typed search params. Avoid ad-hoc `useSearchParams` + manual encoding.
- Browser limits: Chrome ~2k chars practical; keep payload small; no large datasets or secrets in query params.
- If the app uses TanStack Router, search params can be handled there; otherwise nuqs is the standard.
## Zustand migration (client state)
- One store per domain (e.g. DashboardStore, QueryBuilderStore). Multiple `create()` in one file is disallowed; use one store or composed slices.
- Always use a selector: `useStore(s => s.field)` so only that field drives re-renders.
- Never mutate: update only via `set(state => ({ ... }))` or `setState` / `getState()` + `set`.
- State properties first, then actions. For 510+ top-level fields, split into slice factories and combine with one `create()`.
- Large client objects: Zustand is for “large” in the ~1.52MB range; above that, optimize at API/store design.
- Testing: no Provider; stores are plain functions; easy to reset and mock.
## What not to use
- **Redux / Context** for new or migrated shared/global state.
- **Redux / Context** to store or mirror React Query results.
- **Redux / Context** for state that should live in the URL (use nuqs).
- **Zustand / Redux / Context** for component-local UI (use useState/useReducer).
## Summary table
| State type | Use | Avoid |
|-------------|--------------------|-----------------|
| Server/API | React Query | Redux, Context |
| URL/shareable | nuqs | Redux, Context |
| Global client | Zustand | Redux, Context |
| Local UI | useState/useReducer | Zustand, Redux, Context |

View File

@@ -193,37 +193,8 @@ module.exports = {
], ],
}, },
], ],
'no-restricted-syntax': [
'error',
{
selector:
// TODO: Make this generic on removal of redux
"CallExpression[callee.property.name='getState'][callee.object.name=/^use/]",
message:
'Avoid calling .getState() directly. Export a standalone action from the store instead.',
},
],
}, },
overrides: [ overrides: [
{
files: ['src/**/*.{jsx,tsx,ts}'],
excludedFiles: [
'**/*.test.{js,jsx,ts,tsx}',
'**/*.spec.{js,jsx,ts,tsx}',
'**/__tests__/**/*.{js,jsx,ts,tsx}',
],
rules: {
'no-restricted-properties': [
'error',
{
object: 'navigator',
property: 'clipboard',
message:
'Do not use navigator.clipboard directly since it does not work well with specific browsers. Use hook useCopyToClipboard from react-use library. https://streamich.github.io/react-use/?path=/story/side-effects-usecopytoclipboard--docs',
},
],
},
},
{ {
files: [ files: [
'**/*.test.{js,jsx,ts,tsx}', '**/*.test.{js,jsx,ts,tsx}',
@@ -246,13 +217,5 @@ module.exports = {
'@typescript-eslint/no-unused-vars': 'warn', '@typescript-eslint/no-unused-vars': 'warn',
}, },
}, },
{
// Store definition files are the only place .getState() is permitted —
// they are the canonical source for standalone action exports.
files: ['**/*Store.{ts,tsx}'],
rules: {
'no-restricted-syntax': 'off',
},
},
], ],
}; };

View File

@@ -12,7 +12,7 @@
or or
`docker build . -t tagname` `docker build . -t tagname`
**Tag to remote url- Introduce versioning later on** **Tag to remote url- Introduce versinoing later on**
``` ```
docker tag signoz/frontend:latest 7296823551/signoz:latest docker tag signoz/frontend:latest 7296823551/signoz:latest

View File

@@ -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} />;
}

View File

@@ -2,7 +2,6 @@
interface SafeNavigateOptions { interface SafeNavigateOptions {
replace?: boolean; replace?: boolean;
state?: unknown; state?: unknown;
newTab?: boolean;
} }
interface SafeNavigateTo { interface SafeNavigateTo {
@@ -21,7 +20,9 @@ interface UseSafeNavigateReturn {
export const useSafeNavigate = (): UseSafeNavigateReturn => ({ export const useSafeNavigate = (): UseSafeNavigateReturn => ({
safeNavigate: jest.fn( safeNavigate: jest.fn(
(_to: SafeNavigateToType, _options?: SafeNavigateOptions) => {}, (to: SafeNavigateToType, options?: SafeNavigateOptions) => {
console.log(`Mock safeNavigate called with:`, to, options);
},
) as jest.MockedFunction< ) as jest.MockedFunction<
(to: SafeNavigateToType, options?: SafeNavigateOptions) => void (to: SafeNavigateToType, options?: SafeNavigateOptions) => void
>, >,

View File

@@ -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,
@@ -24,8 +23,7 @@ const config: Config.InitialOptions = {
'<rootDir>/node_modules/@signozhq/icons/dist/index.esm.js', '<rootDir>/node_modules/@signozhq/icons/dist/index.esm.js',
'^react-syntax-highlighter/dist/esm/(.*)$': '^react-syntax-highlighter/dist/esm/(.*)$':
'<rootDir>/node_modules/react-syntax-highlighter/dist/cjs/$1', '<rootDir>/node_modules/react-syntax-highlighter/dist/cjs/$1',
'^@signozhq/(?!ui$)([^/]+)$': '^@signozhq/([^/]+)$': '<rootDir>/node_modules/@signozhq/$1/dist/$1.js',
'<rootDir>/node_modules/@signozhq/$1/dist/$1.js',
}, },
extensionsToTreatAsEsm: ['.ts'], extensionsToTreatAsEsm: ['.ts'],
testMatch: ['<rootDir>/src/**/*?(*.)(test).(ts|js)?(x)'], testMatch: ['<rootDir>/src/**/*?(*.)(test).(ts|js)?(x)'],

View File

@@ -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,9 +64,8 @@
"@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",
"@signozhq/ui": "0.0.5",
"@tanstack/react-table": "8.20.6", "@tanstack/react-table": "8.20.6",
"@tanstack/react-virtual": "3.11.2", "@tanstack/react-virtual": "3.11.2",
"@uiw/codemirror-theme-copilot": "4.23.11", "@uiw/codemirror-theme-copilot": "4.23.11",
@@ -137,7 +135,6 @@
"react-full-screen": "1.1.1", "react-full-screen": "1.1.1",
"react-grid-layout": "^1.3.4", "react-grid-layout": "^1.3.4",
"react-helmet-async": "1.3.0", "react-helmet-async": "1.3.0",
"react-hook-form": "7.71.2",
"react-i18next": "^11.16.1", "react-i18next": "^11.16.1",
"react-lottie": "1.2.10", "react-lottie": "1.2.10",
"react-markdown": "8.0.7", "react-markdown": "8.0.7",
@@ -164,7 +161,6 @@
"vite-plugin-html": "3.2.2", "vite-plugin-html": "3.2.2",
"web-vitals": "^0.2.4", "web-vitals": "^0.2.4",
"xstate": "^4.31.0", "xstate": "^4.31.0",
"zod": "4.3.6",
"zustand": "5.0.11" "zustand": "5.0.11"
}, },
"browserslist": { "browserslist": {

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 214 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="#fa520f" viewBox="0 0 24 24"><title>Mistral AI</title><path d="M17.143 3.429v3.428h-3.429v3.429h-3.428V6.857H6.857V3.43H3.43v13.714H0v3.428h10.286v-3.428H6.857v-3.429h3.429v3.429h3.429v-3.429h3.428v3.429h-3.428v3.428H24v-3.428h-3.43V3.429z"/></svg>

Before

Width:  |  Height:  |  Size: 294 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 120 120"><defs><linearGradient id="a" x1="0%" x2="100%" y1="0%" y2="100%"><stop offset="0%" stop-color="#ff4d4d"/><stop offset="100%" stop-color="#991b1b"/></linearGradient></defs><path fill="url(#a)" d="M60 10c-30 0-45 25-45 45s15 40 30 45v10h10v-10s5 2 10 0v10h10v-10c15-5 30-25 30-45S90 10 60 10"/><path fill="url(#a)" d="M20 45C5 40 0 50 5 60s15 5 20-5c3-7 0-10-5-10"/><path fill="url(#a)" d="M100 45c15-5 20 5 15 15s-15 5-20-5c-3-7 0-10 5-10"/><path stroke="#ff4d4d" stroke-linecap="round" stroke-width="3" d="M45 15Q35 5 30 8M75 15Q85 5 90 8"/><circle cx="45" cy="35" r="6" fill="#050810"/><circle cx="75" cy="35" r="6" fill="#050810"/><circle cx="46" cy="34" r="2.5" fill="#00e5cc"/><circle cx="76" cy="34" r="2.5" fill="#00e5cc"/></svg>

Before

Width:  |  Height:  |  Size: 809 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>Render</title><path d="M18.263.007c-3.121-.147-5.744 2.109-6.192 5.082-.018.138-.045.272-.067.405-.696 3.703-3.936 6.507-7.827 6.507a7.9 7.9 0 0 1-3.825-.979.202.202 0 0 0-.302.178V24H12v-8.999c0-1.656 1.338-3 2.987-3h2.988c3.382 0 6.103-2.817 5.97-6.244-.12-3.084-2.61-5.603-5.682-5.75"/></svg>

Before

Width:  |  Height:  |  Size: 362 B

View File

@@ -15,6 +15,5 @@
"logs_to_metrics": "Logs To Metrics", "logs_to_metrics": "Logs To Metrics",
"roles": "Roles", "roles": "Roles",
"role_details": "Role Details", "role_details": "Role Details",
"members": "Members", "members": "Members"
"service_accounts": "Service Accounts"
} }

View File

@@ -50,8 +50,5 @@
"INFRASTRUCTURE_MONITORING_KUBERNETES": "SigNoz | Infra Monitoring", "INFRASTRUCTURE_MONITORING_KUBERNETES": "SigNoz | Infra Monitoring",
"METER_EXPLORER": "SigNoz | Meter Explorer", "METER_EXPLORER": "SigNoz | Meter Explorer",
"METER_EXPLORER_VIEWS": "SigNoz | Meter Explorer Views", "METER_EXPLORER_VIEWS": "SigNoz | Meter Explorer Views",
"METER": "SigNoz | Meter", "METER": "SigNoz | Meter"
"ROLES_SETTINGS": "SigNoz | Roles",
"MEMBERS_SETTINGS": "SigNoz | Members",
"SERVICE_ACCOUNTS_SETTINGS": "SigNoz | Service Accounts"
} }

View File

@@ -15,6 +15,5 @@
"logs_to_metrics": "Logs To Metrics", "logs_to_metrics": "Logs To Metrics",
"roles": "Roles", "roles": "Roles",
"role_details": "Role Details", "role_details": "Role Details",
"members": "Members", "members": "Members"
"service_accounts": "Service Accounts"
} }

View File

@@ -75,6 +75,5 @@
"METER_EXPLORER_VIEWS": "SigNoz | Meter Explorer Views", "METER_EXPLORER_VIEWS": "SigNoz | Meter Explorer Views",
"METER": "SigNoz | Meter", "METER": "SigNoz | Meter",
"ROLES_SETTINGS": "SigNoz | Roles", "ROLES_SETTINGS": "SigNoz | Roles",
"MEMBERS_SETTINGS": "SigNoz | Members", "MEMBERS_SETTINGS": "SigNoz | Members"
"SERVICE_ACCOUNTS_SETTINGS": "SigNoz | Service Accounts"
} }

View File

@@ -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

View File

@@ -101,22 +101,6 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
preference.name === ORG_PREFERENCES.ORG_ONBOARDING, preference.name === ORG_PREFERENCES.ORG_ONBOARDING,
)?.value; )?.value;
// Don't redirect to onboarding if workspace has issues (blocked, suspended, or restricted)
// User needs access to settings/billing to fix payment issues
const isWorkspaceBlocked = trialInfo?.workSpaceBlock;
const isWorkspaceSuspended = activeLicense?.state === LicenseState.DEFAULTED;
const isWorkspaceAccessRestricted =
activeLicense?.state === LicenseState.TERMINATED ||
activeLicense?.state === LicenseState.EXPIRED ||
activeLicense?.state === LicenseState.CANCELLED;
const hasWorkspaceIssue =
isWorkspaceBlocked || isWorkspaceSuspended || isWorkspaceAccessRestricted;
if (hasWorkspaceIssue) {
return;
}
const isFirstUser = checkFirstTimeUser(); const isFirstUser = checkFirstTimeUser();
if ( if (
isFirstUser && isFirstUser &&
@@ -135,36 +119,40 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
orgPreferences, orgPreferences,
usersData, usersData,
pathname, pathname,
trialInfo?.workSpaceBlock,
activeLicense?.state,
]); ]);
const navigateToWorkSpaceBlocked = useCallback((): void => { const navigateToWorkSpaceBlocked = (route: any): void => {
const { path } = route;
const isRouteEnabledForWorkspaceBlockedState = const isRouteEnabledForWorkspaceBlockedState =
isAdmin && isAdmin &&
(pathname === ROUTES.SETTINGS || (path === ROUTES.SETTINGS ||
pathname === ROUTES.ORG_SETTINGS || path === ROUTES.ORG_SETTINGS ||
pathname === ROUTES.MEMBERS_SETTINGS || path === ROUTES.MEMBERS_SETTINGS ||
pathname === ROUTES.BILLING || path === ROUTES.BILLING ||
pathname === ROUTES.MY_SETTINGS); path === ROUTES.MY_SETTINGS);
if ( if (
pathname && path &&
pathname !== ROUTES.WORKSPACE_LOCKED && path !== ROUTES.WORKSPACE_LOCKED &&
!isRouteEnabledForWorkspaceBlockedState !isRouteEnabledForWorkspaceBlockedState
) { ) {
history.push(ROUTES.WORKSPACE_LOCKED); history.push(ROUTES.WORKSPACE_LOCKED);
} }
}, [isAdmin, pathname]); };
const navigateToWorkSpaceAccessRestricted = useCallback((): void => { const navigateToWorkSpaceAccessRestricted = (route: any): void => {
if (pathname && pathname !== ROUTES.WORKSPACE_ACCESS_RESTRICTED) { const { path } = route;
if (path && path !== ROUTES.WORKSPACE_ACCESS_RESTRICTED) {
history.push(ROUTES.WORKSPACE_ACCESS_RESTRICTED); history.push(ROUTES.WORKSPACE_ACCESS_RESTRICTED);
} }
}, [pathname]); };
useEffect(() => { useEffect(() => {
if (!isFetchingActiveLicense && activeLicense) { if (!isFetchingActiveLicense && activeLicense) {
const currentRoute = mapRoutes.get('current');
const isTerminated = activeLicense.state === LicenseState.TERMINATED; const isTerminated = activeLicense.state === LicenseState.TERMINATED;
const isExpired = activeLicense.state === LicenseState.EXPIRED; const isExpired = activeLicense.state === LicenseState.EXPIRED;
const isCancelled = activeLicense.state === LicenseState.CANCELLED; const isCancelled = activeLicense.state === LicenseState.CANCELLED;
@@ -173,53 +161,61 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
const { platform } = activeLicense; const { platform } = activeLicense;
if (isWorkspaceAccessRestricted && platform === LicensePlatform.CLOUD) { if (
navigateToWorkSpaceAccessRestricted(); isWorkspaceAccessRestricted &&
platform === LicensePlatform.CLOUD &&
currentRoute
) {
navigateToWorkSpaceAccessRestricted(currentRoute);
} }
} }
}, [ }, [isFetchingActiveLicense, activeLicense, mapRoutes, pathname]);
isFetchingActiveLicense,
activeLicense,
navigateToWorkSpaceAccessRestricted,
]);
useEffect(() => { useEffect(() => {
if (!isFetchingActiveLicense) { if (!isFetchingActiveLicense) {
const currentRoute = mapRoutes.get('current');
const shouldBlockWorkspace = trialInfo?.workSpaceBlock; const shouldBlockWorkspace = trialInfo?.workSpaceBlock;
if ( if (
shouldBlockWorkspace && shouldBlockWorkspace &&
currentRoute &&
activeLicense?.platform === LicensePlatform.CLOUD activeLicense?.platform === LicensePlatform.CLOUD
) { ) {
navigateToWorkSpaceBlocked(); navigateToWorkSpaceBlocked(currentRoute);
} }
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ }, [
isFetchingActiveLicense, isFetchingActiveLicense,
trialInfo?.workSpaceBlock, trialInfo?.workSpaceBlock,
activeLicense?.platform, activeLicense?.platform,
navigateToWorkSpaceBlocked, mapRoutes,
pathname,
]); ]);
const navigateToWorkSpaceSuspended = useCallback((): void => { const navigateToWorkSpaceSuspended = (route: any): void => {
if (pathname && pathname !== ROUTES.WORKSPACE_SUSPENDED) { const { path } = route;
if (path && path !== ROUTES.WORKSPACE_SUSPENDED) {
history.push(ROUTES.WORKSPACE_SUSPENDED); history.push(ROUTES.WORKSPACE_SUSPENDED);
} }
}, [pathname]); };
useEffect(() => { useEffect(() => {
if (!isFetchingActiveLicense && activeLicense) { if (!isFetchingActiveLicense && activeLicense) {
const currentRoute = mapRoutes.get('current');
const shouldSuspendWorkspace = const shouldSuspendWorkspace =
activeLicense.state === LicenseState.DEFAULTED; activeLicense.state === LicenseState.DEFAULTED;
if ( if (
shouldSuspendWorkspace && shouldSuspendWorkspace &&
currentRoute &&
activeLicense.platform === LicensePlatform.CLOUD activeLicense.platform === LicensePlatform.CLOUD
) { ) {
navigateToWorkSpaceSuspended(); navigateToWorkSpaceSuspended(currentRoute);
} }
} }
}, [isFetchingActiveLicense, activeLicense, navigateToWorkSpaceSuspended]); }, [isFetchingActiveLicense, activeLicense, mapRoutes, pathname]);
useEffect(() => { useEffect(() => {
if (org && org.length > 0 && org[0].id !== undefined) { if (org && org.length > 0 && org[0].id !== undefined) {

File diff suppressed because it is too large Load Diff

View File

@@ -157,6 +157,10 @@ export const IngestionSettings = Loadable(
() => import(/* webpackChunkName: "Ingestion Settings" */ 'pages/Settings'), () => import(/* webpackChunkName: "Ingestion Settings" */ 'pages/Settings'),
); );
export const APIKeys = Loadable(
() => import(/* webpackChunkName: "All Settings" */ 'pages/Settings'),
);
export const MySettings = Loadable( export const MySettings = Loadable(
() => import(/* webpackChunkName: "All MySettings" */ 'pages/Settings'), () => import(/* webpackChunkName: "All MySettings" */ 'pages/Settings'),
); );

View File

@@ -513,7 +513,6 @@ export const oldRoutes = [
'/logs-save-views', '/logs-save-views',
'/traces-save-views', '/traces-save-views',
'/settings/access-tokens', '/settings/access-tokens',
'/settings/api-keys',
'/messaging-queues', '/messaging-queues',
'/alerts/edit', '/alerts/edit',
]; ];
@@ -524,8 +523,7 @@ export const oldNewRoutesMapping: Record<string, string> = {
'/logs-explorer/live': '/logs/logs-explorer/live', '/logs-explorer/live': '/logs/logs-explorer/live',
'/logs-save-views': '/logs/saved-views', '/logs-save-views': '/logs/saved-views',
'/traces-save-views': '/traces/saved-views', '/traces-save-views': '/traces/saved-views',
'/settings/access-tokens': '/settings/service-accounts', '/settings/access-tokens': '/settings/api-keys',
'/settings/api-keys': '/settings/service-accounts',
'/messaging-queues': '/messaging-queues/overview', '/messaging-queues': '/messaging-queues/overview',
'/alerts/edit': '/alerts/overview', '/alerts/edit': '/alerts/overview',
}; };

View File

@@ -1,7 +1,6 @@
import axios from 'api'; import axios from 'api';
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import { DEFAULT_TIME_RANGE } from 'container/TopNav/DateTimeSelectionV2/constants';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { CreatePublicDashboardProps } from 'types/api/dashboard/public/create'; import { CreatePublicDashboardProps } from 'types/api/dashboard/public/create';
@@ -9,7 +8,7 @@ const createPublicDashboard = async (
props: CreatePublicDashboardProps, props: CreatePublicDashboardProps,
): Promise<SuccessResponseV2<CreatePublicDashboardProps>> => { ): Promise<SuccessResponseV2<CreatePublicDashboardProps>> => {
const { dashboardId, timeRangeEnabled = false, defaultTimeRange = DEFAULT_TIME_RANGE } = props; const { dashboardId, timeRangeEnabled = false, defaultTimeRange = '30m' } = props;
try { try {
const response = await axios.post( const response = await axios.post(

View File

@@ -1,7 +1,6 @@
import axios from 'api'; import axios from 'api';
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import { DEFAULT_TIME_RANGE } from 'container/TopNav/DateTimeSelectionV2/constants';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { UpdatePublicDashboardProps } from 'types/api/dashboard/public/update'; import { UpdatePublicDashboardProps } from 'types/api/dashboard/public/update';
@@ -9,7 +8,7 @@ const updatePublicDashboard = async (
props: UpdatePublicDashboardProps, props: UpdatePublicDashboardProps,
): Promise<SuccessResponseV2<UpdatePublicDashboardProps>> => { ): Promise<SuccessResponseV2<UpdatePublicDashboardProps>> => {
const { dashboardId, timeRangeEnabled = false, defaultTimeRange = DEFAULT_TIME_RANGE } = props; const { dashboardId, timeRangeEnabled = false, defaultTimeRange = '30m' } = props;
try { try {
const response = await axios.put( const response = await axios.put(

File diff suppressed because it is too large Load Diff

View File

@@ -1,250 +0,0 @@
/**
* ! Do not edit manually
* * The file has been auto-generated using Orval for SigNoz
* * regenerate with 'yarn generate:api'
* SigNoz
*/
import type {
InvalidateOptions,
QueryClient,
QueryFunction,
QueryKey,
UseQueryOptions,
UseQueryResult,
} from 'react-query';
import { useQuery } from 'react-query';
import type { ErrorType } from '../../../generatedAPIInstance';
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
import type {
Healthz200,
Healthz503,
Livez200,
Readyz200,
Readyz503,
RenderErrorResponseDTO,
} from '../sigNoz.schemas';
/**
* @summary Health check
*/
export const healthz = (signal?: AbortSignal) => {
return GeneratedAPIInstance<Healthz200>({
url: `/api/v2/healthz`,
method: 'GET',
signal,
});
};
export const getHealthzQueryKey = () => {
return [`/api/v2/healthz`] as const;
};
export const getHealthzQueryOptions = <
TData = Awaited<ReturnType<typeof healthz>>,
TError = ErrorType<Healthz503>
>(options?: {
query?: UseQueryOptions<Awaited<ReturnType<typeof healthz>>, TError, TData>;
}) => {
const { query: queryOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getHealthzQueryKey();
const queryFn: QueryFunction<Awaited<ReturnType<typeof healthz>>> = ({
signal,
}) => healthz(signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof healthz>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type HealthzQueryResult = NonNullable<
Awaited<ReturnType<typeof healthz>>
>;
export type HealthzQueryError = ErrorType<Healthz503>;
/**
* @summary Health check
*/
export function useHealthz<
TData = Awaited<ReturnType<typeof healthz>>,
TError = ErrorType<Healthz503>
>(options?: {
query?: UseQueryOptions<Awaited<ReturnType<typeof healthz>>, TError, TData>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getHealthzQueryOptions(options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Health check
*/
export const invalidateHealthz = async (
queryClient: QueryClient,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getHealthzQueryKey() },
options,
);
return queryClient;
};
/**
* @summary Liveness check
*/
export const livez = (signal?: AbortSignal) => {
return GeneratedAPIInstance<Livez200>({
url: `/api/v2/livez`,
method: 'GET',
signal,
});
};
export const getLivezQueryKey = () => {
return [`/api/v2/livez`] as const;
};
export const getLivezQueryOptions = <
TData = Awaited<ReturnType<typeof livez>>,
TError = ErrorType<RenderErrorResponseDTO>
>(options?: {
query?: UseQueryOptions<Awaited<ReturnType<typeof livez>>, TError, TData>;
}) => {
const { query: queryOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getLivezQueryKey();
const queryFn: QueryFunction<Awaited<ReturnType<typeof livez>>> = ({
signal,
}) => livez(signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof livez>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type LivezQueryResult = NonNullable<Awaited<ReturnType<typeof livez>>>;
export type LivezQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Liveness check
*/
export function useLivez<
TData = Awaited<ReturnType<typeof livez>>,
TError = ErrorType<RenderErrorResponseDTO>
>(options?: {
query?: UseQueryOptions<Awaited<ReturnType<typeof livez>>, TError, TData>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getLivezQueryOptions(options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Liveness check
*/
export const invalidateLivez = async (
queryClient: QueryClient,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries({ queryKey: getLivezQueryKey() }, options);
return queryClient;
};
/**
* @summary Readiness check
*/
export const readyz = (signal?: AbortSignal) => {
return GeneratedAPIInstance<Readyz200>({
url: `/api/v2/readyz`,
method: 'GET',
signal,
});
};
export const getReadyzQueryKey = () => {
return [`/api/v2/readyz`] as const;
};
export const getReadyzQueryOptions = <
TData = Awaited<ReturnType<typeof readyz>>,
TError = ErrorType<Readyz503>
>(options?: {
query?: UseQueryOptions<Awaited<ReturnType<typeof readyz>>, TError, TData>;
}) => {
const { query: queryOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getReadyzQueryKey();
const queryFn: QueryFunction<Awaited<ReturnType<typeof readyz>>> = ({
signal,
}) => readyz(signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof readyz>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type ReadyzQueryResult = NonNullable<Awaited<ReturnType<typeof readyz>>>;
export type ReadyzQueryError = ErrorType<Readyz503>;
/**
* @summary Readiness check
*/
export function useReadyz<
TData = Awaited<ReturnType<typeof readyz>>,
TError = ErrorType<Readyz503>
>(options?: {
query?: UseQueryOptions<Awaited<ReturnType<typeof readyz>>, TError, TData>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getReadyzQueryOptions(options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Readiness check
*/
export const invalidateReadyz = async (
queryClient: QueryClient,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getReadyzQueryKey() },
options,
);
return queryClient;
};

View File

@@ -20,113 +20,11 @@ import { useMutation, useQuery } from 'react-query';
import type { BodyType, ErrorType } from '../../../generatedAPIInstance'; import type { BodyType, ErrorType } from '../../../generatedAPIInstance';
import { GeneratedAPIInstance } from '../../../generatedAPIInstance'; import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
import type { import type {
HandleExportRawDataPOSTParams,
ListPromotedAndIndexedPaths200, ListPromotedAndIndexedPaths200,
PromotetypesPromotePathDTO, PromotetypesPromotePathDTO,
Querybuildertypesv5QueryRangeRequestDTO,
RenderErrorResponseDTO, RenderErrorResponseDTO,
} from '../sigNoz.schemas'; } from '../sigNoz.schemas';
/**
* This endpoints allows complex query exporting raw data for traces and logs
* @summary Export raw data
*/
export const handleExportRawDataPOST = (
querybuildertypesv5QueryRangeRequestDTO: BodyType<Querybuildertypesv5QueryRangeRequestDTO>,
params?: HandleExportRawDataPOSTParams,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<string>({
url: `/api/v1/export_raw_data`,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: querybuildertypesv5QueryRangeRequestDTO,
params,
signal,
});
};
export const getHandleExportRawDataPOSTMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof handleExportRawDataPOST>>,
TError,
{
data: BodyType<Querybuildertypesv5QueryRangeRequestDTO>;
params?: HandleExportRawDataPOSTParams;
},
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof handleExportRawDataPOST>>,
TError,
{
data: BodyType<Querybuildertypesv5QueryRangeRequestDTO>;
params?: HandleExportRawDataPOSTParams;
},
TContext
> => {
const mutationKey = ['handleExportRawDataPOST'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof handleExportRawDataPOST>>,
{
data: BodyType<Querybuildertypesv5QueryRangeRequestDTO>;
params?: HandleExportRawDataPOSTParams;
}
> = (props) => {
const { data, params } = props ?? {};
return handleExportRawDataPOST(data, params);
};
return { mutationFn, ...mutationOptions };
};
export type HandleExportRawDataPOSTMutationResult = NonNullable<
Awaited<ReturnType<typeof handleExportRawDataPOST>>
>;
export type HandleExportRawDataPOSTMutationBody = BodyType<Querybuildertypesv5QueryRangeRequestDTO>;
export type HandleExportRawDataPOSTMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Export raw data
*/
export const useHandleExportRawDataPOST = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof handleExportRawDataPOST>>,
TError,
{
data: BodyType<Querybuildertypesv5QueryRangeRequestDTO>;
params?: HandleExportRawDataPOSTParams;
},
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof handleExportRawDataPOST>>,
TError,
{
data: BodyType<Querybuildertypesv5QueryRangeRequestDTO>;
params?: HandleExportRawDataPOSTParams;
},
TContext
> => {
const mutationOptions = getHandleExportRawDataPOSTMutationOptions(options);
return useMutation(mutationOptions);
};
/** /**
* This endpoints promotes and indexes paths * This endpoints promotes and indexes paths
* @summary Promote and index paths * @summary Promote and index paths

View File

@@ -31,13 +31,10 @@ import type {
GetMetricHighlightsPathParameters, GetMetricHighlightsPathParameters,
GetMetricMetadata200, GetMetricMetadata200,
GetMetricMetadataPathParameters, GetMetricMetadataPathParameters,
GetMetricsOnboardingStatus200,
GetMetricsStats200, GetMetricsStats200,
GetMetricsTreemap200, GetMetricsTreemap200,
InspectMetrics200,
ListMetrics200, ListMetrics200,
ListMetricsParams, ListMetricsParams,
MetricsexplorertypesInspectMetricsRequestDTO,
MetricsexplorertypesStatsRequestDTO, MetricsexplorertypesStatsRequestDTO,
MetricsexplorertypesTreemapRequestDTO, MetricsexplorertypesTreemapRequestDTO,
MetricsexplorertypesUpdateMetricMetadataRequestDTO, MetricsexplorertypesUpdateMetricMetadataRequestDTO,
@@ -781,176 +778,6 @@ export const useUpdateMetricMetadata = <
return useMutation(mutationOptions); return useMutation(mutationOptions);
}; };
/**
* Returns raw time series data points for a metric within a time range (max 30 minutes). Each series includes labels and timestamp/value pairs.
* @summary Inspect raw metric data points
*/
export const inspectMetrics = (
metricsexplorertypesInspectMetricsRequestDTO: BodyType<MetricsexplorertypesInspectMetricsRequestDTO>,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<InspectMetrics200>({
url: `/api/v2/metrics/inspect`,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: metricsexplorertypesInspectMetricsRequestDTO,
signal,
});
};
export const getInspectMetricsMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof inspectMetrics>>,
TError,
{ data: BodyType<MetricsexplorertypesInspectMetricsRequestDTO> },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof inspectMetrics>>,
TError,
{ data: BodyType<MetricsexplorertypesInspectMetricsRequestDTO> },
TContext
> => {
const mutationKey = ['inspectMetrics'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof inspectMetrics>>,
{ data: BodyType<MetricsexplorertypesInspectMetricsRequestDTO> }
> = (props) => {
const { data } = props ?? {};
return inspectMetrics(data);
};
return { mutationFn, ...mutationOptions };
};
export type InspectMetricsMutationResult = NonNullable<
Awaited<ReturnType<typeof inspectMetrics>>
>;
export type InspectMetricsMutationBody = BodyType<MetricsexplorertypesInspectMetricsRequestDTO>;
export type InspectMetricsMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Inspect raw metric data points
*/
export const useInspectMetrics = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof inspectMetrics>>,
TError,
{ data: BodyType<MetricsexplorertypesInspectMetricsRequestDTO> },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof inspectMetrics>>,
TError,
{ data: BodyType<MetricsexplorertypesInspectMetricsRequestDTO> },
TContext
> => {
const mutationOptions = getInspectMetricsMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Lightweight endpoint that checks if any non-SigNoz metrics have been ingested, used for onboarding status detection
* @summary Check if non-SigNoz metrics have been received
*/
export const getMetricsOnboardingStatus = (signal?: AbortSignal) => {
return GeneratedAPIInstance<GetMetricsOnboardingStatus200>({
url: `/api/v2/metrics/onboarding`,
method: 'GET',
signal,
});
};
export const getGetMetricsOnboardingStatusQueryKey = () => {
return [`/api/v2/metrics/onboarding`] as const;
};
export const getGetMetricsOnboardingStatusQueryOptions = <
TData = Awaited<ReturnType<typeof getMetricsOnboardingStatus>>,
TError = ErrorType<RenderErrorResponseDTO>
>(options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getMetricsOnboardingStatus>>,
TError,
TData
>;
}) => {
const { query: queryOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ?? getGetMetricsOnboardingStatusQueryKey();
const queryFn: QueryFunction<
Awaited<ReturnType<typeof getMetricsOnboardingStatus>>
> = ({ signal }) => getMetricsOnboardingStatus(signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof getMetricsOnboardingStatus>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type GetMetricsOnboardingStatusQueryResult = NonNullable<
Awaited<ReturnType<typeof getMetricsOnboardingStatus>>
>;
export type GetMetricsOnboardingStatusQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Check if non-SigNoz metrics have been received
*/
export function useGetMetricsOnboardingStatus<
TData = Awaited<ReturnType<typeof getMetricsOnboardingStatus>>,
TError = ErrorType<RenderErrorResponseDTO>
>(options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getMetricsOnboardingStatus>>,
TError,
TData
>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetMetricsOnboardingStatusQueryOptions(options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Check if non-SigNoz metrics have been received
*/
export const invalidateGetMetricsOnboardingStatus = async (
queryClient: QueryClient,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetMetricsOnboardingStatusQueryKey() },
options,
);
return queryClient;
};
/** /**
* This endpoint provides list of metrics with their number of samples and timeseries for the given time range * This endpoint provides list of metrics with their number of samples and timeseries for the given time range
* @summary Get metrics statistics * @summary Get metrics statistics

View File

@@ -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
> => { > => {

View File

@@ -1,744 +0,0 @@
/**
* ! Do not edit manually
* * The file has been auto-generated using Orval for SigNoz
* * regenerate with 'yarn generate:api'
* SigNoz
*/
import type {
InvalidateOptions,
QueryClient,
QueryFunction,
QueryKey,
UseQueryOptions,
UseQueryResult,
} from 'react-query';
import { useQuery } from 'react-query';
import type { ErrorType } from '../../../generatedAPIInstance';
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
import type {
GetRuleHistoryFilterKeys200,
GetRuleHistoryFilterKeysParams,
GetRuleHistoryFilterKeysPathParameters,
GetRuleHistoryFilterValues200,
GetRuleHistoryFilterValuesParams,
GetRuleHistoryFilterValuesPathParameters,
GetRuleHistoryOverallStatus200,
GetRuleHistoryOverallStatusParams,
GetRuleHistoryOverallStatusPathParameters,
GetRuleHistoryStats200,
GetRuleHistoryStatsParams,
GetRuleHistoryStatsPathParameters,
GetRuleHistoryTimeline200,
GetRuleHistoryTimelineParams,
GetRuleHistoryTimelinePathParameters,
GetRuleHistoryTopContributors200,
GetRuleHistoryTopContributorsParams,
GetRuleHistoryTopContributorsPathParameters,
RenderErrorResponseDTO,
} from '../sigNoz.schemas';
/**
* Returns distinct label keys from rule history entries for the selected range.
* @summary Get rule history filter keys
*/
export const getRuleHistoryFilterKeys = (
{ id }: GetRuleHistoryFilterKeysPathParameters,
params?: GetRuleHistoryFilterKeysParams,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<GetRuleHistoryFilterKeys200>({
url: `/api/v2/rules/${id}/history/filter_keys`,
method: 'GET',
params,
signal,
});
};
export const getGetRuleHistoryFilterKeysQueryKey = (
{ id }: GetRuleHistoryFilterKeysPathParameters,
params?: GetRuleHistoryFilterKeysParams,
) => {
return [
`/api/v2/rules/${id}/history/filter_keys`,
...(params ? [params] : []),
] as const;
};
export const getGetRuleHistoryFilterKeysQueryOptions = <
TData = Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetRuleHistoryFilterKeysPathParameters,
params?: GetRuleHistoryFilterKeysParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ?? getGetRuleHistoryFilterKeysQueryKey({ id }, params);
const queryFn: QueryFunction<
Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>
> = ({ signal }) => getRuleHistoryFilterKeys({ id }, params, signal);
return {
queryKey,
queryFn,
enabled: !!id,
...queryOptions,
} as UseQueryOptions<
Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type GetRuleHistoryFilterKeysQueryResult = NonNullable<
Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>
>;
export type GetRuleHistoryFilterKeysQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Get rule history filter keys
*/
export function useGetRuleHistoryFilterKeys<
TData = Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetRuleHistoryFilterKeysPathParameters,
params?: GetRuleHistoryFilterKeysParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetRuleHistoryFilterKeysQueryOptions(
{ id },
params,
options,
);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Get rule history filter keys
*/
export const invalidateGetRuleHistoryFilterKeys = async (
queryClient: QueryClient,
{ id }: GetRuleHistoryFilterKeysPathParameters,
params?: GetRuleHistoryFilterKeysParams,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetRuleHistoryFilterKeysQueryKey({ id }, params) },
options,
);
return queryClient;
};
/**
* Returns distinct label values for a given key from rule history entries.
* @summary Get rule history filter values
*/
export const getRuleHistoryFilterValues = (
{ id }: GetRuleHistoryFilterValuesPathParameters,
params?: GetRuleHistoryFilterValuesParams,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<GetRuleHistoryFilterValues200>({
url: `/api/v2/rules/${id}/history/filter_values`,
method: 'GET',
params,
signal,
});
};
export const getGetRuleHistoryFilterValuesQueryKey = (
{ id }: GetRuleHistoryFilterValuesPathParameters,
params?: GetRuleHistoryFilterValuesParams,
) => {
return [
`/api/v2/rules/${id}/history/filter_values`,
...(params ? [params] : []),
] as const;
};
export const getGetRuleHistoryFilterValuesQueryOptions = <
TData = Awaited<ReturnType<typeof getRuleHistoryFilterValues>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetRuleHistoryFilterValuesPathParameters,
params?: GetRuleHistoryFilterValuesParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getRuleHistoryFilterValues>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ??
getGetRuleHistoryFilterValuesQueryKey({ id }, params);
const queryFn: QueryFunction<
Awaited<ReturnType<typeof getRuleHistoryFilterValues>>
> = ({ signal }) => getRuleHistoryFilterValues({ id }, params, signal);
return {
queryKey,
queryFn,
enabled: !!id,
...queryOptions,
} as UseQueryOptions<
Awaited<ReturnType<typeof getRuleHistoryFilterValues>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type GetRuleHistoryFilterValuesQueryResult = NonNullable<
Awaited<ReturnType<typeof getRuleHistoryFilterValues>>
>;
export type GetRuleHistoryFilterValuesQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Get rule history filter values
*/
export function useGetRuleHistoryFilterValues<
TData = Awaited<ReturnType<typeof getRuleHistoryFilterValues>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetRuleHistoryFilterValuesPathParameters,
params?: GetRuleHistoryFilterValuesParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getRuleHistoryFilterValues>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetRuleHistoryFilterValuesQueryOptions(
{ id },
params,
options,
);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Get rule history filter values
*/
export const invalidateGetRuleHistoryFilterValues = async (
queryClient: QueryClient,
{ id }: GetRuleHistoryFilterValuesPathParameters,
params?: GetRuleHistoryFilterValuesParams,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetRuleHistoryFilterValuesQueryKey({ id }, params) },
options,
);
return queryClient;
};
/**
* Returns overall firing/inactive intervals for a rule in the selected time range.
* @summary Get rule overall status timeline
*/
export const getRuleHistoryOverallStatus = (
{ id }: GetRuleHistoryOverallStatusPathParameters,
params: GetRuleHistoryOverallStatusParams,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<GetRuleHistoryOverallStatus200>({
url: `/api/v2/rules/${id}/history/overall_status`,
method: 'GET',
params,
signal,
});
};
export const getGetRuleHistoryOverallStatusQueryKey = (
{ id }: GetRuleHistoryOverallStatusPathParameters,
params?: GetRuleHistoryOverallStatusParams,
) => {
return [
`/api/v2/rules/${id}/history/overall_status`,
...(params ? [params] : []),
] as const;
};
export const getGetRuleHistoryOverallStatusQueryOptions = <
TData = Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetRuleHistoryOverallStatusPathParameters,
params: GetRuleHistoryOverallStatusParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ??
getGetRuleHistoryOverallStatusQueryKey({ id }, params);
const queryFn: QueryFunction<
Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>
> = ({ signal }) => getRuleHistoryOverallStatus({ id }, params, signal);
return {
queryKey,
queryFn,
enabled: !!id,
...queryOptions,
} as UseQueryOptions<
Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type GetRuleHistoryOverallStatusQueryResult = NonNullable<
Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>
>;
export type GetRuleHistoryOverallStatusQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Get rule overall status timeline
*/
export function useGetRuleHistoryOverallStatus<
TData = Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetRuleHistoryOverallStatusPathParameters,
params: GetRuleHistoryOverallStatusParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetRuleHistoryOverallStatusQueryOptions(
{ id },
params,
options,
);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Get rule overall status timeline
*/
export const invalidateGetRuleHistoryOverallStatus = async (
queryClient: QueryClient,
{ id }: GetRuleHistoryOverallStatusPathParameters,
params: GetRuleHistoryOverallStatusParams,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetRuleHistoryOverallStatusQueryKey({ id }, params) },
options,
);
return queryClient;
};
/**
* Returns trigger and resolution statistics for a rule in the selected time range.
* @summary Get rule history stats
*/
export const getRuleHistoryStats = (
{ id }: GetRuleHistoryStatsPathParameters,
params: GetRuleHistoryStatsParams,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<GetRuleHistoryStats200>({
url: `/api/v2/rules/${id}/history/stats`,
method: 'GET',
params,
signal,
});
};
export const getGetRuleHistoryStatsQueryKey = (
{ id }: GetRuleHistoryStatsPathParameters,
params?: GetRuleHistoryStatsParams,
) => {
return [
`/api/v2/rules/${id}/history/stats`,
...(params ? [params] : []),
] as const;
};
export const getGetRuleHistoryStatsQueryOptions = <
TData = Awaited<ReturnType<typeof getRuleHistoryStats>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetRuleHistoryStatsPathParameters,
params: GetRuleHistoryStatsParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getRuleHistoryStats>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ?? getGetRuleHistoryStatsQueryKey({ id }, params);
const queryFn: QueryFunction<
Awaited<ReturnType<typeof getRuleHistoryStats>>
> = ({ signal }) => getRuleHistoryStats({ id }, params, signal);
return {
queryKey,
queryFn,
enabled: !!id,
...queryOptions,
} as UseQueryOptions<
Awaited<ReturnType<typeof getRuleHistoryStats>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type GetRuleHistoryStatsQueryResult = NonNullable<
Awaited<ReturnType<typeof getRuleHistoryStats>>
>;
export type GetRuleHistoryStatsQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Get rule history stats
*/
export function useGetRuleHistoryStats<
TData = Awaited<ReturnType<typeof getRuleHistoryStats>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetRuleHistoryStatsPathParameters,
params: GetRuleHistoryStatsParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getRuleHistoryStats>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetRuleHistoryStatsQueryOptions(
{ id },
params,
options,
);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Get rule history stats
*/
export const invalidateGetRuleHistoryStats = async (
queryClient: QueryClient,
{ id }: GetRuleHistoryStatsPathParameters,
params: GetRuleHistoryStatsParams,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetRuleHistoryStatsQueryKey({ id }, params) },
options,
);
return queryClient;
};
/**
* Returns paginated timeline entries for rule state transitions.
* @summary Get rule history timeline
*/
export const getRuleHistoryTimeline = (
{ id }: GetRuleHistoryTimelinePathParameters,
params: GetRuleHistoryTimelineParams,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<GetRuleHistoryTimeline200>({
url: `/api/v2/rules/${id}/history/timeline`,
method: 'GET',
params,
signal,
});
};
export const getGetRuleHistoryTimelineQueryKey = (
{ id }: GetRuleHistoryTimelinePathParameters,
params?: GetRuleHistoryTimelineParams,
) => {
return [
`/api/v2/rules/${id}/history/timeline`,
...(params ? [params] : []),
] as const;
};
export const getGetRuleHistoryTimelineQueryOptions = <
TData = Awaited<ReturnType<typeof getRuleHistoryTimeline>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetRuleHistoryTimelinePathParameters,
params: GetRuleHistoryTimelineParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getRuleHistoryTimeline>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ?? getGetRuleHistoryTimelineQueryKey({ id }, params);
const queryFn: QueryFunction<
Awaited<ReturnType<typeof getRuleHistoryTimeline>>
> = ({ signal }) => getRuleHistoryTimeline({ id }, params, signal);
return {
queryKey,
queryFn,
enabled: !!id,
...queryOptions,
} as UseQueryOptions<
Awaited<ReturnType<typeof getRuleHistoryTimeline>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type GetRuleHistoryTimelineQueryResult = NonNullable<
Awaited<ReturnType<typeof getRuleHistoryTimeline>>
>;
export type GetRuleHistoryTimelineQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Get rule history timeline
*/
export function useGetRuleHistoryTimeline<
TData = Awaited<ReturnType<typeof getRuleHistoryTimeline>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetRuleHistoryTimelinePathParameters,
params: GetRuleHistoryTimelineParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getRuleHistoryTimeline>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetRuleHistoryTimelineQueryOptions(
{ id },
params,
options,
);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Get rule history timeline
*/
export const invalidateGetRuleHistoryTimeline = async (
queryClient: QueryClient,
{ id }: GetRuleHistoryTimelinePathParameters,
params: GetRuleHistoryTimelineParams,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetRuleHistoryTimelineQueryKey({ id }, params) },
options,
);
return queryClient;
};
/**
* Returns top label combinations contributing to rule firing in the selected time range.
* @summary Get top contributors to rule firing
*/
export const getRuleHistoryTopContributors = (
{ id }: GetRuleHistoryTopContributorsPathParameters,
params: GetRuleHistoryTopContributorsParams,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<GetRuleHistoryTopContributors200>({
url: `/api/v2/rules/${id}/history/top_contributors`,
method: 'GET',
params,
signal,
});
};
export const getGetRuleHistoryTopContributorsQueryKey = (
{ id }: GetRuleHistoryTopContributorsPathParameters,
params?: GetRuleHistoryTopContributorsParams,
) => {
return [
`/api/v2/rules/${id}/history/top_contributors`,
...(params ? [params] : []),
] as const;
};
export const getGetRuleHistoryTopContributorsQueryOptions = <
TData = Awaited<ReturnType<typeof getRuleHistoryTopContributors>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetRuleHistoryTopContributorsPathParameters,
params: GetRuleHistoryTopContributorsParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getRuleHistoryTopContributors>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ??
getGetRuleHistoryTopContributorsQueryKey({ id }, params);
const queryFn: QueryFunction<
Awaited<ReturnType<typeof getRuleHistoryTopContributors>>
> = ({ signal }) => getRuleHistoryTopContributors({ id }, params, signal);
return {
queryKey,
queryFn,
enabled: !!id,
...queryOptions,
} as UseQueryOptions<
Awaited<ReturnType<typeof getRuleHistoryTopContributors>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type GetRuleHistoryTopContributorsQueryResult = NonNullable<
Awaited<ReturnType<typeof getRuleHistoryTopContributors>>
>;
export type GetRuleHistoryTopContributorsQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Get top contributors to rule firing
*/
export function useGetRuleHistoryTopContributors<
TData = Awaited<ReturnType<typeof getRuleHistoryTopContributors>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetRuleHistoryTopContributorsPathParameters,
params: GetRuleHistoryTopContributorsParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getRuleHistoryTopContributors>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetRuleHistoryTopContributorsQueryOptions(
{ id },
params,
options,
);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Get top contributors to rule firing
*/
export const invalidateGetRuleHistoryTopContributors = async (
queryClient: QueryClient,
{ id }: GetRuleHistoryTopContributorsPathParameters,
params: GetRuleHistoryTopContributorsParams,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetRuleHistoryTopContributorsQueryKey({ id }, params) },
options,
);
return queryClient;
};

View File

@@ -23,15 +23,9 @@ import type {
CreateServiceAccount201, CreateServiceAccount201,
CreateServiceAccountKey201, CreateServiceAccountKey201,
CreateServiceAccountKeyPathParameters, CreateServiceAccountKeyPathParameters,
CreateServiceAccountRole201,
CreateServiceAccountRolePathParameters,
DeleteServiceAccountPathParameters, DeleteServiceAccountPathParameters,
DeleteServiceAccountRolePathParameters,
GetMyServiceAccount200,
GetServiceAccount200, GetServiceAccount200,
GetServiceAccountPathParameters, GetServiceAccountPathParameters,
GetServiceAccountRoles200,
GetServiceAccountRolesPathParameters,
ListServiceAccountKeys200, ListServiceAccountKeys200,
ListServiceAccountKeysPathParameters, ListServiceAccountKeysPathParameters,
ListServiceAccounts200, ListServiceAccounts200,
@@ -39,10 +33,12 @@ import type {
RevokeServiceAccountKeyPathParameters, RevokeServiceAccountKeyPathParameters,
ServiceaccounttypesPostableFactorAPIKeyDTO, ServiceaccounttypesPostableFactorAPIKeyDTO,
ServiceaccounttypesPostableServiceAccountDTO, ServiceaccounttypesPostableServiceAccountDTO,
ServiceaccounttypesPostableServiceAccountRoleDTO,
ServiceaccounttypesUpdatableFactorAPIKeyDTO, ServiceaccounttypesUpdatableFactorAPIKeyDTO,
ServiceaccounttypesUpdatableServiceAccountDTO,
ServiceaccounttypesUpdatableServiceAccountStatusDTO,
UpdateServiceAccountKeyPathParameters, UpdateServiceAccountKeyPathParameters,
UpdateServiceAccountPathParameters, UpdateServiceAccountPathParameters,
UpdateServiceAccountStatusPathParameters,
} from '../sigNoz.schemas'; } from '../sigNoz.schemas';
/** /**
@@ -403,13 +399,13 @@ export const invalidateGetServiceAccount = async (
*/ */
export const updateServiceAccount = ( export const updateServiceAccount = (
{ id }: UpdateServiceAccountPathParameters, { id }: UpdateServiceAccountPathParameters,
serviceaccounttypesPostableServiceAccountDTO: BodyType<ServiceaccounttypesPostableServiceAccountDTO>, serviceaccounttypesUpdatableServiceAccountDTO: BodyType<ServiceaccounttypesUpdatableServiceAccountDTO>,
) => { ) => {
return GeneratedAPIInstance<string>({ return GeneratedAPIInstance<string>({
url: `/api/v1/service_accounts/${id}`, url: `/api/v1/service_accounts/${id}`,
method: 'PUT', method: 'PUT',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
data: serviceaccounttypesPostableServiceAccountDTO, data: serviceaccounttypesUpdatableServiceAccountDTO,
}); });
}; };
@@ -422,7 +418,7 @@ export const getUpdateServiceAccountMutationOptions = <
TError, TError,
{ {
pathParams: UpdateServiceAccountPathParameters; pathParams: UpdateServiceAccountPathParameters;
data: BodyType<ServiceaccounttypesPostableServiceAccountDTO>; data: BodyType<ServiceaccounttypesUpdatableServiceAccountDTO>;
}, },
TContext TContext
>; >;
@@ -431,7 +427,7 @@ export const getUpdateServiceAccountMutationOptions = <
TError, TError,
{ {
pathParams: UpdateServiceAccountPathParameters; pathParams: UpdateServiceAccountPathParameters;
data: BodyType<ServiceaccounttypesPostableServiceAccountDTO>; data: BodyType<ServiceaccounttypesUpdatableServiceAccountDTO>;
}, },
TContext TContext
> => { > => {
@@ -448,7 +444,7 @@ export const getUpdateServiceAccountMutationOptions = <
Awaited<ReturnType<typeof updateServiceAccount>>, Awaited<ReturnType<typeof updateServiceAccount>>,
{ {
pathParams: UpdateServiceAccountPathParameters; pathParams: UpdateServiceAccountPathParameters;
data: BodyType<ServiceaccounttypesPostableServiceAccountDTO>; data: BodyType<ServiceaccounttypesUpdatableServiceAccountDTO>;
} }
> = (props) => { > = (props) => {
const { pathParams, data } = props ?? {}; const { pathParams, data } = props ?? {};
@@ -462,7 +458,7 @@ export const getUpdateServiceAccountMutationOptions = <
export type UpdateServiceAccountMutationResult = NonNullable< export type UpdateServiceAccountMutationResult = NonNullable<
Awaited<ReturnType<typeof updateServiceAccount>> Awaited<ReturnType<typeof updateServiceAccount>>
>; >;
export type UpdateServiceAccountMutationBody = BodyType<ServiceaccounttypesPostableServiceAccountDTO>; export type UpdateServiceAccountMutationBody = BodyType<ServiceaccounttypesUpdatableServiceAccountDTO>;
export type UpdateServiceAccountMutationError = ErrorType<RenderErrorResponseDTO>; export type UpdateServiceAccountMutationError = ErrorType<RenderErrorResponseDTO>;
/** /**
@@ -477,7 +473,7 @@ export const useUpdateServiceAccount = <
TError, TError,
{ {
pathParams: UpdateServiceAccountPathParameters; pathParams: UpdateServiceAccountPathParameters;
data: BodyType<ServiceaccounttypesPostableServiceAccountDTO>; data: BodyType<ServiceaccounttypesUpdatableServiceAccountDTO>;
}, },
TContext TContext
>; >;
@@ -486,7 +482,7 @@ export const useUpdateServiceAccount = <
TError, TError,
{ {
pathParams: UpdateServiceAccountPathParameters; pathParams: UpdateServiceAccountPathParameters;
data: BodyType<ServiceaccounttypesPostableServiceAccountDTO>; data: BodyType<ServiceaccounttypesUpdatableServiceAccountDTO>;
}, },
TContext TContext
> => { > => {
@@ -875,150 +871,44 @@ export const useUpdateServiceAccountKey = <
return useMutation(mutationOptions); return useMutation(mutationOptions);
}; };
/** /**
* This endpoint gets all the roles for the existing service account * This endpoint updates an existing service account status
* @summary Gets service account roles * @summary Updates a service account status
*/ */
export const getServiceAccountRoles = ( export const updateServiceAccountStatus = (
{ id }: GetServiceAccountRolesPathParameters, { id }: UpdateServiceAccountStatusPathParameters,
signal?: AbortSignal, serviceaccounttypesUpdatableServiceAccountStatusDTO: BodyType<ServiceaccounttypesUpdatableServiceAccountStatusDTO>,
) => { ) => {
return GeneratedAPIInstance<GetServiceAccountRoles200>({ return GeneratedAPIInstance<string>({
url: `/api/v1/service_accounts/${id}/roles`, url: `/api/v1/service_accounts/${id}/status`,
method: 'GET', method: 'PUT',
signal,
});
};
export const getGetServiceAccountRolesQueryKey = ({
id,
}: GetServiceAccountRolesPathParameters) => {
return [`/api/v1/service_accounts/${id}/roles`] as const;
};
export const getGetServiceAccountRolesQueryOptions = <
TData = Awaited<ReturnType<typeof getServiceAccountRoles>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetServiceAccountRolesPathParameters,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getServiceAccountRoles>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ?? getGetServiceAccountRolesQueryKey({ id });
const queryFn: QueryFunction<
Awaited<ReturnType<typeof getServiceAccountRoles>>
> = ({ signal }) => getServiceAccountRoles({ id }, signal);
return {
queryKey,
queryFn,
enabled: !!id,
...queryOptions,
} as UseQueryOptions<
Awaited<ReturnType<typeof getServiceAccountRoles>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type GetServiceAccountRolesQueryResult = NonNullable<
Awaited<ReturnType<typeof getServiceAccountRoles>>
>;
export type GetServiceAccountRolesQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Gets service account roles
*/
export function useGetServiceAccountRoles<
TData = Awaited<ReturnType<typeof getServiceAccountRoles>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetServiceAccountRolesPathParameters,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getServiceAccountRoles>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetServiceAccountRolesQueryOptions({ id }, options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Gets service account roles
*/
export const invalidateGetServiceAccountRoles = async (
queryClient: QueryClient,
{ id }: GetServiceAccountRolesPathParameters,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetServiceAccountRolesQueryKey({ id }) },
options,
);
return queryClient;
};
/**
* This endpoint assigns a role to a service account
* @summary Create service account role
*/
export const createServiceAccountRole = (
{ id }: CreateServiceAccountRolePathParameters,
serviceaccounttypesPostableServiceAccountRoleDTO: BodyType<ServiceaccounttypesPostableServiceAccountRoleDTO>,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<CreateServiceAccountRole201>({
url: `/api/v1/service_accounts/${id}/roles`,
method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
data: serviceaccounttypesPostableServiceAccountRoleDTO, data: serviceaccounttypesUpdatableServiceAccountStatusDTO,
signal,
}); });
}; };
export const getCreateServiceAccountRoleMutationOptions = < export const getUpdateServiceAccountStatusMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>, TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown TContext = unknown
>(options?: { >(options?: {
mutation?: UseMutationOptions< mutation?: UseMutationOptions<
Awaited<ReturnType<typeof createServiceAccountRole>>, Awaited<ReturnType<typeof updateServiceAccountStatus>>,
TError, TError,
{ {
pathParams: CreateServiceAccountRolePathParameters; pathParams: UpdateServiceAccountStatusPathParameters;
data: BodyType<ServiceaccounttypesPostableServiceAccountRoleDTO>; data: BodyType<ServiceaccounttypesUpdatableServiceAccountStatusDTO>;
}, },
TContext TContext
>; >;
}): UseMutationOptions< }): UseMutationOptions<
Awaited<ReturnType<typeof createServiceAccountRole>>, Awaited<ReturnType<typeof updateServiceAccountStatus>>,
TError, TError,
{ {
pathParams: CreateServiceAccountRolePathParameters; pathParams: UpdateServiceAccountStatusPathParameters;
data: BodyType<ServiceaccounttypesPostableServiceAccountRoleDTO>; data: BodyType<ServiceaccounttypesUpdatableServiceAccountStatusDTO>;
}, },
TContext TContext
> => { > => {
const mutationKey = ['createServiceAccountRole']; const mutationKey = ['updateServiceAccountStatus'];
const { mutation: mutationOptions } = options const { mutation: mutationOptions } = options
? options.mutation && ? options.mutation &&
'mutationKey' in options.mutation && 'mutationKey' in options.mutation &&
@@ -1028,299 +918,52 @@ export const getCreateServiceAccountRoleMutationOptions = <
: { mutation: { mutationKey } }; : { mutation: { mutationKey } };
const mutationFn: MutationFunction< const mutationFn: MutationFunction<
Awaited<ReturnType<typeof createServiceAccountRole>>, Awaited<ReturnType<typeof updateServiceAccountStatus>>,
{ {
pathParams: CreateServiceAccountRolePathParameters; pathParams: UpdateServiceAccountStatusPathParameters;
data: BodyType<ServiceaccounttypesPostableServiceAccountRoleDTO>; data: BodyType<ServiceaccounttypesUpdatableServiceAccountStatusDTO>;
} }
> = (props) => { > = (props) => {
const { pathParams, data } = props ?? {}; const { pathParams, data } = props ?? {};
return createServiceAccountRole(pathParams, data); return updateServiceAccountStatus(pathParams, data);
}; };
return { mutationFn, ...mutationOptions }; return { mutationFn, ...mutationOptions };
}; };
export type CreateServiceAccountRoleMutationResult = NonNullable< export type UpdateServiceAccountStatusMutationResult = NonNullable<
Awaited<ReturnType<typeof createServiceAccountRole>> Awaited<ReturnType<typeof updateServiceAccountStatus>>
>; >;
export type CreateServiceAccountRoleMutationBody = BodyType<ServiceaccounttypesPostableServiceAccountRoleDTO>; export type UpdateServiceAccountStatusMutationBody = BodyType<ServiceaccounttypesUpdatableServiceAccountStatusDTO>;
export type CreateServiceAccountRoleMutationError = ErrorType<RenderErrorResponseDTO>; export type UpdateServiceAccountStatusMutationError = ErrorType<RenderErrorResponseDTO>;
/** /**
* @summary Create service account role * @summary Updates a service account status
*/ */
export const useCreateServiceAccountRole = < export const useUpdateServiceAccountStatus = <
TError = ErrorType<RenderErrorResponseDTO>, TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown TContext = unknown
>(options?: { >(options?: {
mutation?: UseMutationOptions< mutation?: UseMutationOptions<
Awaited<ReturnType<typeof createServiceAccountRole>>, Awaited<ReturnType<typeof updateServiceAccountStatus>>,
TError, TError,
{ {
pathParams: CreateServiceAccountRolePathParameters; pathParams: UpdateServiceAccountStatusPathParameters;
data: BodyType<ServiceaccounttypesPostableServiceAccountRoleDTO>; data: BodyType<ServiceaccounttypesUpdatableServiceAccountStatusDTO>;
}, },
TContext TContext
>; >;
}): UseMutationResult< }): UseMutationResult<
Awaited<ReturnType<typeof createServiceAccountRole>>, Awaited<ReturnType<typeof updateServiceAccountStatus>>,
TError, TError,
{ {
pathParams: CreateServiceAccountRolePathParameters; pathParams: UpdateServiceAccountStatusPathParameters;
data: BodyType<ServiceaccounttypesPostableServiceAccountRoleDTO>; data: BodyType<ServiceaccounttypesUpdatableServiceAccountStatusDTO>;
}, },
TContext TContext
> => { > => {
const mutationOptions = getCreateServiceAccountRoleMutationOptions(options); const mutationOptions = getUpdateServiceAccountStatusMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* This endpoint revokes a role from service account
* @summary Delete service account role
*/
export const deleteServiceAccountRole = ({
id,
rid,
}: DeleteServiceAccountRolePathParameters) => {
return GeneratedAPIInstance<string>({
url: `/api/v1/service_accounts/${id}/roles/${rid}`,
method: 'DELETE',
});
};
export const getDeleteServiceAccountRoleMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof deleteServiceAccountRole>>,
TError,
{ pathParams: DeleteServiceAccountRolePathParameters },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof deleteServiceAccountRole>>,
TError,
{ pathParams: DeleteServiceAccountRolePathParameters },
TContext
> => {
const mutationKey = ['deleteServiceAccountRole'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof deleteServiceAccountRole>>,
{ pathParams: DeleteServiceAccountRolePathParameters }
> = (props) => {
const { pathParams } = props ?? {};
return deleteServiceAccountRole(pathParams);
};
return { mutationFn, ...mutationOptions };
};
export type DeleteServiceAccountRoleMutationResult = NonNullable<
Awaited<ReturnType<typeof deleteServiceAccountRole>>
>;
export type DeleteServiceAccountRoleMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Delete service account role
*/
export const useDeleteServiceAccountRole = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof deleteServiceAccountRole>>,
TError,
{ pathParams: DeleteServiceAccountRolePathParameters },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof deleteServiceAccountRole>>,
TError,
{ pathParams: DeleteServiceAccountRolePathParameters },
TContext
> => {
const mutationOptions = getDeleteServiceAccountRoleMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* This endpoint gets my service account
* @summary Gets my service account
*/
export const getMyServiceAccount = (signal?: AbortSignal) => {
return GeneratedAPIInstance<GetMyServiceAccount200>({
url: `/api/v1/service_accounts/me`,
method: 'GET',
signal,
});
};
export const getGetMyServiceAccountQueryKey = () => {
return [`/api/v1/service_accounts/me`] as const;
};
export const getGetMyServiceAccountQueryOptions = <
TData = Awaited<ReturnType<typeof getMyServiceAccount>>,
TError = ErrorType<RenderErrorResponseDTO>
>(options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getMyServiceAccount>>,
TError,
TData
>;
}) => {
const { query: queryOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getGetMyServiceAccountQueryKey();
const queryFn: QueryFunction<
Awaited<ReturnType<typeof getMyServiceAccount>>
> = ({ signal }) => getMyServiceAccount(signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof getMyServiceAccount>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type GetMyServiceAccountQueryResult = NonNullable<
Awaited<ReturnType<typeof getMyServiceAccount>>
>;
export type GetMyServiceAccountQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Gets my service account
*/
export function useGetMyServiceAccount<
TData = Awaited<ReturnType<typeof getMyServiceAccount>>,
TError = ErrorType<RenderErrorResponseDTO>
>(options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getMyServiceAccount>>,
TError,
TData
>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetMyServiceAccountQueryOptions(options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Gets my service account
*/
export const invalidateGetMyServiceAccount = async (
queryClient: QueryClient,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetMyServiceAccountQueryKey() },
options,
);
return queryClient;
};
/**
* This endpoint gets my service account
* @summary Updates my service account
*/
export const updateMyServiceAccount = (
serviceaccounttypesPostableServiceAccountDTO: BodyType<ServiceaccounttypesPostableServiceAccountDTO>,
) => {
return GeneratedAPIInstance<string>({
url: `/api/v1/service_accounts/me`,
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
data: serviceaccounttypesPostableServiceAccountDTO,
});
};
export const getUpdateMyServiceAccountMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof updateMyServiceAccount>>,
TError,
{ data: BodyType<ServiceaccounttypesPostableServiceAccountDTO> },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof updateMyServiceAccount>>,
TError,
{ data: BodyType<ServiceaccounttypesPostableServiceAccountDTO> },
TContext
> => {
const mutationKey = ['updateMyServiceAccount'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof updateMyServiceAccount>>,
{ data: BodyType<ServiceaccounttypesPostableServiceAccountDTO> }
> = (props) => {
const { data } = props ?? {};
return updateMyServiceAccount(data);
};
return { mutationFn, ...mutationOptions };
};
export type UpdateMyServiceAccountMutationResult = NonNullable<
Awaited<ReturnType<typeof updateMyServiceAccount>>
>;
export type UpdateMyServiceAccountMutationBody = BodyType<ServiceaccounttypesPostableServiceAccountDTO>;
export type UpdateMyServiceAccountMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Updates my service account
*/
export const useUpdateMyServiceAccount = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof updateMyServiceAccount>>,
TError,
{ data: BodyType<ServiceaccounttypesPostableServiceAccountDTO> },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof updateMyServiceAccount>>,
TError,
{ data: BodyType<ServiceaccounttypesPostableServiceAccountDTO> },
TContext
> => {
const mutationOptions = getUpdateMyServiceAccountMutationOptions(options);
return useMutation(mutationOptions); return useMutation(mutationOptions);
}; };

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -81,8 +81,7 @@ export const interceptorRejected = async (
response.config.url !== '/sessions/email_password' && response.config.url !== '/sessions/email_password' &&
!( !(
response.config.url === '/sessions' && response.config.method === 'delete' response.config.url === '/sessions' && response.config.method === 'delete'
) && )
response.config.url !== '/authz/check'
) { ) {
try { try {
const accessToken = getLocalStorageApi(LOCALSTORAGE.AUTH_TOKEN); const accessToken = getLocalStorageApi(LOCALSTORAGE.AUTH_TOKEN);

View File

@@ -1,152 +0,0 @@
import axios, { AxiosHeaders, AxiosResponse } from 'axios';
import { interceptorRejected } from './index';
jest.mock('api/browser/localstorage/get', () => ({
__esModule: true,
default: jest.fn(() => 'mock-token'),
}));
jest.mock('api/v2/sessions/rotate/post', () => ({
__esModule: true,
default: jest.fn(() =>
Promise.resolve({
data: { accessToken: 'new-token', refreshToken: 'new-refresh' },
}),
),
}));
jest.mock('AppRoutes/utils', () => ({
__esModule: true,
default: jest.fn(),
}));
jest.mock('axios', () => {
const actualAxios = jest.requireActual('axios');
const mockAxios = jest.fn().mockResolvedValue({ data: 'success' });
return {
...actualAxios,
default: Object.assign(mockAxios, {
...actualAxios.default,
isAxiosError: jest.fn().mockReturnValue(true),
create: actualAxios.create,
}),
__esModule: true,
};
});
describe('interceptorRejected', () => {
beforeEach(() => {
jest.clearAllMocks();
((axios as unknown) as jest.Mock).mockResolvedValue({ data: 'success' });
((axios.isAxiosError as unknown) as jest.Mock).mockReturnValue(true);
});
it('should preserve array payload structure when retrying a 401 request', async () => {
const arrayPayload = [
{ relation: 'assignee', object: { resource: { name: 'role' } } },
{ relation: 'assignee', object: { resource: { name: 'editor' } } },
];
const error = ({
response: {
status: 401,
config: {
url: '/some-endpoint',
method: 'POST',
baseURL: 'http://localhost/',
headers: new AxiosHeaders(),
data: JSON.stringify(arrayPayload),
},
},
config: {
url: '/some-endpoint',
method: 'POST',
baseURL: 'http://localhost/',
headers: new AxiosHeaders(),
data: JSON.stringify(arrayPayload),
},
} as unknown) as AxiosResponse;
try {
await interceptorRejected(error);
} catch {
// Expected to reject after retry
}
const mockAxiosFn = (axios as unknown) as jest.Mock;
expect(mockAxiosFn.mock.calls.length).toBe(1);
const retryCallConfig = mockAxiosFn.mock.calls[0][0];
expect(Array.isArray(JSON.parse(retryCallConfig.data))).toBe(true);
expect(JSON.parse(retryCallConfig.data)).toEqual(arrayPayload);
});
it('should preserve object payload structure when retrying a 401 request', async () => {
const objectPayload = { key: 'value', nested: { data: 123 } };
const error = ({
response: {
status: 401,
config: {
url: '/some-endpoint',
method: 'POST',
baseURL: 'http://localhost/',
headers: new AxiosHeaders(),
data: JSON.stringify(objectPayload),
},
},
config: {
url: '/some-endpoint',
method: 'POST',
baseURL: 'http://localhost/',
headers: new AxiosHeaders(),
data: JSON.stringify(objectPayload),
},
} as unknown) as AxiosResponse;
try {
await interceptorRejected(error);
} catch {
// Expected to reject after retry
}
const mockAxiosFn = (axios as unknown) as jest.Mock;
expect(mockAxiosFn.mock.calls.length).toBe(1);
const retryCallConfig = mockAxiosFn.mock.calls[0][0];
expect(JSON.parse(retryCallConfig.data)).toEqual(objectPayload);
});
it('should handle undefined data gracefully when retrying', async () => {
const error = ({
response: {
status: 401,
config: {
url: '/some-endpoint',
method: 'GET',
baseURL: 'http://localhost/',
headers: new AxiosHeaders(),
data: undefined,
},
},
config: {
url: '/some-endpoint',
method: 'GET',
baseURL: 'http://localhost/',
headers: new AxiosHeaders(),
data: undefined,
},
} as unknown) as AxiosResponse;
try {
await interceptorRejected(error);
} catch {
// Expected to reject after retry
}
const mockAxiosFn = (axios as unknown) as jest.Mock;
expect(mockAxiosFn.mock.calls.length).toBe(1);
const retryCallConfig = mockAxiosFn.mock.calls[0][0];
expect(retryCallConfig.data).toBeUndefined();
});
});

View File

@@ -0,0 +1,54 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
export interface InspectMetricsRequest {
metricName: string;
start: number;
end: number;
filters: TagFilter;
}
export interface InspectMetricsResponse {
status: string;
data: {
series: InspectMetricsSeries[];
};
}
export interface InspectMetricsSeries {
title?: string;
strokeColor?: string;
labels: Record<string, string>;
labelsArray: Array<Record<string, string>>;
values: InspectMetricsTimestampValue[];
}
interface InspectMetricsTimestampValue {
timestamp: number;
value: string;
}
export const getInspectMetricsDetails = async (
request: InspectMetricsRequest,
signal?: AbortSignal,
headers?: Record<string, string>,
): Promise<SuccessResponse<InspectMetricsResponse> | ErrorResponse> => {
try {
const response = await axios.post(`/metrics/inspect`, request, {
signal,
headers,
});
return {
statusCode: 200,
error: null,
message: 'Success',
payload: response.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};

View File

@@ -0,0 +1,75 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { MetricType } from './getMetricsList';
export interface MetricDetails {
name: string;
description: string;
type: string;
unit: string;
timeseries: number;
samples: number;
timeSeriesTotal: number;
timeSeriesActive: number;
lastReceived: string;
attributes: MetricDetailsAttribute[] | null;
metadata?: {
metric_type: MetricType;
description: string;
unit: string;
temporality?: Temporality;
};
alerts: MetricDetailsAlert[] | null;
dashboards: MetricDetailsDashboard[] | null;
}
export enum Temporality {
CUMULATIVE = 'Cumulative',
DELTA = 'Delta',
}
export interface MetricDetailsAttribute {
key: string;
value: string[];
valueCount: number;
}
export interface MetricDetailsAlert {
alert_name: string;
alert_id: string;
}
export interface MetricDetailsDashboard {
dashboard_name: string;
dashboard_id: string;
}
export interface MetricDetailsResponse {
status: string;
data: MetricDetails;
}
export const getMetricDetails = async (
metricName: string,
signal?: AbortSignal,
headers?: Record<string, string>,
): Promise<SuccessResponse<MetricDetailsResponse> | ErrorResponse> => {
try {
const response = await axios.get(`/metrics/${metricName}/metadata`, {
signal,
headers,
});
return {
statusCode: 200,
error: null,
message: 'Success',
payload: response.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};

View File

@@ -0,0 +1,67 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import {
OrderByPayload,
TreemapViewType,
} from 'container/MetricsExplorer/Summary/types';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
export interface MetricsListPayload {
filters: TagFilter;
groupBy?: BaseAutocompleteData[];
offset?: number;
limit?: number;
orderBy?: OrderByPayload;
}
export enum MetricType {
SUM = 'Sum',
GAUGE = 'Gauge',
HISTOGRAM = 'Histogram',
SUMMARY = 'Summary',
EXPONENTIAL_HISTOGRAM = 'ExponentialHistogram',
}
export interface MetricsListItemData {
metric_name: string;
description: string;
type: MetricType;
unit: string;
[TreemapViewType.TIMESERIES]: number;
[TreemapViewType.SAMPLES]: number;
lastReceived: string;
}
export interface MetricsListResponse {
status: string;
data: {
metrics: MetricsListItemData[];
total?: number;
};
}
export const getMetricsList = async (
props: MetricsListPayload,
signal?: AbortSignal,
headers?: Record<string, string>,
): Promise<SuccessResponse<MetricsListResponse> | ErrorResponse> => {
try {
const response = await axios.post('/metrics', props, {
signal,
headers,
});
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data,
params: props,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};

Some files were not shown because too many files have changed in this diff Show More