Merge branch 'funcs' into formula-eval

This commit is contained in:
Srikanth Chekuri 2025-06-03 01:26:25 +05:30 committed by GitHub
commit 08e8f053aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
108 changed files with 44677 additions and 641 deletions

1
.gitignore vendored
View File

@ -66,6 +66,7 @@ e2e/.auth
# go # go
vendor/ vendor/
**/main/** **/main/**
__debug_bin**
# git-town # git-town
.git-branches.toml .git-branches.toml

View File

@ -207,3 +207,11 @@ emailing:
key_file_path: key_file_path:
# The path to the certificate file. # The path to the certificate file.
cert_file_path: cert_file_path:
##################### Sharder (experimental) #####################
sharder:
# Specifies the sharder provider to use.
provider: noop
single:
# The org id to which this instance belongs to.
org_id: org_id

View File

@ -3,13 +3,15 @@ package httplicensing
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"github.com/SigNoz/signoz/ee/query-service/constants"
"time" "time"
"github.com/SigNoz/signoz/ee/query-service/constants"
"github.com/SigNoz/signoz/ee/licensing/licensingstore/sqllicensingstore" "github.com/SigNoz/signoz/ee/licensing/licensingstore/sqllicensingstore"
"github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/factory" "github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/licensing" "github.com/SigNoz/signoz/pkg/licensing"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/sqlstore" "github.com/SigNoz/signoz/pkg/sqlstore"
"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"
@ -19,23 +21,31 @@ import (
) )
type provider struct { type provider struct {
store licensetypes.Store store licensetypes.Store
zeus zeus.Zeus zeus zeus.Zeus
config licensing.Config config licensing.Config
settings factory.ScopedProviderSettings settings factory.ScopedProviderSettings
stopChan chan struct{} orgGetter organization.Getter
stopChan chan struct{}
} }
func NewProviderFactory(store sqlstore.SQLStore, zeus zeus.Zeus) factory.ProviderFactory[licensing.Licensing, licensing.Config] { func NewProviderFactory(store sqlstore.SQLStore, zeus zeus.Zeus, orgGetter organization.Getter) factory.ProviderFactory[licensing.Licensing, licensing.Config] {
return factory.NewProviderFactory(factory.MustNewName("http"), func(ctx context.Context, providerSettings factory.ProviderSettings, config licensing.Config) (licensing.Licensing, error) { return factory.NewProviderFactory(factory.MustNewName("http"), func(ctx context.Context, providerSettings factory.ProviderSettings, config licensing.Config) (licensing.Licensing, error) {
return New(ctx, providerSettings, config, store, zeus) return New(ctx, providerSettings, config, store, zeus, orgGetter)
}) })
} }
func New(ctx context.Context, ps factory.ProviderSettings, config licensing.Config, sqlstore sqlstore.SQLStore, zeus zeus.Zeus) (licensing.Licensing, error) { func New(ctx context.Context, ps factory.ProviderSettings, config licensing.Config, sqlstore sqlstore.SQLStore, zeus zeus.Zeus, orgGetter organization.Getter) (licensing.Licensing, error) {
settings := factory.NewScopedProviderSettings(ps, "github.com/SigNoz/signoz/ee/licensing/httplicensing") settings := factory.NewScopedProviderSettings(ps, "github.com/SigNoz/signoz/ee/licensing/httplicensing")
licensestore := sqllicensingstore.New(sqlstore) licensestore := sqllicensingstore.New(sqlstore)
return &provider{store: licensestore, zeus: zeus, config: config, settings: settings, stopChan: make(chan struct{})}, nil return &provider{
store: licensestore,
zeus: zeus,
config: config,
settings: settings,
orgGetter: orgGetter,
stopChan: make(chan struct{}),
}, nil
} }
func (provider *provider) Start(ctx context.Context) error { func (provider *provider) Start(ctx context.Context) error {
@ -67,13 +77,13 @@ func (provider *provider) Stop(ctx context.Context) error {
} }
func (provider *provider) Validate(ctx context.Context) error { func (provider *provider) Validate(ctx context.Context) error {
organizations, err := provider.store.ListOrganizations(ctx) organizations, err := provider.orgGetter.ListByOwnedKeyRange(ctx)
if err != nil { if err != nil {
return err return err
} }
for _, organizationID := range organizations { for _, organization := range organizations {
err := provider.Refresh(ctx, organizationID) err := provider.Refresh(ctx, organization.ID)
if err != nil { if err != nil {
return err return err
} }
@ -168,6 +178,11 @@ func (provider *provider) Refresh(ctx context.Context, organizationID valuer.UUI
return err return err
} }
err = provider.InitFeatures(ctx, activeLicense.Features)
if err != nil {
return err
}
return nil return nil
} }

View File

@ -5,7 +5,6 @@ import (
"github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/sqlstore" "github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types"
"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"
@ -82,31 +81,6 @@ func (store *store) Update(ctx context.Context, organizationID valuer.UUID, stor
return nil return nil
} }
func (store *store) ListOrganizations(ctx context.Context) ([]valuer.UUID, error) {
orgIDStrs := make([]string, 0)
err := store.sqlstore.
BunDB().
NewSelect().
Model(new(types.Organization)).
Column("id").
Scan(ctx, &orgIDStrs)
if err != nil {
return nil, err
}
orgIDs := make([]valuer.UUID, len(orgIDStrs))
for idx, orgIDStr := range orgIDStrs {
orgID, err := valuer.NewUUID(orgIDStr)
if err != nil {
return nil, err
}
orgIDs[idx] = orgID
}
return orgIDs, nil
}
func (store *store) CreateFeature(ctx context.Context, storableFeature *featuretypes.StorableFeature) error { func (store *store) CreateFeature(ctx context.Context, storableFeature *featuretypes.StorableFeature) error {
_, err := store. _, err := store.
sqlstore. sqlstore.

View File

@ -20,6 +20,7 @@ import (
"github.com/SigNoz/signoz/pkg/alertmanager" "github.com/SigNoz/signoz/pkg/alertmanager"
"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/prometheus" "github.com/SigNoz/signoz/pkg/prometheus"
"github.com/SigNoz/signoz/pkg/signoz" "github.com/SigNoz/signoz/pkg/signoz"
"github.com/SigNoz/signoz/pkg/sqlstore" "github.com/SigNoz/signoz/pkg/sqlstore"
@ -113,6 +114,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
serverOptions.SigNoz.SQLStore, serverOptions.SigNoz.SQLStore,
serverOptions.SigNoz.TelemetryStore, serverOptions.SigNoz.TelemetryStore,
serverOptions.SigNoz.Prometheus, serverOptions.SigNoz.Prometheus,
serverOptions.SigNoz.Modules.OrgGetter,
) )
if err != nil { if err != nil {
@ -157,7 +159,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
} }
// start the usagemanager // start the usagemanager
usageManager, err := usage.New(serverOptions.SigNoz.Licensing, serverOptions.SigNoz.TelemetryStore.ClickhouseDB(), serverOptions.SigNoz.Zeus, serverOptions.SigNoz.Modules.Organization) usageManager, err := usage.New(serverOptions.SigNoz.Licensing, serverOptions.SigNoz.TelemetryStore.ClickhouseDB(), serverOptions.SigNoz.Zeus, serverOptions.SigNoz.Modules.OrgGetter)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -225,7 +227,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
&opAmpModel.AllAgents, agentConfMgr, &opAmpModel.AllAgents, agentConfMgr,
) )
orgs, err := apiHandler.Signoz.Modules.Organization.GetAll(context.Background()) orgs, err := apiHandler.Signoz.Modules.OrgGetter.ListByOwnedKeyRange(context.Background())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -240,11 +242,10 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
} }
func (s *Server) createPrivateServer(apiHandler *api.APIHandler) (*http.Server, error) { func (s *Server) createPrivateServer(apiHandler *api.APIHandler) (*http.Server, error) {
r := baseapp.NewRouter() r := baseapp.NewRouter()
r.Use(middleware.NewAuth(s.serverOptions.Jwt, []string{"Authorization", "Sec-WebSocket-Protocol"}).Wrap) r.Use(middleware.NewAuth(s.serverOptions.Jwt, []string{"Authorization", "Sec-WebSocket-Protocol"}, s.serverOptions.SigNoz.Sharder, s.serverOptions.SigNoz.Instrumentation.Logger()).Wrap)
r.Use(middleware.NewAPIKey(s.serverOptions.SigNoz.SQLStore, []string{"SIGNOZ-API-KEY"}, s.serverOptions.SigNoz.Instrumentation.Logger()).Wrap) r.Use(middleware.NewAPIKey(s.serverOptions.SigNoz.SQLStore, []string{"SIGNOZ-API-KEY"}, s.serverOptions.SigNoz.Instrumentation.Logger(), s.serverOptions.SigNoz.Sharder).Wrap)
r.Use(middleware.NewTimeout(s.serverOptions.SigNoz.Instrumentation.Logger(), r.Use(middleware.NewTimeout(s.serverOptions.SigNoz.Instrumentation.Logger(),
s.serverOptions.Config.APIServer.Timeout.ExcludedRoutes, s.serverOptions.Config.APIServer.Timeout.ExcludedRoutes,
s.serverOptions.Config.APIServer.Timeout.Default, s.serverOptions.Config.APIServer.Timeout.Default,
@ -275,8 +276,8 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*h
r := baseapp.NewRouter() r := baseapp.NewRouter()
am := middleware.NewAuthZ(s.serverOptions.SigNoz.Instrumentation.Logger()) am := middleware.NewAuthZ(s.serverOptions.SigNoz.Instrumentation.Logger())
r.Use(middleware.NewAuth(s.serverOptions.Jwt, []string{"Authorization", "Sec-WebSocket-Protocol"}).Wrap) r.Use(middleware.NewAuth(s.serverOptions.Jwt, []string{"Authorization", "Sec-WebSocket-Protocol"}, s.serverOptions.SigNoz.Sharder, s.serverOptions.SigNoz.Instrumentation.Logger()).Wrap)
r.Use(middleware.NewAPIKey(s.serverOptions.SigNoz.SQLStore, []string{"SIGNOZ-API-KEY"}, s.serverOptions.SigNoz.Instrumentation.Logger()).Wrap) r.Use(middleware.NewAPIKey(s.serverOptions.SigNoz.SQLStore, []string{"SIGNOZ-API-KEY"}, s.serverOptions.SigNoz.Instrumentation.Logger(), s.serverOptions.SigNoz.Sharder).Wrap)
r.Use(middleware.NewTimeout(s.serverOptions.SigNoz.Instrumentation.Logger(), r.Use(middleware.NewTimeout(s.serverOptions.SigNoz.Instrumentation.Logger(),
s.serverOptions.Config.APIServer.Timeout.ExcludedRoutes, s.serverOptions.Config.APIServer.Timeout.ExcludedRoutes,
s.serverOptions.Config.APIServer.Timeout.Default, s.serverOptions.Config.APIServer.Timeout.Default,
@ -297,6 +298,7 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*h
apiHandler.RegisterMessagingQueuesRoutes(r, am) apiHandler.RegisterMessagingQueuesRoutes(r, am)
apiHandler.RegisterThirdPartyApiRoutes(r, am) apiHandler.RegisterThirdPartyApiRoutes(r, am)
apiHandler.MetricExplorerRoutes(r, am) apiHandler.MetricExplorerRoutes(r, am)
apiHandler.RegisterTraceFunnelsRoutes(r, am)
c := cors.New(cors.Options{ c := cors.New(cors.Options{
AllowedOrigins: []string{"*"}, AllowedOrigins: []string{"*"},
@ -450,6 +452,7 @@ func makeRulesManager(
sqlstore sqlstore.SQLStore, sqlstore sqlstore.SQLStore,
telemetryStore telemetrystore.TelemetryStore, telemetryStore telemetrystore.TelemetryStore,
prometheus prometheus.Prometheus, prometheus prometheus.Prometheus,
orgGetter organization.Getter,
) (*baserules.Manager, error) { ) (*baserules.Manager, error) {
// create manager opts // create manager opts
managerOpts := &baserules.ManagerOptions{ managerOpts := &baserules.ManagerOptions{
@ -465,6 +468,7 @@ func makeRulesManager(
PrepareTestRuleFunc: rules.TestNotification, PrepareTestRuleFunc: rules.TestNotification,
Alertmanager: alertmanager, Alertmanager: alertmanager,
SQLStore: sqlstore, SQLStore: sqlstore,
OrgGetter: orgGetter,
} }
// create Manager // create Manager

View File

@ -17,6 +17,7 @@ import (
"github.com/SigNoz/signoz/pkg/config/fileprovider" "github.com/SigNoz/signoz/pkg/config/fileprovider"
"github.com/SigNoz/signoz/pkg/factory" "github.com/SigNoz/signoz/pkg/factory"
pkglicensing "github.com/SigNoz/signoz/pkg/licensing" pkglicensing "github.com/SigNoz/signoz/pkg/licensing"
"github.com/SigNoz/signoz/pkg/modules/organization"
baseconst "github.com/SigNoz/signoz/pkg/query-service/constants" baseconst "github.com/SigNoz/signoz/pkg/query-service/constants"
"github.com/SigNoz/signoz/pkg/signoz" "github.com/SigNoz/signoz/pkg/signoz"
"github.com/SigNoz/signoz/pkg/sqlstore" "github.com/SigNoz/signoz/pkg/sqlstore"
@ -133,8 +134,8 @@ func main() {
zeus.Config(), zeus.Config(),
httpzeus.NewProviderFactory(), httpzeus.NewProviderFactory(),
licensing.Config(24*time.Hour, 3), licensing.Config(24*time.Hour, 3),
func(sqlstore sqlstore.SQLStore, zeus pkgzeus.Zeus) factory.ProviderFactory[pkglicensing.Licensing, pkglicensing.Config] { func(sqlstore sqlstore.SQLStore, zeus pkgzeus.Zeus, orgGetter organization.Getter) factory.ProviderFactory[pkglicensing.Licensing, pkglicensing.Config] {
return httplicensing.NewProviderFactory(sqlstore, zeus) return httplicensing.NewProviderFactory(sqlstore, zeus, orgGetter)
}, },
signoz.NewEmailingProviderFactories(), signoz.NewEmailingProviderFactories(),
signoz.NewCacheProviderFactories(), signoz.NewCacheProviderFactories(),

View File

@ -41,16 +41,16 @@ type Manager struct {
zeus zeus.Zeus zeus zeus.Zeus
organizationModule organization.Module orgGetter organization.Getter
} }
func New(licenseService licensing.Licensing, clickhouseConn clickhouse.Conn, zeus zeus.Zeus, organizationModule organization.Module) (*Manager, error) { func New(licenseService licensing.Licensing, clickhouseConn clickhouse.Conn, zeus zeus.Zeus, orgGetter organization.Getter) (*Manager, error) {
m := &Manager{ m := &Manager{
clickhouseConn: clickhouseConn, clickhouseConn: clickhouseConn,
licenseService: licenseService, licenseService: licenseService,
scheduler: gocron.NewScheduler(time.UTC).Every(1).Day().At("00:00"), // send usage every at 00:00 UTC scheduler: gocron.NewScheduler(time.UTC).Every(1).Day().At("00:00"), // send usage every at 00:00 UTC
zeus: zeus, zeus: zeus,
organizationModule: organizationModule, orgGetter: orgGetter,
} }
return m, nil return m, nil
} }
@ -74,8 +74,7 @@ func (lm *Manager) Start(ctx context.Context) error {
return nil return nil
} }
func (lm *Manager) UploadUsage(ctx context.Context) { func (lm *Manager) UploadUsage(ctx context.Context) {
organizations, err := lm.orgGetter.ListByOwnedKeyRange(ctx)
organizations, err := lm.organizationModule.GetAll(context.Background())
if err != nil { if err != nil {
zap.L().Error("failed to get organizations", zap.Error(err)) zap.L().Error("failed to get organizations", zap.Error(err))
return return

View File

@ -28,6 +28,7 @@ import { QueryBuilderProvider } from 'providers/QueryBuilder';
import { Suspense, useCallback, useEffect, useState } from 'react'; import { Suspense, useCallback, useEffect, useState } from 'react';
import { Route, Router, Switch } from 'react-router-dom'; import { Route, Router, Switch } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat'; import { CompatRouter } from 'react-router-dom-v5-compat';
import { LicenseStatus } from 'types/api/licensesV3/getActive';
import { Userpilot } from 'userpilot'; import { Userpilot } from 'userpilot';
import { extractDomain } from 'utils/app'; import { extractDomain } from 'utils/app';
@ -171,11 +172,13 @@ function App(): JSX.Element {
user && user &&
!!user.email !!user.email
) { ) {
// either the active API returns error with 404 or 501 and if it returns a terminated license means it's on basic plan
const isOnBasicPlan = const isOnBasicPlan =
activeLicenseFetchError && (activeLicenseFetchError &&
[StatusCodes.NOT_FOUND, StatusCodes.NOT_IMPLEMENTED].includes( [StatusCodes.NOT_FOUND, StatusCodes.NOT_IMPLEMENTED].includes(
activeLicenseFetchError?.getHttpStatusCode(), activeLicenseFetchError?.getHttpStatusCode(),
); )) ||
(activeLicense?.status && activeLicense.status === LicenseStatus.INVALID);
const isIdentifiedUser = getLocalStorageApi(LOCALSTORAGE.IS_IDENTIFIED_USER); const isIdentifiedUser = getLocalStorageApi(LOCALSTORAGE.IS_IDENTIFIED_USER);
if (isLoggedInState && user && user.id && user.email && !isIdentifiedUser) { if (isLoggedInState && user && user.id && user.email && !isIdentifiedUser) {
@ -190,6 +193,10 @@ function App(): JSX.Element {
updatedRoutes = updatedRoutes.filter( updatedRoutes = updatedRoutes.filter(
(route) => route?.path !== ROUTES.BILLING, (route) => route?.path !== ROUTES.BILLING,
); );
if (isEnterpriseSelfHostedUser) {
updatedRoutes.push(LIST_LICENSES);
}
} }
// always add support route for cloud users // always add support route for cloud users
updatedRoutes = [...updatedRoutes, SUPPORT_ROUTE]; updatedRoutes = [...updatedRoutes, SUPPORT_ROUTE];

View File

@ -16,6 +16,7 @@ import JSONView from 'container/LogDetailedView/JsonView';
import Overview from 'container/LogDetailedView/Overview'; import Overview from 'container/LogDetailedView/Overview';
import { import {
aggregateAttributesResourcesToString, aggregateAttributesResourcesToString,
escapeHtml,
removeEscapeCharacters, removeEscapeCharacters,
unescapeString, unescapeString,
} from 'container/LogDetailedView/utils'; } from 'container/LogDetailedView/utils';
@ -118,7 +119,7 @@ function LogDetail({
const htmlBody = useMemo( const htmlBody = useMemo(
() => ({ () => ({
__html: convert.toHtml( __html: convert.toHtml(
dompurify.sanitize(unescapeString(log?.body || ''), { dompurify.sanitize(unescapeString(escapeHtml(log?.body || '')), {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS], FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}), }),
), ),

View File

@ -7,7 +7,7 @@ import cx from 'classnames';
import LogDetail from 'components/LogDetail'; import LogDetail from 'components/LogDetail';
import { VIEW_TYPES } from 'components/LogDetail/constants'; import { VIEW_TYPES } from 'components/LogDetail/constants';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats'; import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { unescapeString } from 'container/LogDetailedView/utils'; import { escapeHtml, unescapeString } from 'container/LogDetailedView/utils';
import { FontSize } from 'container/OptionsMenu/types'; import { FontSize } from 'container/OptionsMenu/types';
import dompurify from 'dompurify'; import dompurify from 'dompurify';
import { useActiveLog } from 'hooks/logs/useActiveLog'; import { useActiveLog } from 'hooks/logs/useActiveLog';
@ -58,7 +58,7 @@ function LogGeneralField({
const html = useMemo( const html = useMemo(
() => ({ () => ({
__html: convert.toHtml( __html: convert.toHtml(
dompurify.sanitize(unescapeString(fieldValue), { dompurify.sanitize(unescapeString(escapeHtml(fieldValue)), {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS], FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}), }),
), ),

View File

@ -5,7 +5,7 @@ import { DrawerProps } from 'antd';
import LogDetail from 'components/LogDetail'; import LogDetail from 'components/LogDetail';
import { VIEW_TYPES, VIEWS } from 'components/LogDetail/constants'; import { VIEW_TYPES, VIEWS } from 'components/LogDetail/constants';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats'; import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { unescapeString } from 'container/LogDetailedView/utils'; import { escapeHtml, unescapeString } from 'container/LogDetailedView/utils';
import LogsExplorerContext from 'container/LogsExplorerContext'; import LogsExplorerContext from 'container/LogsExplorerContext';
import dompurify from 'dompurify'; import dompurify from 'dompurify';
import { useActiveLog } from 'hooks/logs/useActiveLog'; import { useActiveLog } from 'hooks/logs/useActiveLog';
@ -177,7 +177,7 @@ function RawLogView({
const html = useMemo( const html = useMemo(
() => ({ () => ({
__html: convert.toHtml( __html: convert.toHtml(
dompurify.sanitize(unescapeString(text), { dompurify.sanitize(unescapeString(escapeHtml(text)), {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS], FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}), }),
), ),

View File

@ -21,8 +21,10 @@ import { FORBID_DOM_PURIFY_TAGS } from 'utils/app';
import { DataType } from '../TableView'; import { DataType } from '../TableView';
import { import {
escapeHtml,
filterKeyForField, filterKeyForField,
jsonToDataNodes, jsonToDataNodes,
parseFieldValue,
recursiveParseJSON, recursiveParseJSON,
removeEscapeCharacters, removeEscapeCharacters,
unescapeString, unescapeString,
@ -85,7 +87,7 @@ export function TableViewActions(
record.field === 'body' record.field === 'body'
? { ? {
__html: convert.toHtml( __html: convert.toHtml(
dompurify.sanitize(unescapeString(record.value), { dompurify.sanitize(unescapeString(escapeHtml(record.value)), {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS], FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}), }),
), ),
@ -155,7 +157,11 @@ export function TableViewActions(
<ArrowDownToDot size={14} style={{ transform: 'rotate(90deg)' }} /> <ArrowDownToDot size={14} style={{ transform: 'rotate(90deg)' }} />
) )
} }
onClick={onClickHandler(OPERATORS['='], fieldFilterKey, fieldData.value)} onClick={onClickHandler(
OPERATORS['='],
fieldFilterKey,
parseFieldValue(fieldData.value),
)}
/> />
</Tooltip> </Tooltip>
<Tooltip title="Filter out value"> <Tooltip title="Filter out value">
@ -171,7 +177,7 @@ export function TableViewActions(
onClick={onClickHandler( onClick={onClickHandler(
OPERATORS['!='], OPERATORS['!='],
fieldFilterKey, fieldFilterKey,
fieldData.value, parseFieldValue(fieldData.value),
)} )}
/> />
</Tooltip> </Tooltip>

View File

@ -259,6 +259,24 @@ export const getDataTypes = (value: unknown): DataTypes => {
return determineType(value); return determineType(value);
}; };
// prevent html rendering in the value
export const escapeHtml = (unsafe: string): string =>
unsafe
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
// parse field value to remove escaping characters
export const parseFieldValue = (value: string): string => {
try {
return JSON.parse(value);
} catch (error) {
return value;
}
};
// now we do not want to render colors everywhere like in tooltip and monaco editor hence we remove such codes to make // now we do not want to render colors everywhere like in tooltip and monaco editor hence we remove such codes to make
// the log line readable // the log line readable
export const removeEscapeCharacters = (str: string): string => export const removeEscapeCharacters = (str: string): string =>

View File

@ -28,16 +28,12 @@ import LogsExplorerTable from 'container/LogsExplorerTable';
import { useOptionsMenu } from 'container/OptionsMenu'; import { useOptionsMenu } from 'container/OptionsMenu';
import TimeSeriesView from 'container/TimeSeriesView/TimeSeriesView'; import TimeSeriesView from 'container/TimeSeriesView/TimeSeriesView';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
import { addEmptyWidgetInDashboardJSONWithQuery } from 'hooks/dashboard/utils';
import { useCopyLogLink } from 'hooks/logs/useCopyLogLink'; import { useCopyLogLink } from 'hooks/logs/useCopyLogLink';
import { useGetExplorerQueryRange } from 'hooks/queryBuilder/useGetExplorerQueryRange'; import { useGetExplorerQueryRange } from 'hooks/queryBuilder/useGetExplorerQueryRange';
import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam'; import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import useAxiosError from 'hooks/useAxiosError';
import useClickOutside from 'hooks/useClickOutside'; import useClickOutside from 'hooks/useClickOutside';
import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange'; import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange';
import { useNotifications } from 'hooks/useNotifications';
import { useSafeNavigate } from 'hooks/useSafeNavigate'; import { useSafeNavigate } from 'hooks/useSafeNavigate';
import useUrlQueryData from 'hooks/useUrlQueryData'; import useUrlQueryData from 'hooks/useUrlQueryData';
import { FlatLogData } from 'lib/logs/flatLogData'; import { FlatLogData } from 'lib/logs/flatLogData';
@ -98,7 +94,6 @@ function LogsExplorerViews({
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
chartQueryKeyRef: MutableRefObject<any>; chartQueryKeyRef: MutableRefObject<any>;
}): JSX.Element { }): JSX.Element {
const { notifications } = useNotifications();
const { safeNavigate } = useSafeNavigate(); const { safeNavigate } = useSafeNavigate();
// this is to respect the panel type present in the URL rather than defaulting it to list always. // this is to respect the panel type present in the URL rather than defaulting it to list always.
@ -141,8 +136,6 @@ function LogsExplorerViews({
const [queryId, setQueryId] = useState<string>(v4()); const [queryId, setQueryId] = useState<string>(v4());
const [queryStats, setQueryStats] = useState<WsDataEvent>(); const [queryStats, setQueryStats] = useState<WsDataEvent>();
const handleAxisError = useAxiosError();
const listQuery = useMemo(() => { const listQuery = useMemo(() => {
if (!stagedQuery || stagedQuery.builder.queryData.length < 1) return null; if (!stagedQuery || stagedQuery.builder.queryData.length < 1) return null;
@ -396,11 +389,6 @@ function LogsExplorerViews({
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [data?.payload]); }, [data?.payload]);
const {
mutate: updateDashboard,
isLoading: isUpdateDashboardLoading,
} = useUpdateDashboard();
const getUpdatedQueryForExport = useCallback((): Query => { const getUpdatedQueryForExport = useCallback((): Query => {
const updatedQuery = cloneDeep(currentQuery); const updatedQuery = cloneDeep(currentQuery);
@ -424,68 +412,22 @@ function LogsExplorerViews({
? getUpdatedQueryForExport() ? getUpdatedQueryForExport()
: exportDefaultQuery; : exportDefaultQuery;
const updatedDashboard = addEmptyWidgetInDashboardJSONWithQuery(
dashboard,
query,
widgetId,
panelTypeParam,
options.selectColumns,
);
logEvent('Logs Explorer: Add to dashboard successful', { logEvent('Logs Explorer: Add to dashboard successful', {
panelType, panelType,
isNewDashboard, isNewDashboard,
dashboardName: dashboard?.data?.title, dashboardName: dashboard?.data?.title,
}); });
updateDashboard(updatedDashboard, { const dashboardEditView = generateExportToDashboardLink({
onSuccess: (data) => { query,
if (data.error) { panelType: panelTypeParam,
const message = dashboardId: dashboard.uuid,
data.error === 'feature usage exceeded' ? ( widgetId,
<span>
Panel limit exceeded for {DataSource.LOGS} in community edition. Please
checkout our paid plans{' '}
<a
href="https://signoz.io/pricing/?utm_source=product&utm_medium=dashboard-limit"
rel="noreferrer noopener"
target="_blank"
>
here
</a>
</span>
) : (
data.error
);
notifications.error({
message,
});
return;
}
const dashboardEditView = generateExportToDashboardLink({
query,
panelType: panelTypeParam,
dashboardId: data.payload?.uuid || '',
widgetId,
});
safeNavigate(dashboardEditView);
},
onError: handleAxisError,
}); });
safeNavigate(dashboardEditView);
}, },
[ [getUpdatedQueryForExport, exportDefaultQuery, safeNavigate, panelType],
getUpdatedQueryForExport,
exportDefaultQuery,
options.selectColumns,
safeNavigate,
notifications,
panelType,
updateDashboard,
handleAxisError,
],
); );
useEffect(() => { useEffect(() => {
@ -811,7 +753,6 @@ function LogsExplorerViews({
<ExplorerOptionWrapper <ExplorerOptionWrapper
disabled={!stagedQuery} disabled={!stagedQuery}
query={exportDefaultQuery} query={exportDefaultQuery}
isLoading={isUpdateDashboardLoading}
onExport={handleExport} onExport={handleExport}
sourcepage={DataSource.LOGS} sourcepage={DataSource.LOGS}
/> />

View File

@ -2,18 +2,12 @@ import './Explorer.styles.scss';
import * as Sentry from '@sentry/react'; import * as Sentry from '@sentry/react';
import { Switch } from 'antd'; import { Switch } from 'antd';
import axios from 'axios';
import { LOCALSTORAGE } from 'constants/localStorage';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder'; import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import ExplorerOptionWrapper from 'container/ExplorerOptions/ExplorerOptionWrapper'; import ExplorerOptionWrapper from 'container/ExplorerOptions/ExplorerOptionWrapper';
import { useOptionsMenu } from 'container/OptionsMenu';
import RightToolbarActions from 'container/QueryBuilder/components/ToolbarActions/RightToolbarActions'; import RightToolbarActions from 'container/QueryBuilder/components/ToolbarActions/RightToolbarActions';
import DateTimeSelector from 'container/TopNav/DateTimeSelectionV2'; import DateTimeSelector from 'container/TopNav/DateTimeSelectionV2';
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
import { addEmptyWidgetInDashboardJSONWithQuery } from 'hooks/dashboard/utils';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl'; import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
import { useNotifications } from 'hooks/useNotifications';
import { useSafeNavigate } from 'hooks/useSafeNavigate'; import { useSafeNavigate } from 'hooks/useSafeNavigate';
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
@ -39,13 +33,6 @@ function Explorer(): JSX.Element {
currentQuery, currentQuery,
} = useQueryBuilder(); } = useQueryBuilder();
const { safeNavigate } = useSafeNavigate(); const { safeNavigate } = useSafeNavigate();
const { notifications } = useNotifications();
const { mutate: updateDashboard, isLoading } = useUpdateDashboard();
const { options } = useOptionsMenu({
storageKey: LOCALSTORAGE.METRICS_LIST_OPTIONS,
dataSource: DataSource.METRICS,
aggregateOperator: 'noop',
});
const [searchParams, setSearchParams] = useSearchParams(); const [searchParams, setSearchParams] = useSearchParams();
const isOneChartPerQueryEnabled = const isOneChartPerQueryEnabled =
@ -86,59 +73,16 @@ function Explorer(): JSX.Element {
const widgetId = uuid(); const widgetId = uuid();
const updatedDashboard = addEmptyWidgetInDashboardJSONWithQuery( const dashboardEditView = generateExportToDashboardLink({
dashboard, query: queryToExport || exportDefaultQuery,
queryToExport || exportDefaultQuery, panelType: PANEL_TYPES.TIME_SERIES,
dashboardId: dashboard?.uuid || '',
widgetId, widgetId,
PANEL_TYPES.TIME_SERIES,
options.selectColumns,
);
updateDashboard(updatedDashboard, {
onSuccess: (data) => {
if (data.error) {
const message =
data.error === 'feature usage exceeded' ? (
<span>
Panel limit exceeded for {DataSource.METRICS} in community edition.
Please checkout our paid plans{' '}
<a
href="https://signoz.io/pricing/?utm_source=product&utm_medium=dashboard-limit"
rel="noreferrer noopener"
target="_blank"
>
here
</a>
</span>
) : (
data.error
);
notifications.error({
message,
});
return;
}
const dashboardEditView = generateExportToDashboardLink({
query: queryToExport || exportDefaultQuery,
panelType: PANEL_TYPES.TIME_SERIES,
dashboardId: data.payload?.uuid || '',
widgetId,
});
safeNavigate(dashboardEditView);
},
onError: (error) => {
if (axios.isAxiosError(error)) {
notifications.error({
message: error.message,
});
}
},
}); });
safeNavigate(dashboardEditView);
}, },
// eslint-disable-next-line react-hooks/exhaustive-deps [exportDefaultQuery, safeNavigate],
[exportDefaultQuery, notifications, updateDashboard],
); );
const splitedQueries = useMemo( const splitedQueries = useMemo(
@ -201,7 +145,6 @@ function Explorer(): JSX.Element {
<ExplorerOptionWrapper <ExplorerOptionWrapper
disabled={!stagedQuery} disabled={!stagedQuery}
query={exportDefaultQuery} query={exportDefaultQuery}
isLoading={isLoading}
sourcepage={DataSource.METRICS} sourcepage={DataSource.METRICS}
onExport={handleExport} onExport={handleExport}
isOneChartPerQuery={showOneChartPerQuery} isOneChartPerQuery={showOneChartPerQuery}

View File

@ -26,6 +26,7 @@ import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import { LicenseStatus } from 'types/api/licensesV3/getActive';
import AppReducer from 'types/reducer/app'; import AppReducer from 'types/reducer/app';
import { USER_ROLES } from 'types/roles'; import { USER_ROLES } from 'types/roles';
import { checkVersionState } from 'utils/app'; import { checkVersionState } from 'utils/app';
@ -301,10 +302,11 @@ function SideNav(): JSX.Element {
} }
const isOnBasicPlan = const isOnBasicPlan =
activeLicenseFetchError && (activeLicenseFetchError &&
[StatusCodes.NOT_FOUND, StatusCodes.NOT_IMPLEMENTED].includes( [StatusCodes.NOT_FOUND, StatusCodes.NOT_IMPLEMENTED].includes(
activeLicenseFetchError?.getHttpStatusCode(), activeLicenseFetchError?.getHttpStatusCode(),
); )) ||
(activeLicense?.status && activeLicense.status === LicenseStatus.INVALID);
if (user.role !== USER_ROLES.ADMIN || isOnBasicPlan) { if (user.role !== USER_ROLES.ADMIN || isOnBasicPlan) {
updatedMenuItems = updatedMenuItems.filter( updatedMenuItems = updatedMenuItems.filter(
@ -353,6 +355,7 @@ function SideNav(): JSX.Element {
t, t,
user.role, user.role,
activeLicenseFetchError, activeLicenseFetchError,
activeLicense?.status,
]); ]);
return ( return (

View File

@ -4,7 +4,6 @@ import { FilterOutlined } from '@ant-design/icons';
import * as Sentry from '@sentry/react'; import * as Sentry from '@sentry/react';
import { Button, Card, Tabs, Tooltip } from 'antd'; import { Button, Card, Tabs, Tooltip } from 'antd';
import logEvent from 'api/common/logEvent'; import logEvent from 'api/common/logEvent';
import axios from 'axios';
import cx from 'classnames'; import cx from 'classnames';
import ExplorerCard from 'components/ExplorerCard/ExplorerCard'; import ExplorerCard from 'components/ExplorerCard/ExplorerCard';
import QuickFilters from 'components/QuickFilters/QuickFilters'; import QuickFilters from 'components/QuickFilters/QuickFilters';
@ -19,13 +18,10 @@ import RightToolbarActions from 'container/QueryBuilder/components/ToolbarAction
import DateTimeSelector from 'container/TopNav/DateTimeSelectionV2'; import DateTimeSelector from 'container/TopNav/DateTimeSelectionV2';
import { defaultSelectedColumns } from 'container/TracesExplorer/ListView/configs'; import { defaultSelectedColumns } from 'container/TracesExplorer/ListView/configs';
import QuerySection from 'container/TracesExplorer/QuerySection'; import QuerySection from 'container/TracesExplorer/QuerySection';
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
import { addEmptyWidgetInDashboardJSONWithQuery } from 'hooks/dashboard/utils';
import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam'; import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl'; import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange'; import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange';
import { useNotifications } from 'hooks/useNotifications';
import { useSafeNavigate } from 'hooks/useSafeNavigate'; import { useSafeNavigate } from 'hooks/useSafeNavigate';
import { cloneDeep, isEmpty, set } from 'lodash-es'; import { cloneDeep, isEmpty, set } from 'lodash-es';
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
@ -40,8 +36,6 @@ import { ActionsWrapper, Container } from './styles';
import { getTabsItems } from './utils'; import { getTabsItems } from './utils';
function TracesExplorer(): JSX.Element { function TracesExplorer(): JSX.Element {
const { notifications } = useNotifications();
const { const {
currentQuery, currentQuery,
panelType, panelType,
@ -124,9 +118,7 @@ function TracesExplorer(): JSX.Element {
[currentQuery, updateAllQueriesOperators], [currentQuery, updateAllQueriesOperators],
); );
const { mutate: updateDashboard, isLoading } = useUpdateDashboard(); const getUpdatedQueryForExport = useCallback((): Query => {
const getUpdatedQueryForExport = (): Query => {
const updatedQuery = cloneDeep(currentQuery); const updatedQuery = cloneDeep(currentQuery);
set( set(
@ -136,7 +128,7 @@ function TracesExplorer(): JSX.Element {
); );
return updatedQuery; return updatedQuery;
}; }, [currentQuery, options.selectColumns]);
const handleExport = useCallback( const handleExport = useCallback(
(dashboard: Dashboard | null, isNewDashboard?: boolean): void => { (dashboard: Dashboard | null, isNewDashboard?: boolean): void => {
@ -153,65 +145,22 @@ function TracesExplorer(): JSX.Element {
? getUpdatedQueryForExport() ? getUpdatedQueryForExport()
: exportDefaultQuery; : exportDefaultQuery;
const updatedDashboard = addEmptyWidgetInDashboardJSONWithQuery(
dashboard,
query,
widgetId,
panelTypeParam,
options.selectColumns,
);
logEvent('Traces Explorer: Add to dashboard successful', { logEvent('Traces Explorer: Add to dashboard successful', {
panelType, panelType,
isNewDashboard, isNewDashboard,
dashboardName: dashboard?.data?.title, dashboardName: dashboard?.data?.title,
}); });
updateDashboard(updatedDashboard, { const dashboardEditView = generateExportToDashboardLink({
onSuccess: (data) => { query,
if (data.error) { panelType: panelTypeParam,
const message = dashboardId: dashboard?.uuid || '',
data.error === 'feature usage exceeded' ? ( widgetId,
<span>
Panel limit exceeded for {DataSource.TRACES} in community edition.
Please checkout our paid plans{' '}
<a
href="https://signoz.io/pricing/?utm_source=product&utm_medium=dashboard-limit"
rel="noreferrer noopener"
target="_blank"
>
here
</a>
</span>
) : (
data.error
);
notifications.error({
message,
});
return;
}
const dashboardEditView = generateExportToDashboardLink({
query,
panelType: panelTypeParam,
dashboardId: data.payload?.uuid || '',
widgetId,
});
safeNavigate(dashboardEditView);
},
onError: (error) => {
if (axios.isAxiosError(error)) {
notifications.error({
message: error.message,
});
}
},
}); });
safeNavigate(dashboardEditView);
}, },
// eslint-disable-next-line react-hooks/exhaustive-deps [exportDefaultQuery, panelType, safeNavigate, getUpdatedQueryForExport],
[exportDefaultQuery, notifications, panelType, updateDashboard],
); );
useShareBuilderUrl(defaultQuery); useShareBuilderUrl(defaultQuery);
@ -282,11 +231,7 @@ function TracesExplorer(): JSX.Element {
<Container className="traces-explorer-views"> <Container className="traces-explorer-views">
<ActionsWrapper> <ActionsWrapper>
<ExportPanel <ExportPanel query={exportDefaultQuery} onExport={handleExport} />
query={exportDefaultQuery}
isLoading={isLoading}
onExport={handleExport}
/>
</ActionsWrapper> </ActionsWrapper>
<Tabs <Tabs
@ -299,7 +244,6 @@ function TracesExplorer(): JSX.Element {
<ExplorerOptionWrapper <ExplorerOptionWrapper
disabled={!stagedQuery} disabled={!stagedQuery}
query={exportDefaultQuery} query={exportDefaultQuery}
isLoading={isLoading}
sourcepage={DataSource.TRACES} sourcepage={DataSource.TRACES}
onExport={handleExport} onExport={handleExport}
/> />

View File

@ -6,6 +6,7 @@ export enum LicenseEvent {
export enum LicenseStatus { export enum LicenseStatus {
SUSPENDED = 'SUSPENDED', SUSPENDED = 'SUSPENDED',
VALID = 'VALID', VALID = 'VALID',
INVALID = 'INVALID',
} }
export enum LicenseState { export enum LicenseState {

View File

@ -67,23 +67,6 @@ func (store *config) Set(ctx context.Context, config *alertmanagertypes.Config,
}, opts...) }, opts...)
} }
func (store *config) ListOrgs(ctx context.Context) ([]string, error) {
var orgIDs []string
err := store.
sqlstore.
BunDB().
NewSelect().
Table("organizations").
ColumnExpr("id").
Scan(ctx, &orgIDs)
if err != nil {
return nil, err
}
return orgIDs, nil
}
func (store *config) CreateChannel(ctx context.Context, channel *alertmanagertypes.Channel, opts ...alertmanagertypes.StoreOption) error { func (store *config) CreateChannel(ctx context.Context, channel *alertmanagertypes.Channel, opts ...alertmanagertypes.StoreOption) error {
return store.wrap(ctx, func(ctx context.Context) error { return store.wrap(ctx, func(ctx context.Context) error {
if _, err := store. if _, err := store.

View File

@ -14,6 +14,7 @@ import (
"github.com/SigNoz/signoz/pkg/alertmanager/alertmanagerbatcher" "github.com/SigNoz/signoz/pkg/alertmanager/alertmanagerbatcher"
"github.com/SigNoz/signoz/pkg/alertmanager/alertmanagerstore/sqlalertmanagerstore" "github.com/SigNoz/signoz/pkg/alertmanager/alertmanagerstore/sqlalertmanagerstore"
"github.com/SigNoz/signoz/pkg/factory" "github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/sqlstore" "github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types/alertmanagertypes" "github.com/SigNoz/signoz/pkg/types/alertmanagertypes"
"github.com/SigNoz/signoz/pkg/valuer" "github.com/SigNoz/signoz/pkg/valuer"
@ -57,16 +58,17 @@ type provider struct {
configStore alertmanagertypes.ConfigStore configStore alertmanagertypes.ConfigStore
batcher *alertmanagerbatcher.Batcher batcher *alertmanagerbatcher.Batcher
url *url.URL url *url.URL
orgGetter organization.Getter
orgID string orgID string
} }
func NewFactory(sqlstore sqlstore.SQLStore) factory.ProviderFactory[alertmanager.Alertmanager, alertmanager.Config] { func NewFactory(sqlstore sqlstore.SQLStore, orgGetter organization.Getter) factory.ProviderFactory[alertmanager.Alertmanager, alertmanager.Config] {
return factory.NewProviderFactory(factory.MustNewName("legacy"), func(ctx context.Context, settings factory.ProviderSettings, config alertmanager.Config) (alertmanager.Alertmanager, error) { return factory.NewProviderFactory(factory.MustNewName("legacy"), func(ctx context.Context, settings factory.ProviderSettings, config alertmanager.Config) (alertmanager.Alertmanager, error) {
return New(ctx, settings, config, sqlstore) return New(ctx, settings, config, sqlstore, orgGetter)
}) })
} }
func New(ctx context.Context, providerSettings factory.ProviderSettings, config alertmanager.Config, sqlstore sqlstore.SQLStore) (*provider, error) { func New(ctx context.Context, providerSettings factory.ProviderSettings, config alertmanager.Config, sqlstore sqlstore.SQLStore, orgGetter organization.Getter) (*provider, error) {
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/alertmanager/legacyalertmanager") settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/alertmanager/legacyalertmanager")
configStore := sqlalertmanagerstore.NewConfigStore(sqlstore) configStore := sqlalertmanagerstore.NewConfigStore(sqlstore)
@ -92,7 +94,7 @@ func (provider *provider) Start(ctx context.Context) error {
// For the first time, we need to get the orgID from the config store. // For the first time, we need to get the orgID from the config store.
// Since this is the legacy alertmanager, we get the first org from the store. // Since this is the legacy alertmanager, we get the first org from the store.
if provider.orgID == "" { if provider.orgID == "" {
orgIDs, err := provider.configStore.ListOrgs(ctx) orgIDs, err := provider.orgGetter.ListByOwnedKeyRange(ctx)
if err != nil { if err != nil {
provider.settings.Logger().ErrorContext(ctx, "failed to send alerts to alertmanager", "error", err) provider.settings.Logger().ErrorContext(ctx, "failed to send alerts to alertmanager", "error", err)
continue continue
@ -103,7 +105,7 @@ func (provider *provider) Start(ctx context.Context) error {
continue continue
} }
provider.orgID = orgIDs[0] provider.orgID = orgIDs[0].ID.String()
} }
if err := provider.putAlerts(ctx, provider.orgID, alerts); err != nil { if err := provider.putAlerts(ctx, provider.orgID, alerts); err != nil {

View File

@ -7,6 +7,7 @@ import (
"github.com/SigNoz/signoz/pkg/alertmanager/alertmanagerserver" "github.com/SigNoz/signoz/pkg/alertmanager/alertmanagerserver"
"github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/factory" "github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/types/alertmanagertypes" "github.com/SigNoz/signoz/pkg/types/alertmanagertypes"
) )
@ -20,6 +21,9 @@ type Service struct {
// configStore is the config store for the alertmanager service // configStore is the config store for the alertmanager service
configStore alertmanagertypes.ConfigStore configStore alertmanagertypes.ConfigStore
// organization is the organization module for the alertmanager service
orgGetter organization.Getter
// settings is the settings for the alertmanager service // settings is the settings for the alertmanager service
settings factory.ScopedProviderSettings settings factory.ScopedProviderSettings
@ -30,11 +34,19 @@ type Service struct {
serversMtx sync.RWMutex serversMtx sync.RWMutex
} }
func New(ctx context.Context, settings factory.ScopedProviderSettings, config alertmanagerserver.Config, stateStore alertmanagertypes.StateStore, configStore alertmanagertypes.ConfigStore) *Service { func New(
ctx context.Context,
settings factory.ScopedProviderSettings,
config alertmanagerserver.Config,
stateStore alertmanagertypes.StateStore,
configStore alertmanagertypes.ConfigStore,
orgGetter organization.Getter,
) *Service {
service := &Service{ service := &Service{
config: config, config: config,
stateStore: stateStore, stateStore: stateStore,
configStore: configStore, configStore: configStore,
orgGetter: orgGetter,
settings: settings, settings: settings,
servers: make(map[string]*alertmanagerserver.Server), servers: make(map[string]*alertmanagerserver.Server),
serversMtx: sync.RWMutex{}, serversMtx: sync.RWMutex{},
@ -44,38 +56,38 @@ func New(ctx context.Context, settings factory.ScopedProviderSettings, config al
} }
func (service *Service) SyncServers(ctx context.Context) error { func (service *Service) SyncServers(ctx context.Context) error {
orgIDs, err := service.configStore.ListOrgs(ctx) orgs, err := service.orgGetter.ListByOwnedKeyRange(ctx)
if err != nil { if err != nil {
return err return err
} }
service.serversMtx.Lock() service.serversMtx.Lock()
for _, orgID := range orgIDs { for _, org := range orgs {
config, err := service.getConfig(ctx, orgID) config, err := service.getConfig(ctx, org.ID.StringValue())
if err != nil { if err != nil {
service.settings.Logger().ErrorContext(ctx, "failed to get alertmanager config for org", "org_id", orgID, "error", err) service.settings.Logger().ErrorContext(ctx, "failed to get alertmanager config for org", "org_id", org.ID.StringValue(), "error", err)
continue continue
} }
// If the server is not present, create it and sync the config // If the server is not present, create it and sync the config
if _, ok := service.servers[orgID]; !ok { if _, ok := service.servers[org.ID.StringValue()]; !ok {
server, err := service.newServer(ctx, orgID) server, err := service.newServer(ctx, org.ID.StringValue())
if err != nil { if err != nil {
service.settings.Logger().ErrorContext(ctx, "failed to create alertmanager server", "org_id", orgID, "error", err) service.settings.Logger().ErrorContext(ctx, "failed to create alertmanager server", "org_id", org.ID.StringValue(), "error", err)
continue continue
} }
service.servers[orgID] = server service.servers[org.ID.StringValue()] = server
} }
if service.servers[orgID].Hash() == config.StoreableConfig().Hash { if service.servers[org.ID.StringValue()].Hash() == config.StoreableConfig().Hash {
service.settings.Logger().DebugContext(ctx, "skipping alertmanager sync for org", "org_id", orgID, "hash", config.StoreableConfig().Hash) service.settings.Logger().DebugContext(ctx, "skipping alertmanager sync for org", "org_id", org.ID.StringValue(), "hash", config.StoreableConfig().Hash)
continue continue
} }
err = service.servers[orgID].SetConfig(ctx, config) err = service.servers[org.ID.StringValue()].SetConfig(ctx, config)
if err != nil { if err != nil {
service.settings.Logger().ErrorContext(ctx, "failed to set config for alertmanager server", "org_id", orgID, "error", err) service.settings.Logger().ErrorContext(ctx, "failed to set config for alertmanager server", "org_id", org.ID.StringValue(), "error", err)
continue continue
} }
} }

View File

@ -8,6 +8,7 @@ import (
"github.com/SigNoz/signoz/pkg/alertmanager/alertmanagerstore/sqlalertmanagerstore" "github.com/SigNoz/signoz/pkg/alertmanager/alertmanagerstore/sqlalertmanagerstore"
"github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/factory" "github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/sqlstore" "github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types/alertmanagertypes" "github.com/SigNoz/signoz/pkg/types/alertmanagertypes"
"github.com/SigNoz/signoz/pkg/valuer" "github.com/SigNoz/signoz/pkg/valuer"
@ -22,13 +23,13 @@ type provider struct {
stopC chan struct{} stopC chan struct{}
} }
func NewFactory(sqlstore sqlstore.SQLStore) factory.ProviderFactory[alertmanager.Alertmanager, alertmanager.Config] { func NewFactory(sqlstore sqlstore.SQLStore, orgGetter organization.Getter) factory.ProviderFactory[alertmanager.Alertmanager, alertmanager.Config] {
return factory.NewProviderFactory(factory.MustNewName("signoz"), func(ctx context.Context, settings factory.ProviderSettings, config alertmanager.Config) (alertmanager.Alertmanager, error) { return factory.NewProviderFactory(factory.MustNewName("signoz"), func(ctx context.Context, settings factory.ProviderSettings, config alertmanager.Config) (alertmanager.Alertmanager, error) {
return New(ctx, settings, config, sqlstore) return New(ctx, settings, config, sqlstore, orgGetter)
}) })
} }
func New(ctx context.Context, providerSettings factory.ProviderSettings, config alertmanager.Config, sqlstore sqlstore.SQLStore) (*provider, error) { func New(ctx context.Context, providerSettings factory.ProviderSettings, config alertmanager.Config, sqlstore sqlstore.SQLStore, orgGetter organization.Getter) (*provider, error) {
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/alertmanager/signozalertmanager") settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/alertmanager/signozalertmanager")
configStore := sqlalertmanagerstore.NewConfigStore(sqlstore) configStore := sqlalertmanagerstore.NewConfigStore(sqlstore)
stateStore := sqlalertmanagerstore.NewStateStore(sqlstore) stateStore := sqlalertmanagerstore.NewStateStore(sqlstore)
@ -40,6 +41,7 @@ func New(ctx context.Context, providerSettings factory.ProviderSettings, config
config.Signoz.Config, config.Signoz.Config,
stateStore, stateStore,
configStore, configStore,
orgGetter,
), ),
settings: settings, settings: settings,
config: config, config: config,

View File

@ -5,9 +5,15 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/SigNoz/signoz/pkg/sharder"
"github.com/SigNoz/signoz/pkg/sqlstore" "github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types" "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/valuer"
)
const (
apiKeyCrossOrgMessage string = "::API-KEY-CROSS-ORG::"
) )
type APIKey struct { type APIKey struct {
@ -15,10 +21,11 @@ type APIKey struct {
uuid *authtypes.UUID uuid *authtypes.UUID
headers []string headers []string
logger *slog.Logger logger *slog.Logger
sharder sharder.Sharder
} }
func NewAPIKey(store sqlstore.SQLStore, headers []string, logger *slog.Logger) *APIKey { func NewAPIKey(store sqlstore.SQLStore, headers []string, logger *slog.Logger, sharder sharder.Sharder) *APIKey {
return &APIKey{store: store, uuid: authtypes.NewUUID(), headers: headers, logger: logger} return &APIKey{store: store, uuid: authtypes.NewUUID(), headers: headers, logger: logger, sharder: sharder}
} }
func (a *APIKey) Wrap(next http.Handler) http.Handler { func (a *APIKey) Wrap(next http.Handler) http.Handler {
@ -36,13 +43,20 @@ func (a *APIKey) Wrap(next http.Handler) http.Handler {
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
return return
} }
apiKeyToken, ok := authtypes.UUIDFromContext(ctx) apiKeyToken, ok := authtypes.UUIDFromContext(ctx)
if !ok { if !ok {
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
return return
} }
err = a.store.BunDB().NewSelect().Model(&apiKey).Where("token = ?", apiKeyToken).Scan(r.Context()) err = a.
store.
BunDB().
NewSelect().
Model(&apiKey).
Where("token = ?", apiKeyToken).
Scan(r.Context())
if err != nil { if err != nil {
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
return return
@ -71,6 +85,18 @@ func (a *APIKey) Wrap(next http.Handler) http.Handler {
ctx = authtypes.NewContextWithClaims(ctx, jwt) ctx = authtypes.NewContextWithClaims(ctx, jwt)
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
next.ServeHTTP(w, r)
return
}
if err := a.sharder.IsMyOwnedKey(r.Context(), types.NewOrganizationKey(valuer.MustNewUUID(claims.OrgID))); err != nil {
a.logger.ErrorContext(r.Context(), apiKeyCrossOrgMessage, "claims", claims, "error", err)
next.ServeHTTP(w, r)
return
}
r = r.WithContext(ctx) r = r.WithContext(ctx)
next.ServeHTTP(w, r) next.ServeHTTP(w, r)

View File

@ -1,18 +1,28 @@
package middleware package middleware
import ( import (
"log/slog"
"net/http" "net/http"
"github.com/SigNoz/signoz/pkg/sharder"
"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/valuer"
)
const (
authCrossOrgMessage string = "::AUTH-CROSS-ORG::"
) )
type Auth struct { type Auth struct {
jwt *authtypes.JWT jwt *authtypes.JWT
headers []string headers []string
sharder sharder.Sharder
logger *slog.Logger
} }
func NewAuth(jwt *authtypes.JWT, headers []string) *Auth { func NewAuth(jwt *authtypes.JWT, headers []string, sharder sharder.Sharder, logger *slog.Logger) *Auth {
return &Auth{jwt: jwt, headers: headers} return &Auth{jwt: jwt, headers: headers, sharder: sharder, logger: logger}
} }
func (a *Auth) Wrap(next http.Handler) http.Handler { func (a *Auth) Wrap(next http.Handler) http.Handler {
@ -28,6 +38,18 @@ func (a *Auth) Wrap(next http.Handler) http.Handler {
return return
} }
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
next.ServeHTTP(w, r)
return
}
if err := a.sharder.IsMyOwnedKey(r.Context(), types.NewOrganizationKey(valuer.MustNewUUID(claims.OrgID))); err != nil {
a.logger.ErrorContext(r.Context(), authCrossOrgMessage, "claims", claims, "error", err)
next.ServeHTTP(w, r)
return
}
r = r.WithContext(ctx) r = r.WithContext(ctx)
next.ServeHTTP(w, r) next.ServeHTTP(w, r)

View File

@ -4,13 +4,13 @@ import (
"context" "context"
"net/http" "net/http"
"github.com/SigNoz/signoz/pkg/types" "github.com/SigNoz/signoz/pkg/types/apdextypes"
) )
type Module interface { type Module interface {
Get(context.Context, string, []string) ([]*types.ApdexSettings, error) Get(context.Context, string, []string) ([]*apdextypes.Settings, error)
Set(context.Context, string, *types.ApdexSettings) error Set(context.Context, string, *apdextypes.Settings) error
} }
type Handler interface { type Handler interface {

View File

@ -9,7 +9,7 @@ import (
"github.com/SigNoz/signoz/pkg/http/render" "github.com/SigNoz/signoz/pkg/http/render"
"github.com/SigNoz/signoz/pkg/modules/apdex" "github.com/SigNoz/signoz/pkg/modules/apdex"
"github.com/SigNoz/signoz/pkg/types" "github.com/SigNoz/signoz/pkg/types/apdextypes"
"github.com/SigNoz/signoz/pkg/types/authtypes" "github.com/SigNoz/signoz/pkg/types/authtypes"
) )
@ -31,7 +31,7 @@ func (handler *handler) Set(rw http.ResponseWriter, req *http.Request) {
return return
} }
var apdexSettings types.ApdexSettings var apdexSettings apdextypes.Settings
if err := json.NewDecoder(req.Body).Decode(&apdexSettings); err != nil { if err := json.NewDecoder(req.Body).Decode(&apdexSettings); err != nil {
render.Error(rw, err) render.Error(rw, err)
return return

View File

@ -6,7 +6,7 @@ import (
"github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/modules/apdex" "github.com/SigNoz/signoz/pkg/modules/apdex"
"github.com/SigNoz/signoz/pkg/sqlstore" "github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types" "github.com/SigNoz/signoz/pkg/types/apdextypes"
"github.com/SigNoz/signoz/pkg/valuer" "github.com/SigNoz/signoz/pkg/valuer"
"github.com/uptrace/bun" "github.com/uptrace/bun"
) )
@ -25,8 +25,8 @@ func NewModule(sqlstore sqlstore.SQLStore) apdex.Module {
} }
} }
func (module *module) Get(ctx context.Context, orgID string, services []string) ([]*types.ApdexSettings, error) { func (module *module) Get(ctx context.Context, orgID string, services []string) ([]*apdextypes.Settings, error) {
var apdexSettings []*types.ApdexSettings var apdexSettings []*apdextypes.Settings
err := module. err := module.
sqlstore. sqlstore.
@ -51,7 +51,7 @@ func (module *module) Get(ctx context.Context, orgID string, services []string)
} }
if !found { if !found {
apdexSettings = append(apdexSettings, &types.ApdexSettings{ apdexSettings = append(apdexSettings, &apdextypes.Settings{
ServiceName: service, ServiceName: service,
Threshold: defaultApdexThreshold, Threshold: defaultApdexThreshold,
}) })
@ -61,7 +61,7 @@ func (module *module) Get(ctx context.Context, orgID string, services []string)
return apdexSettings, nil return apdexSettings, nil
} }
func (module *module) Set(ctx context.Context, orgID string, apdexSettings *types.ApdexSettings) error { func (module *module) Set(ctx context.Context, orgID string, apdexSettings *apdextypes.Settings) error {
apdexSettings.OrgID = orgID apdexSettings.OrgID = orgID
apdexSettings.Identifiable.ID = valuer.GenerateUUID() apdexSettings.Identifiable.ID = valuer.GenerateUUID()

View File

@ -0,0 +1,36 @@
package implorganization
import (
"context"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/sharder"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/valuer"
)
type getter struct {
store types.OrganizationStore
sharder sharder.Sharder
}
func NewGetter(store types.OrganizationStore, sharder sharder.Sharder) organization.Getter {
return &getter{store: store, sharder: sharder}
}
func (module *getter) Get(ctx context.Context, id valuer.UUID) (*types.Organization, error) {
return module.store.Get(ctx, id)
}
func (module *getter) List(ctx context.Context) ([]*types.Organization, error) {
return module.store.GetAll(ctx)
}
func (module *getter) ListByOwnedKeyRange(ctx context.Context) ([]*types.Organization, error) {
start, end, err := module.sharder.GetMyOwnedKeyRange(ctx)
if err != nil {
return nil, err
}
return module.store.ListByKeyRange(ctx, start, end)
}

View File

@ -15,11 +15,12 @@ import (
) )
type handler struct { type handler struct {
module organization.Module orgGetter organization.Getter
orgSetter organization.Setter
} }
func NewHandler(module organization.Module) organization.Handler { func NewHandler(orgGetter organization.Getter, orgSetter organization.Setter) organization.Handler {
return &handler{module: module} return &handler{orgGetter: orgGetter, orgSetter: orgSetter}
} }
func (handler *handler) Get(rw http.ResponseWriter, r *http.Request) { func (handler *handler) Get(rw http.ResponseWriter, r *http.Request) {
@ -38,7 +39,7 @@ func (handler *handler) Get(rw http.ResponseWriter, r *http.Request) {
return return
} }
organization, err := handler.module.Get(ctx, orgID) organization, err := handler.orgGetter.Get(ctx, orgID)
if err != nil { if err != nil {
render.Error(rw, err) render.Error(rw, err)
return return
@ -70,7 +71,7 @@ func (handler *handler) Update(rw http.ResponseWriter, r *http.Request) {
} }
req.ID = orgID req.ID = orgID
err = handler.module.Update(ctx, req) err = handler.orgSetter.Update(ctx, req)
if err != nil { if err != nil {
render.Error(rw, err) render.Error(rw, err)
return return

View File

@ -1,33 +0,0 @@
package implorganization
import (
"context"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/valuer"
)
type module struct {
store types.OrganizationStore
}
func NewModule(organizationStore types.OrganizationStore) organization.Module {
return &module{store: organizationStore}
}
func (module *module) Create(ctx context.Context, organization *types.Organization) error {
return module.store.Create(ctx, organization)
}
func (module *module) Get(ctx context.Context, id valuer.UUID) (*types.Organization, error) {
return module.store.Get(ctx, id)
}
func (module *module) GetAll(ctx context.Context) ([]*types.Organization, error) {
return module.store.GetAll(ctx)
}
func (module *module) Update(ctx context.Context, updatedOrganization *types.Organization) error {
return module.store.Update(ctx, updatedOrganization)
}

View File

@ -0,0 +1,40 @@
package implorganization
import (
"context"
"github.com/SigNoz/signoz/pkg/alertmanager"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/modules/quickfilter"
"github.com/SigNoz/signoz/pkg/types"
)
type setter struct {
store types.OrganizationStore
alertmanager alertmanager.Alertmanager
quickfilter quickfilter.Module
}
func NewSetter(store types.OrganizationStore, alertmanager alertmanager.Alertmanager, quickfilter quickfilter.Module) organization.Setter {
return &setter{store: store, alertmanager: alertmanager, quickfilter: quickfilter}
}
func (module *setter) Create(ctx context.Context, organization *types.Organization) error {
if err := module.store.Create(ctx, organization); err != nil {
return err
}
if err := module.alertmanager.SetDefaultConfig(ctx, organization.ID.StringValue()); err != nil {
return err
}
if err := module.quickfilter.SetDefaultConfig(ctx, organization.ID); err != nil {
return err
}
return nil
}
func (module *setter) Update(ctx context.Context, updatedOrganization *types.Organization) error {
return module.store.Update(ctx, updatedOrganization)
}

View File

@ -92,3 +92,20 @@ func (store *store) Delete(ctx context.Context, id valuer.UUID) error {
return nil return nil
} }
func (store *store) ListByKeyRange(ctx context.Context, start, end uint32) ([]*types.Organization, error) {
organizations := make([]*types.Organization, 0)
err := store.
sqlstore.
BunDB().
NewSelect().
Model(&organizations).
Where("key >= ?", start).
Where("key <= ?", end).
Scan(ctx)
if err != nil {
return nil, err
}
return organizations, nil
}

View File

@ -8,17 +8,22 @@ import (
"github.com/SigNoz/signoz/pkg/valuer" "github.com/SigNoz/signoz/pkg/valuer"
) )
type Module interface { type Getter interface {
// Create creates the given organization
Create(context.Context, *types.Organization) error
// Get gets the organization based on the given id // Get gets the organization based on the given id
Get(context.Context, valuer.UUID) (*types.Organization, error) Get(context.Context, valuer.UUID) (*types.Organization, error)
// GetAll gets all the organizations // Lists all the organizations
GetAll(context.Context) ([]*types.Organization, error) List(context.Context) ([]*types.Organization, error)
// Update updates the given organization based on id present // ListByOwnedKeyRange gets all the organizations owned by the instance
ListByOwnedKeyRange(context.Context) ([]*types.Organization, error)
}
type Setter interface {
// Create creates the given organization
Create(context.Context, *types.Organization) error
// Update updates the given organization
Update(context.Context, *types.Organization) error Update(context.Context, *types.Organization) error
} }

View File

@ -0,0 +1,235 @@
package impltracefunnel
import (
"encoding/json"
"net/http"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/http/render"
"github.com/SigNoz/signoz/pkg/modules/tracefunnel"
"github.com/SigNoz/signoz/pkg/types/authtypes"
tf "github.com/SigNoz/signoz/pkg/types/tracefunneltypes"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/gorilla/mux"
)
type handler struct {
module tracefunnel.Module
}
func NewHandler(module tracefunnel.Module) tracefunnel.Handler {
return &handler{module: module}
}
func (handler *handler) New(rw http.ResponseWriter, r *http.Request) {
var req tf.PostableFunnel
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
render.Error(rw, err)
return
}
claims, err := authtypes.ClaimsFromContext(r.Context())
if err != nil {
render.Error(rw, err)
return
}
funnel, err := handler.module.Create(r.Context(), req.Timestamp, req.Name, valuer.MustNewUUID(claims.UserID), valuer.MustNewUUID(claims.OrgID))
if err != nil {
render.Error(rw, errors.Newf(errors.TypeInvalidInput,
errors.CodeInvalidInput,
"failed to create funnel: %v", err))
return
}
response := tf.ConstructFunnelResponse(funnel, &claims)
render.Success(rw, http.StatusOK, response)
}
func (handler *handler) UpdateSteps(rw http.ResponseWriter, r *http.Request) {
var req tf.PostableFunnel
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
render.Error(rw, err)
return
}
claims, err := authtypes.ClaimsFromContext(r.Context())
if err != nil {
render.Error(rw, err)
return
}
updatedAt, err := tf.ValidateAndConvertTimestamp(req.Timestamp)
if err != nil {
render.Error(rw, err)
return
}
funnel, err := handler.module.Get(r.Context(), req.FunnelID, valuer.MustNewUUID(claims.OrgID))
if err != nil {
render.Error(rw, errors.Newf(errors.TypeInvalidInput,
errors.CodeInvalidInput,
"funnel not found: %v", err))
return
}
steps, err := tf.ProcessFunnelSteps(req.Steps)
if err != nil {
render.Error(rw, err)
return
}
funnel.Steps = steps
funnel.UpdatedAt = updatedAt
funnel.UpdatedBy = claims.UserID
if req.Name != "" {
funnel.Name = req.Name
}
if req.Description != "" {
funnel.Description = req.Description
}
if err := handler.module.Update(r.Context(), funnel, valuer.MustNewUUID(claims.UserID)); err != nil {
render.Error(rw, errors.Newf(errors.TypeInvalidInput,
errors.CodeInvalidInput,
"failed to update funnel in database: %v", err))
return
}
updatedFunnel, err := handler.module.Get(r.Context(), funnel.ID, valuer.MustNewUUID(claims.OrgID))
if err != nil {
render.Error(rw, errors.Newf(errors.TypeInvalidInput,
errors.CodeInvalidInput,
"failed to get updated funnel: %v", err))
return
}
response := tf.ConstructFunnelResponse(updatedFunnel, &claims)
render.Success(rw, http.StatusOK, response)
}
func (handler *handler) UpdateFunnel(rw http.ResponseWriter, r *http.Request) {
var req tf.PostableFunnel
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
render.Error(rw, err)
return
}
claims, err := authtypes.ClaimsFromContext(r.Context())
if err != nil {
render.Error(rw, err)
return
}
updatedAt, err := tf.ValidateAndConvertTimestamp(req.Timestamp)
if err != nil {
render.Error(rw, err)
return
}
vars := mux.Vars(r)
funnelID := vars["funnel_id"]
funnel, err := handler.module.Get(r.Context(), valuer.MustNewUUID(funnelID), valuer.MustNewUUID(claims.OrgID))
if err != nil {
render.Error(rw, errors.Newf(errors.TypeInvalidInput,
errors.CodeInvalidInput,
"funnel not found: %v", err))
return
}
funnel.UpdatedAt = updatedAt
funnel.UpdatedBy = claims.UserID
if req.Name != "" {
funnel.Name = req.Name
}
if req.Description != "" {
funnel.Description = req.Description
}
if err := handler.module.Update(r.Context(), funnel, valuer.MustNewUUID(claims.UserID)); err != nil {
render.Error(rw, errors.Newf(errors.TypeInvalidInput,
errors.CodeInvalidInput,
"failed to update funnel in database: %v", err))
return
}
updatedFunnel, err := handler.module.Get(r.Context(), funnel.ID, valuer.MustNewUUID(claims.OrgID))
if err != nil {
render.Error(rw, errors.Newf(errors.TypeInvalidInput,
errors.CodeInvalidInput,
"failed to get updated funnel: %v", err))
return
}
response := tf.ConstructFunnelResponse(updatedFunnel, &claims)
render.Success(rw, http.StatusOK, response)
}
func (handler *handler) List(rw http.ResponseWriter, r *http.Request) {
claims, err := authtypes.ClaimsFromContext(r.Context())
if err != nil {
render.Error(rw, err)
return
}
funnels, err := handler.module.List(r.Context(), valuer.MustNewUUID(claims.OrgID))
if err != nil {
render.Error(rw, errors.Newf(errors.TypeInvalidInput,
errors.CodeInvalidInput,
"failed to list funnels: %v", err))
return
}
var response []tf.GettableFunnel
for _, f := range funnels {
response = append(response, tf.ConstructFunnelResponse(f, &claims))
}
render.Success(rw, http.StatusOK, response)
}
func (handler *handler) Get(rw http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
funnelID := vars["funnel_id"]
claims, err := authtypes.ClaimsFromContext(r.Context())
if err != nil {
render.Error(rw, err)
return
}
funnel, err := handler.module.Get(r.Context(), valuer.MustNewUUID(funnelID), valuer.MustNewUUID(claims.OrgID))
if err != nil {
render.Error(rw, errors.Newf(errors.TypeInvalidInput,
errors.CodeInvalidInput,
"funnel not found: %v", err))
return
}
response := tf.ConstructFunnelResponse(funnel, &claims)
render.Success(rw, http.StatusOK, response)
}
func (handler *handler) Delete(rw http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
funnelID := vars["funnel_id"]
claims, err := authtypes.ClaimsFromContext(r.Context())
if err != nil {
render.Error(rw, err)
return
}
if err := handler.module.Delete(r.Context(), valuer.MustNewUUID(funnelID), valuer.MustNewUUID(claims.OrgID)); err != nil {
render.Error(rw, errors.Newf(errors.TypeInvalidInput,
errors.CodeInvalidInput,
"failed to delete funnel: %v", err))
return
}
render.Success(rw, http.StatusOK, nil)
}

View File

@ -0,0 +1,173 @@
package impltracefunnel
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/authtypes"
traceFunnels "github.com/SigNoz/signoz/pkg/types/tracefunneltypes"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
type MockModule struct {
mock.Mock
}
func (m *MockModule) Create(ctx context.Context, timestamp int64, name string, userID valuer.UUID, orgID valuer.UUID) (*traceFunnels.StorableFunnel, error) {
args := m.Called(ctx, timestamp, name, userID, orgID)
return args.Get(0).(*traceFunnels.StorableFunnel), args.Error(1)
}
func (m *MockModule) Get(ctx context.Context, funnelID valuer.UUID, orgID valuer.UUID) (*traceFunnels.StorableFunnel, error) {
args := m.Called(ctx, funnelID, orgID)
return args.Get(0).(*traceFunnels.StorableFunnel), args.Error(1)
}
func (m *MockModule) Update(ctx context.Context, funnel *traceFunnels.StorableFunnel, userID valuer.UUID) error {
args := m.Called(ctx, funnel, userID)
return args.Error(0)
}
func (m *MockModule) List(ctx context.Context, orgID valuer.UUID) ([]*traceFunnels.StorableFunnel, error) {
args := m.Called(ctx, orgID)
return args.Get(0).([]*traceFunnels.StorableFunnel), args.Error(1)
}
func (m *MockModule) Delete(ctx context.Context, funnelID valuer.UUID, orgID valuer.UUID) error {
args := m.Called(ctx, funnelID, orgID)
return args.Error(0)
}
func (m *MockModule) Save(ctx context.Context, funnel *traceFunnels.StorableFunnel, userID valuer.UUID, orgID valuer.UUID) error {
args := m.Called(ctx, funnel, userID, orgID)
return args.Error(0)
}
func (m *MockModule) GetFunnelMetadata(ctx context.Context, funnelID valuer.UUID, orgID valuer.UUID) (int64, int64, string, error) {
args := m.Called(ctx, funnelID, orgID)
return args.Get(0).(int64), args.Get(1).(int64), args.String(2), args.Error(3)
}
func TestHandler_List(t *testing.T) {
mockModule := new(MockModule)
handler := NewHandler(mockModule)
req := httptest.NewRequest(http.MethodGet, "/api/v1/trace-funnels/list", nil)
orgID := valuer.GenerateUUID()
claims := authtypes.Claims{
OrgID: orgID.String(),
}
req = req.WithContext(authtypes.NewContextWithClaims(req.Context(), claims))
rr := httptest.NewRecorder()
funnel1ID := valuer.GenerateUUID()
funnel2ID := valuer.GenerateUUID()
expectedFunnels := []*traceFunnels.StorableFunnel{
{
Identifiable: types.Identifiable{
ID: funnel1ID,
},
Name: "funnel-1",
OrgID: orgID,
},
{
Identifiable: types.Identifiable{
ID: funnel2ID,
},
Name: "funnel-2",
OrgID: orgID,
},
}
mockModule.On("List", req.Context(), orgID).Return(expectedFunnels, nil)
handler.List(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
var response struct {
Status string `json:"status"`
Data []traceFunnels.GettableFunnel `json:"data"`
}
err := json.Unmarshal(rr.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Equal(t, "success", response.Status)
assert.Len(t, response.Data, 2)
assert.Equal(t, "funnel-1", response.Data[0].FunnelName)
assert.Equal(t, "funnel-2", response.Data[1].FunnelName)
mockModule.AssertExpectations(t)
}
func TestHandler_Get(t *testing.T) {
mockModule := new(MockModule)
handler := NewHandler(mockModule)
funnelID := valuer.GenerateUUID()
orgID := valuer.GenerateUUID()
req := httptest.NewRequest(http.MethodGet, "/api/v1/trace-funnels/"+funnelID.String(), nil)
req = mux.SetURLVars(req, map[string]string{"funnel_id": funnelID.String()})
req = req.WithContext(authtypes.NewContextWithClaims(req.Context(), authtypes.Claims{
OrgID: orgID.String(),
}))
rr := httptest.NewRecorder()
expectedFunnel := &traceFunnels.StorableFunnel{
Identifiable: types.Identifiable{
ID: funnelID,
},
Name: "test-funnel",
OrgID: orgID,
}
mockModule.On("Get", req.Context(), funnelID, orgID).Return(expectedFunnel, nil)
handler.Get(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
var response struct {
Status string `json:"status"`
Data traceFunnels.GettableFunnel `json:"data"`
}
err := json.Unmarshal(rr.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Equal(t, "success", response.Status)
assert.Equal(t, "test-funnel", response.Data.FunnelName)
assert.Equal(t, expectedFunnel.OrgID.String(), response.Data.OrgID)
mockModule.AssertExpectations(t)
}
func TestHandler_Delete(t *testing.T) {
mockModule := new(MockModule)
handler := NewHandler(mockModule)
funnelID := valuer.GenerateUUID()
orgID := valuer.GenerateUUID()
req := httptest.NewRequest(http.MethodDelete, "/api/v1/trace-funnels/"+funnelID.String(), nil)
req = mux.SetURLVars(req, map[string]string{"funnel_id": funnelID.String()})
req = req.WithContext(authtypes.NewContextWithClaims(req.Context(), authtypes.Claims{
OrgID: orgID.String(),
}))
rr := httptest.NewRecorder()
mockModule.On("Delete", req.Context(), funnelID, orgID).Return(nil)
handler.Delete(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
mockModule.AssertExpectations(t)
}

View File

@ -0,0 +1,96 @@
package impltracefunnel
import (
"context"
"fmt"
"time"
"github.com/SigNoz/signoz/pkg/modules/tracefunnel"
"github.com/SigNoz/signoz/pkg/types"
traceFunnels "github.com/SigNoz/signoz/pkg/types/tracefunneltypes"
"github.com/SigNoz/signoz/pkg/valuer"
)
type module struct {
store traceFunnels.FunnelStore
}
func NewModule(store traceFunnels.FunnelStore) tracefunnel.Module {
return &module{
store: store,
}
}
func (module *module) Create(ctx context.Context, timestamp int64, name string, userID valuer.UUID, orgID valuer.UUID) (*traceFunnels.StorableFunnel, error) {
funnel := &traceFunnels.StorableFunnel{
Name: name,
OrgID: orgID,
}
funnel.CreatedAt = time.Unix(0, timestamp*1000000) // Convert to nanoseconds
funnel.CreatedBy = userID.String()
// Set up the user relationship
funnel.CreatedByUser = &types.User{
Identifiable: types.Identifiable{
ID: userID,
},
}
if funnel.ID.IsZero() {
funnel.ID = valuer.GenerateUUID()
}
if funnel.CreatedAt.IsZero() {
funnel.CreatedAt = time.Now()
}
if funnel.UpdatedAt.IsZero() {
funnel.UpdatedAt = time.Now()
}
// Set created_by if CreatedByUser is present
if funnel.CreatedByUser != nil {
funnel.CreatedBy = funnel.CreatedByUser.Identifiable.ID.String()
}
if err := module.store.Create(ctx, funnel); err != nil {
return nil, fmt.Errorf("failed to create funnel: %v", err)
}
return funnel, nil
}
// Get gets a funnel by ID
func (module *module) Get(ctx context.Context, funnelID valuer.UUID, orgID valuer.UUID) (*traceFunnels.StorableFunnel, error) {
return module.store.Get(ctx, funnelID, orgID)
}
// Update updates a funnel
func (module *module) Update(ctx context.Context, funnel *traceFunnels.StorableFunnel, userID valuer.UUID) error {
funnel.UpdatedBy = userID.String()
return module.store.Update(ctx, funnel)
}
// List lists all funnels for an organization
func (module *module) List(ctx context.Context, orgID valuer.UUID) ([]*traceFunnels.StorableFunnel, error) {
funnels, err := module.store.List(ctx, orgID)
if err != nil {
return nil, fmt.Errorf("failed to list funnels: %v", err)
}
return funnels, nil
}
// Delete deletes a funnel
func (module *module) Delete(ctx context.Context, funnelID valuer.UUID, orgID valuer.UUID) error {
return module.store.Delete(ctx, funnelID, orgID)
}
// GetFunnelMetadata gets metadata for a funnel
func (module *module) GetFunnelMetadata(ctx context.Context, funnelID valuer.UUID, orgID valuer.UUID) (int64, int64, string, error) {
funnel, err := module.store.Get(ctx, funnelID, orgID)
if err != nil {
return 0, 0, "", err
}
return funnel.CreatedAt.UnixNano() / 1000000, funnel.UpdatedAt.UnixNano() / 1000000, funnel.Description, nil
}

View File

@ -0,0 +1,114 @@
package impltracefunnel
import (
"context"
"time"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/sqlstore"
traceFunnels "github.com/SigNoz/signoz/pkg/types/tracefunneltypes"
"github.com/SigNoz/signoz/pkg/valuer"
)
type store struct {
sqlstore sqlstore.SQLStore
}
func NewStore(sqlstore sqlstore.SQLStore) traceFunnels.FunnelStore {
return &store{sqlstore: sqlstore}
}
func (store *store) Create(ctx context.Context, funnel *traceFunnels.StorableFunnel) error {
// Check if a funnel with the same name already exists in the organization
exists, err := store.
sqlstore.
BunDB().
NewSelect().
Model(new(traceFunnels.StorableFunnel)).
Where("name = ? AND org_id = ?", funnel.Name, funnel.OrgID.String()).
Exists(ctx)
if err != nil {
return errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to check for existing funnelr")
}
if exists {
return store.sqlstore.WrapAlreadyExistsErrf(nil, traceFunnels.ErrFunnelAlreadyExists, "a funnel with name '%s' already exists in this organization", funnel.Name)
}
_, err = store.
sqlstore.
BunDB().
NewInsert().
Model(funnel).
Exec(ctx)
if err != nil {
return errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to create funnels")
}
return nil
}
// Get retrieves a funnel by ID
func (store *store) Get(ctx context.Context, uuid valuer.UUID, orgID valuer.UUID) (*traceFunnels.StorableFunnel, error) {
funnel := &traceFunnels.StorableFunnel{}
err := store.
sqlstore.
BunDB().
NewSelect().
Model(funnel).
Relation("CreatedByUser").
Where("?TableAlias.id = ? AND ?TableAlias.org_id = ?", uuid.String(), orgID.String()).
Scan(ctx)
if err != nil {
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to get funnels")
}
return funnel, nil
}
// Update updates an existing funnel
func (store *store) Update(ctx context.Context, funnel *traceFunnels.StorableFunnel) error {
funnel.UpdatedAt = time.Now()
_, err := store.
sqlstore.
BunDB().
NewUpdate().
Model(funnel).
WherePK().
Exec(ctx)
if err != nil {
return store.sqlstore.WrapAlreadyExistsErrf(err, traceFunnels.ErrFunnelAlreadyExists, "a funnel with name '%s' already exists in this organization", funnel.Name)
}
return nil
}
// List retrieves all funnels for a given organization
func (store *store) List(ctx context.Context, orgID valuer.UUID) ([]*traceFunnels.StorableFunnel, error) {
var funnels []*traceFunnels.StorableFunnel
err := store.
sqlstore.
BunDB().
NewSelect().
Model(&funnels).
Relation("CreatedByUser").
Where("?TableAlias.org_id = ?", orgID.String()).
Scan(ctx)
if err != nil {
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to list funnels")
}
return funnels, nil
}
// Delete removes a funnel by ID
func (store *store) Delete(ctx context.Context, funnelID valuer.UUID, orgID valuer.UUID) error {
_, err := store.
sqlstore.
BunDB().
NewDelete().
Model(new(traceFunnels.StorableFunnel)).
Where("id = ? AND org_id = ?", funnelID.String(), orgID.String()).
Exec(ctx)
if err != nil {
return errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to delete funnel")
}
return nil
}

View File

@ -0,0 +1,38 @@
package tracefunnel
import (
"context"
"github.com/SigNoz/signoz/pkg/valuer"
"net/http"
traceFunnels "github.com/SigNoz/signoz/pkg/types/tracefunneltypes"
)
// Module defines the interface for trace funnel operations
type Module interface {
Create(ctx context.Context, timestamp int64, name string, userID valuer.UUID, orgID valuer.UUID) (*traceFunnels.StorableFunnel, error)
Get(ctx context.Context, funnelID valuer.UUID, orgID valuer.UUID) (*traceFunnels.StorableFunnel, error)
Update(ctx context.Context, funnel *traceFunnels.StorableFunnel, userID valuer.UUID) error
List(ctx context.Context, orgID valuer.UUID) ([]*traceFunnels.StorableFunnel, error)
Delete(ctx context.Context, funnelID valuer.UUID, orgID valuer.UUID) error
GetFunnelMetadata(ctx context.Context, funnelID valuer.UUID, orgID valuer.UUID) (int64, int64, string, error)
}
type Handler interface {
New(http.ResponseWriter, *http.Request)
UpdateSteps(http.ResponseWriter, *http.Request)
UpdateFunnel(http.ResponseWriter, *http.Request)
List(http.ResponseWriter, *http.Request)
Get(http.ResponseWriter, *http.Request)
Delete(http.ResponseWriter, *http.Request)
}

View File

@ -0,0 +1,183 @@
package tracefunneltest
import (
"context"
"testing"
"time"
"github.com/SigNoz/signoz/pkg/modules/tracefunnel/impltracefunnel"
"github.com/SigNoz/signoz/pkg/types"
traceFunnels "github.com/SigNoz/signoz/pkg/types/tracefunneltypes"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
type MockStore struct {
mock.Mock
}
func (m *MockStore) Create(ctx context.Context, funnel *traceFunnels.StorableFunnel) error {
args := m.Called(ctx, funnel)
return args.Error(0)
}
func (m *MockStore) Get(ctx context.Context, uuid valuer.UUID, orgID valuer.UUID) (*traceFunnels.StorableFunnel, error) {
args := m.Called(ctx, uuid, orgID)
return args.Get(0).(*traceFunnels.StorableFunnel), args.Error(1)
}
func (m *MockStore) List(ctx context.Context, orgID valuer.UUID) ([]*traceFunnels.StorableFunnel, error) {
args := m.Called(ctx, orgID)
return args.Get(0).([]*traceFunnels.StorableFunnel), args.Error(1)
}
func (m *MockStore) Update(ctx context.Context, funnel *traceFunnels.StorableFunnel) error {
args := m.Called(ctx, funnel)
return args.Error(0)
}
func (m *MockStore) Delete(ctx context.Context, uuid valuer.UUID, orgID valuer.UUID) error {
args := m.Called(ctx, uuid, orgID)
return args.Error(0)
}
func TestModule_Create(t *testing.T) {
mockStore := new(MockStore)
module := impltracefunnel.NewModule(mockStore)
ctx := context.Background()
timestamp := time.Now().UnixMilli()
name := "test-funnel"
userID := valuer.GenerateUUID()
orgID := valuer.GenerateUUID()
mockStore.On("Create", ctx, mock.MatchedBy(func(f *traceFunnels.StorableFunnel) bool {
return f.Name == name &&
f.CreatedBy == userID.String() &&
f.OrgID == orgID &&
f.CreatedByUser != nil &&
f.CreatedByUser.ID == userID &&
f.CreatedAt.UnixNano()/1000000 == timestamp
})).Return(nil)
funnel, err := module.Create(ctx, timestamp, name, userID, orgID)
assert.NoError(t, err)
assert.NotNil(t, funnel)
assert.Equal(t, name, funnel.Name)
assert.Equal(t, userID.String(), funnel.CreatedBy)
assert.Equal(t, orgID, funnel.OrgID)
assert.NotNil(t, funnel.CreatedByUser)
assert.Equal(t, userID, funnel.CreatedByUser.ID)
mockStore.AssertExpectations(t)
}
func TestModule_Get(t *testing.T) {
mockStore := new(MockStore)
module := impltracefunnel.NewModule(mockStore)
ctx := context.Background()
funnelID := valuer.GenerateUUID()
orgID := valuer.GenerateUUID()
expectedFunnel := &traceFunnels.StorableFunnel{
Name: "test-funnel",
}
mockStore.On("Get", ctx, funnelID, orgID).Return(expectedFunnel, nil)
funnel, err := module.Get(ctx, funnelID, orgID)
assert.NoError(t, err)
assert.Equal(t, expectedFunnel, funnel)
mockStore.AssertExpectations(t)
}
func TestModule_Update(t *testing.T) {
mockStore := new(MockStore)
module := impltracefunnel.NewModule(mockStore)
ctx := context.Background()
userID := valuer.GenerateUUID()
funnel := &traceFunnels.StorableFunnel{
Name: "test-funnel",
}
mockStore.On("Update", ctx, funnel).Return(nil)
err := module.Update(ctx, funnel, userID)
assert.NoError(t, err)
assert.Equal(t, userID.String(), funnel.UpdatedBy)
mockStore.AssertExpectations(t)
}
func TestModule_List(t *testing.T) {
mockStore := new(MockStore)
module := impltracefunnel.NewModule(mockStore)
ctx := context.Background()
orgID := valuer.GenerateUUID()
expectedFunnels := []*traceFunnels.StorableFunnel{
{
Name: "funnel-1",
OrgID: orgID,
},
{
Name: "funnel-2",
OrgID: orgID,
},
}
mockStore.On("List", ctx, orgID).Return(expectedFunnels, nil)
funnels, err := module.List(ctx, orgID)
assert.NoError(t, err)
assert.Len(t, funnels, 2)
assert.Equal(t, expectedFunnels, funnels)
mockStore.AssertExpectations(t)
}
func TestModule_Delete(t *testing.T) {
mockStore := new(MockStore)
module := impltracefunnel.NewModule(mockStore)
ctx := context.Background()
funnelID := valuer.GenerateUUID()
orgID := valuer.GenerateUUID()
mockStore.On("Delete", ctx, funnelID, orgID).Return(nil)
err := module.Delete(ctx, funnelID, orgID)
assert.NoError(t, err)
mockStore.AssertExpectations(t)
}
func TestModule_GetFunnelMetadata(t *testing.T) {
mockStore := new(MockStore)
module := impltracefunnel.NewModule(mockStore)
ctx := context.Background()
funnelID := valuer.GenerateUUID()
orgID := valuer.GenerateUUID()
now := time.Now()
expectedFunnel := &traceFunnels.StorableFunnel{
Description: "test description",
TimeAuditable: types.TimeAuditable{
CreatedAt: now,
UpdatedAt: now,
},
}
mockStore.On("Get", ctx, funnelID, orgID).Return(expectedFunnel, nil)
createdAt, updatedAt, description, err := module.GetFunnelMetadata(ctx, funnelID, orgID)
assert.NoError(t, err)
assert.Equal(t, now.UnixNano()/1000000, createdAt)
assert.Equal(t, now.UnixNano()/1000000, updatedAt)
assert.Equal(t, "test description", description)
mockStore.AssertExpectations(t)
}

View File

@ -11,8 +11,10 @@ import (
"github.com/SigNoz/signoz/pkg/emailing" "github.com/SigNoz/signoz/pkg/emailing"
"github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/factory" "github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/modules/user" "github.com/SigNoz/signoz/pkg/modules/user"
"github.com/SigNoz/signoz/pkg/query-service/constants" "github.com/SigNoz/signoz/pkg/query-service/constants"
"github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/query-service/telemetry" "github.com/SigNoz/signoz/pkg/query-service/telemetry"
"github.com/SigNoz/signoz/pkg/types" "github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/authtypes" "github.com/SigNoz/signoz/pkg/types/authtypes"
@ -22,20 +24,22 @@ import (
) )
type Module struct { type Module struct {
store types.UserStore store types.UserStore
jwt *authtypes.JWT jwt *authtypes.JWT
emailing emailing.Emailing emailing emailing.Emailing
settings factory.ScopedProviderSettings settings factory.ScopedProviderSettings
orgSetter organization.Setter
} }
// This module is a WIP, don't take inspiration from this. // This module is a WIP, don't take inspiration from this.
func NewModule(store types.UserStore, jwt *authtypes.JWT, emailing emailing.Emailing, providerSettings factory.ProviderSettings) user.Module { func NewModule(store types.UserStore, jwt *authtypes.JWT, emailing emailing.Emailing, providerSettings factory.ProviderSettings, orgSetter organization.Setter) user.Module {
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/modules/user/impluser") settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/modules/user/impluser")
return &Module{ return &Module{
store: store, store: store,
jwt: jwt, jwt: jwt,
emailing: emailing, emailing: emailing,
settings: settings, settings: settings,
orgSetter: orgSetter,
} }
} }
@ -538,3 +542,36 @@ func (m *Module) ListDomains(ctx context.Context, orgID valuer.UUID) ([]*types.G
func (m *Module) UpdateDomain(ctx context.Context, domain *types.GettableOrgDomain) error { func (m *Module) UpdateDomain(ctx context.Context, domain *types.GettableOrgDomain) error {
return m.store.UpdateDomain(ctx, domain) return m.store.UpdateDomain(ctx, domain)
} }
func (m *Module) Register(ctx context.Context, req *types.PostableRegisterOrgAndAdmin) (*types.User, error) {
if req.Email == "" {
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "email is required")
}
if req.Password == "" {
return nil, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "password is required")
}
organization := types.NewOrganization(req.OrgDisplayName)
err := m.orgSetter.Create(ctx, organization)
if err != nil {
return nil, model.InternalError(err)
}
user, err := types.NewUser(req.Name, req.Email, types.RoleAdmin.String(), organization.ID.StringValue())
if err != nil {
return nil, model.InternalError(err)
}
password, err := types.NewFactorPassword(req.Password)
if err != nil {
return nil, model.InternalError(err)
}
user, err = m.CreateUserWithPassword(ctx, user, password)
if err != nil {
return nil, model.InternalError(err)
}
return user, nil
}

View File

@ -62,6 +62,9 @@ type Module interface {
ListAPIKeys(ctx context.Context, orgID valuer.UUID) ([]*types.StorableAPIKeyUser, error) ListAPIKeys(ctx context.Context, orgID valuer.UUID) ([]*types.StorableAPIKeyUser, error)
RevokeAPIKey(ctx context.Context, id, removedByUserID valuer.UUID) error RevokeAPIKey(ctx context.Context, id, removedByUserID valuer.UUID) error
GetAPIKey(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*types.StorableAPIKeyUser, error) GetAPIKey(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*types.StorableAPIKeyUser, error)
// Register
Register(ctx context.Context, req *types.PostableRegisterOrgAndAdmin) (*types.User, error)
} }
type Handler interface { type Handler interface {

View File

@ -6267,9 +6267,6 @@ func (r *ClickHouseReader) GetUpdatedMetricsMetadata(ctx context.Context, orgID
if err == nil { if err == nil {
cachedMetadata[metricName] = metadata cachedMetadata[metricName] = metadata
} else { } else {
if err != nil {
zap.L().Error("Error retrieving metrics metadata from cache", zap.String("metric_name", metricName), zap.Error(err))
}
missingMetrics = append(missingMetrics, metricName) missingMetrics = append(missingMetrics, metricName)
} }
} }

View File

@ -3,17 +3,23 @@ package cloudintegrations
import ( import (
"context" "context"
"testing" "testing"
"time"
"github.com/SigNoz/signoz/pkg/emailing" "github.com/SigNoz/signoz/pkg/alertmanager"
"github.com/SigNoz/signoz/pkg/emailing/noopemailing" "github.com/SigNoz/signoz/pkg/alertmanager/alertmanagerserver"
"github.com/SigNoz/signoz/pkg/alertmanager/signozalertmanager"
"github.com/SigNoz/signoz/pkg/emailing/emailingtest"
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest" "github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
"github.com/SigNoz/signoz/pkg/modules/organization" "github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization" "github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
"github.com/SigNoz/signoz/pkg/modules/user" "github.com/SigNoz/signoz/pkg/modules/user"
"github.com/SigNoz/signoz/pkg/modules/user/impluser"
"github.com/SigNoz/signoz/pkg/query-service/model" "github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/query-service/utils" "github.com/SigNoz/signoz/pkg/query-service/utils"
"github.com/SigNoz/signoz/pkg/sharder"
"github.com/SigNoz/signoz/pkg/sharder/noopsharder"
"github.com/SigNoz/signoz/pkg/signoz"
"github.com/SigNoz/signoz/pkg/types" "github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -24,11 +30,16 @@ func TestRegenerateConnectionUrlWithUpdatedConfig(t *testing.T) {
controller, err := NewController(sqlStore) controller, err := NewController(sqlStore)
require.NoError(err) require.NoError(err)
organizationModule := implorganization.NewModule(implorganization.NewStore(sqlStore))
providerSettings := instrumentationtest.New().ToProviderSettings() providerSettings := instrumentationtest.New().ToProviderSettings()
emailing, _ := noopemailing.New(context.Background(), providerSettings, emailing.Config{}) sharder, err := noopsharder.New(context.TODO(), providerSettings, sharder.Config{})
userModule := impluser.NewModule(impluser.NewStore(sqlStore, providerSettings), nil, emailing, providerSettings) require.NoError(err)
user, apiErr := createTestUser(organizationModule, userModule) orgGetter := implorganization.NewGetter(implorganization.NewStore(sqlStore), sharder)
alertmanager, err := signozalertmanager.New(context.TODO(), providerSettings, alertmanager.Config{Provider: "signoz", Signoz: alertmanager.Signoz{PollInterval: 10 * time.Second, Config: alertmanagerserver.NewConfig()}}, sqlStore, orgGetter)
require.NoError(err)
jwt := authtypes.NewJWT("", 1*time.Hour, 1*time.Hour)
emailing := emailingtest.New()
modules := signoz.NewModules(sqlStore, jwt, emailing, providerSettings, orgGetter, alertmanager)
user, apiErr := createTestUser(modules.OrgSetter, modules.User)
require.Nil(apiErr) require.Nil(apiErr)
// should be able to generate connection url for // should be able to generate connection url for
@ -74,11 +85,17 @@ func TestAgentCheckIns(t *testing.T) {
sqlStore := utils.NewQueryServiceDBForTests(t) sqlStore := utils.NewQueryServiceDBForTests(t)
controller, err := NewController(sqlStore) controller, err := NewController(sqlStore)
require.NoError(err) require.NoError(err)
organizationModule := implorganization.NewModule(implorganization.NewStore(sqlStore))
providerSettings := instrumentationtest.New().ToProviderSettings() providerSettings := instrumentationtest.New().ToProviderSettings()
emailing, _ := noopemailing.New(context.Background(), providerSettings, emailing.Config{}) sharder, err := noopsharder.New(context.TODO(), providerSettings, sharder.Config{})
userModule := impluser.NewModule(impluser.NewStore(sqlStore, providerSettings), nil, emailing, providerSettings) require.NoError(err)
user, apiErr := createTestUser(organizationModule, userModule) orgGetter := implorganization.NewGetter(implorganization.NewStore(sqlStore), sharder)
alertmanager, err := signozalertmanager.New(context.TODO(), providerSettings, alertmanager.Config{Provider: "signoz", Signoz: alertmanager.Signoz{PollInterval: 10 * time.Second, Config: alertmanagerserver.NewConfig()}}, sqlStore, orgGetter)
require.NoError(err)
jwt := authtypes.NewJWT("", 1*time.Hour, 1*time.Hour)
emailing := emailingtest.New()
modules := signoz.NewModules(sqlStore, jwt, emailing, providerSettings, orgGetter, alertmanager)
user, apiErr := createTestUser(modules.OrgSetter, modules.User)
require.Nil(apiErr) require.Nil(apiErr)
// An agent should be able to check in from a cloud account even // An agent should be able to check in from a cloud account even
@ -164,11 +181,16 @@ func TestCantDisconnectNonExistentAccount(t *testing.T) {
controller, err := NewController(sqlStore) controller, err := NewController(sqlStore)
require.NoError(err) require.NoError(err)
organizationModule := implorganization.NewModule(implorganization.NewStore(sqlStore))
providerSettings := instrumentationtest.New().ToProviderSettings() providerSettings := instrumentationtest.New().ToProviderSettings()
emailing, _ := noopemailing.New(context.Background(), providerSettings, emailing.Config{}) sharder, err := noopsharder.New(context.TODO(), providerSettings, sharder.Config{})
userModule := impluser.NewModule(impluser.NewStore(sqlStore, providerSettings), nil, emailing, providerSettings) require.NoError(err)
user, apiErr := createTestUser(organizationModule, userModule) orgGetter := implorganization.NewGetter(implorganization.NewStore(sqlStore), sharder)
alertmanager, err := signozalertmanager.New(context.TODO(), providerSettings, alertmanager.Config{Provider: "signoz", Signoz: alertmanager.Signoz{PollInterval: 10 * time.Second, Config: alertmanagerserver.NewConfig()}}, sqlStore, orgGetter)
require.NoError(err)
jwt := authtypes.NewJWT("", 1*time.Hour, 1*time.Hour)
emailing := emailingtest.New()
modules := signoz.NewModules(sqlStore, jwt, emailing, providerSettings, orgGetter, alertmanager)
user, apiErr := createTestUser(modules.OrgSetter, modules.User)
require.Nil(apiErr) require.Nil(apiErr)
// Attempting to disconnect a non-existent account should return error // Attempting to disconnect a non-existent account should return error
@ -186,11 +208,16 @@ func TestConfigureService(t *testing.T) {
controller, err := NewController(sqlStore) controller, err := NewController(sqlStore)
require.NoError(err) require.NoError(err)
organizationModule := implorganization.NewModule(implorganization.NewStore(sqlStore))
providerSettings := instrumentationtest.New().ToProviderSettings() providerSettings := instrumentationtest.New().ToProviderSettings()
emailing, _ := noopemailing.New(context.Background(), providerSettings, emailing.Config{}) sharder, err := noopsharder.New(context.TODO(), providerSettings, sharder.Config{})
userModule := impluser.NewModule(impluser.NewStore(sqlStore, providerSettings), nil, emailing, providerSettings) require.NoError(err)
user, apiErr := createTestUser(organizationModule, userModule) orgGetter := implorganization.NewGetter(implorganization.NewStore(sqlStore), sharder)
alertmanager, err := signozalertmanager.New(context.TODO(), providerSettings, alertmanager.Config{Provider: "signoz", Signoz: alertmanager.Signoz{PollInterval: 10 * time.Second, Config: alertmanagerserver.NewConfig()}}, sqlStore, orgGetter)
require.NoError(err)
jwt := authtypes.NewJWT("", 1*time.Hour, 1*time.Hour)
emailing := emailingtest.New()
modules := signoz.NewModules(sqlStore, jwt, emailing, providerSettings, orgGetter, alertmanager)
user, apiErr := createTestUser(modules.OrgSetter, modules.User)
require.Nil(apiErr) require.Nil(apiErr)
// create a connected account // create a connected account
@ -305,7 +332,7 @@ func makeTestConnectedAccount(t *testing.T, orgId string, controller *Controller
return acc return acc
} }
func createTestUser(organizationModule organization.Module, userModule user.Module) (*types.User, *model.ApiError) { func createTestUser(organizationModule organization.Setter, userModule user.Module) (*types.User, *model.ApiError) {
// Create a test user for auth // Create a test user for auth
ctx := context.Background() ctx := context.Background()
organization := types.NewOrganization("test") organization := types.NewOrganization("test")

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,851 @@
{
"description": "View key AWS ECS metrics with an out of the box dashboard.\n",
"image":"data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%3Csvg%20width%3D%2280px%22%20height%3D%2280px%22%20viewBox%3D%220%200%2080%2080%22%20version%3D%221.1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%3E%3C!--%20Generator%3A%20Sketch%2064%20(93537)%20-%20https%3A%2F%2Fsketch.com%20--%3E%3Ctitle%3EIcon-Architecture%2F64%2FArch_Amazon-Elastic-Container-Service_64%3C%2Ftitle%3E%3Cdesc%3ECreated%20with%20Sketch.%3C%2Fdesc%3E%3Cdefs%3E%3ClinearGradient%20x1%3D%220%25%22%20y1%3D%22100%25%22%20x2%3D%22100%25%22%20y2%3D%220%25%22%20id%3D%22linearGradient-1%22%3E%3Cstop%20stop-color%3D%22%23C8511B%22%20offset%3D%220%25%22%3E%3C%2Fstop%3E%3Cstop%20stop-color%3D%22%23FF9900%22%20offset%3D%22100%25%22%3E%3C%2Fstop%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Cg%20id%3D%22Icon-Architecture%2F64%2FArch_Amazon-Elastic-Container-Service_64%22%20stroke%3D%22none%22%20stroke-width%3D%221%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Cg%20id%3D%22Icon-Architecture-BG%2F64%2FContainers%22%20fill%3D%22url(%23linearGradient-1)%22%3E%3Crect%20id%3D%22Rectangle%22%20x%3D%220%22%20y%3D%220%22%20width%3D%2280%22%20height%3D%2280%22%3E%3C%2Frect%3E%3C%2Fg%3E%3Cpath%20d%3D%22M64%2C48.2340095%20L56%2C43.4330117%20L56%2C32.0000169%20C56%2C31.6440171%2055.812%2C31.3150172%2055.504%2C31.1360173%20L44%2C24.4260204%20L44%2C14.7520248%20L64%2C26.5710194%20L64%2C48.2340095%20Z%20M65.509%2C25.13902%20L43.509%2C12.139026%20C43.199%2C11.9560261%2042.818%2C11.9540261%2042.504%2C12.131026%20C42.193%2C12.3090259%2042%2C12.6410257%2042%2C13.0000256%20L42%2C25.0000201%20C42%2C25.3550199%2042.189%2C25.6840198%2042.496%2C25.8640197%20L54%2C32.5740166%20L54%2C44.0000114%20C54%2C44.3510113%2054.185%2C44.6770111%2054.486%2C44.857011%20L64.486%2C50.8570083%20C64.644%2C50.9520082%2064.822%2C51%2065%2C51%20C65.17%2C51%2065.34%2C50.9570082%2065.493%2C50.8700083%20C65.807%2C50.6930084%2066%2C50.3600085%2066%2C50%20L66%2C26.0000196%20C66%2C25.6460198%2065.814%2C25.31902%2065.509%2C25.13902%20L65.509%2C25.13902%20Z%20M40.445%2C66.863001%20L17%2C54.3990067%20L17%2C26.5710194%20L37%2C14.7520248%20L37%2C24.4510204%20L26.463%2C31.1560173%20C26.175%2C31.3400172%2026%2C31.6580171%2026%2C32.0000169%20L26%2C49.0000091%20C26%2C49.373009%2026.208%2C49.7150088%2026.538%2C49.8870087%20L39.991%2C56.8870055%20C40.28%2C57.0370055%2040.624%2C57.0380055%2040.912%2C56.8880055%20L53.964%2C50.1440086%20L61.996%2C54.9640064%20L40.445%2C66.863001%20Z%20M64.515%2C54.1420068%20L54.515%2C48.1420095%20C54.217%2C47.9640096%2053.849%2C47.9520096%2053.541%2C48.1120095%20L40.455%2C54.8730065%20L28%2C48.3930094%20L28%2C32.5490167%20L38.537%2C25.8440197%20C38.825%2C25.6600198%2039%2C25.3420199%2039%2C25.0000201%20L39%2C13.0000256%20C39%2C12.6410257%2038.808%2C12.3090259%2038.496%2C12.131026%20C38.184%2C11.9540261%2037.802%2C11.9560261%2037.491%2C12.139026%20L15.491%2C25.13902%20C15.187%2C25.31902%2015%2C25.6460198%2015%2C26.0000196%20L15%2C55%20C15%2C55.3690062%2015.204%2C55.7090061%2015.53%2C55.883006%20L39.984%2C68.8830001%20C40.131%2C68.961%2040.292%2C69%2040.453%2C69%20C40.62%2C69%2040.786%2C68.958%2040.937%2C68.8750001%20L64.484%2C55.875006%20C64.797%2C55.7020061%2064.993%2C55.3750062%2065.0001416%2C55.0180064%20C65.006%2C54.6600066%2064.821%2C54.3260067%2064.515%2C54.1420068%20L64.515%2C54.1420068%20Z%22%20id%3D%22Amazon-Elastic-Container-Service_Icon_64_Squid%22%20fill%3D%22%23FFFFFF%22%3E%3C%2Fpath%3E%3C%2Fg%3E%3C%2Fsvg%3E",
"layout": [
{
"h": 6,
"i": "f78becf8-0328-48b4-84b6-ff4dac325940",
"moved": false,
"static": false,
"w": 6,
"x": 0,
"y": 0
},
{
"h": 6,
"i": "2b4eac06-b426-4f78-b874-2e1734c4104b",
"moved": false,
"static": false,
"w": 6,
"x": 6,
"y": 0
},
{
"h": 6,
"i": "5bea2bc0-13a2-4937-bccb-60ffe8a43ad5",
"moved": false,
"static": false,
"w": 6,
"x": 0,
"y": 6
},
{
"h": 6,
"i": "6fac67b0-50ec-4b43-ac4b-320a303d0369",
"moved": false,
"static": false,
"w": 6,
"x": 6,
"y": 6
}
],
"panelMap": {},
"tags": [],
"title": "AWS ECS Overview",
"uploadedGrafana": false,
"variables": {
"51f4fa2b-89c7-47c2-9795-f32cffaab985": {
"allSelected": false,
"customValue": "",
"description": "AWS Account ID",
"id": "51f4fa2b-89c7-47c2-9795-f32cffaab985",
"key": "51f4fa2b-89c7-47c2-9795-f32cffaab985",
"modificationUUID": "7b814d17-8fff-4ed6-a4ea-90e3b1a97584",
"multiSelect": false,
"name": "Account",
"order": 0,
"queryValue": "SELECT DISTINCT JSONExtractString(labels, 'cloud.account.id') AS `cloud.account.id`\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'aws_ECS_MemoryUtilization_max' GROUP BY `cloud.account.id`",
"showALLOption": false,
"sort": "DISABLED",
"textboxValue": "",
"type": "QUERY"
},
"9faf0f4b-b245-4b3c-83a3-60cfa76dfeb0": {
"allSelected": false,
"customValue": "",
"description": "Account Region",
"id": "9faf0f4b-b245-4b3c-83a3-60cfa76dfeb0",
"key": "9faf0f4b-b245-4b3c-83a3-60cfa76dfeb0",
"modificationUUID": "3b5f499b-22a3-4c8a-847c-8d3811c9e6b2",
"multiSelect": false,
"name": "Region",
"order": 1,
"queryValue": "SELECT DISTINCT JSONExtractString(labels, 'cloud.region') AS region\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'aws_ECS_MemoryUtilization_max' AND JSONExtractString(labels, 'cloud.account.id') IN {{.Account}} GROUP BY region",
"showALLOption": false,
"sort": "ASC",
"textboxValue": "",
"type": "QUERY"
},
"bfbdbcbe-a168-4d81-b108-36339e249116": {
"allSelected": true,
"customValue": "",
"description": "ECS Cluster Name",
"id": "bfbdbcbe-a168-4d81-b108-36339e249116",
"key": "bfbdbcbe-a168-4d81-b108-36339e249116",
"modificationUUID": "9fb0d63c-ac6c-497d-82b3-17d95944e245",
"multiSelect": true,
"name": "Cluster",
"order": 2,
"queryValue": "SELECT DISTINCT JSONExtractString(labels, 'ClusterName') AS cluster\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'aws_ECS_MemoryUtilization_max' AND JSONExtractString(labels, 'cloud.account.id') IN {{.Account}} AND JSONExtractString(labels, 'cloud.region') IN {{.Region}}\nGROUP BY cluster",
"showALLOption": true,
"sort": "ASC",
"textboxValue": "",
"type": "QUERY"
}
},
"version": "v4",
"widgets": [
{
"bucketCount": 30,
"bucketWidth": 0,
"columnUnits": {},
"description": "",
"fillSpans": false,
"id": "f78becf8-0328-48b4-84b6-ff4dac325940",
"isLogScale": false,
"isStacked": false,
"mergeAllActiveQueries": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "aws_ECS_MemoryUtilization_max--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "aws_ECS_MemoryUtilization_max",
"type": "Gauge"
},
"aggregateOperator": "max",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "26ac617d",
"key": {
"dataType": "string",
"id": "cloud.region--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "cloud.region",
"type": "tag"
},
"op": "=",
"value": "$Region"
},
{
"id": "57172ed9",
"key": {
"dataType": "string",
"id": "cloud.account.id--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "cloud.account.id",
"type": "tag"
},
"op": "=",
"value": "$Account"
},
{
"id": "49b9f85e",
"key": {
"dataType": "string",
"id": "ClusterName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "ClusterName",
"type": "tag"
},
"op": "in",
"value": [
"$Cluster"
]
}
],
"op": "AND"
},
"functions": [],
"groupBy": [
{
"dataType": "string",
"id": "ServiceName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "ServiceName",
"type": "tag"
},
{
"dataType": "string",
"id": "ClusterName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "ClusterName",
"type": "tag"
}
],
"having": [],
"legend": "{{ServiceName}} ({{ClusterName}})",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "avg",
"spaceAggregation": "max",
"stepInterval": 60,
"timeAggregation": "max"
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "56068fdd-d523-4117-92fa-87c6518ad07c",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"selectedLogFields": [
{
"dataType": "string",
"name": "body",
"type": ""
},
{
"dataType": "string",
"name": "timestamp",
"type": ""
}
],
"selectedTracesFields": [
{
"dataType": "string",
"id": "serviceName--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "serviceName",
"type": "tag"
},
{
"dataType": "string",
"id": "name--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "name",
"type": "tag"
},
{
"dataType": "float64",
"id": "durationNano--float64--tag--true",
"isColumn": true,
"isJSON": false,
"key": "durationNano",
"type": "tag"
},
{
"dataType": "string",
"id": "httpMethod--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "httpMethod",
"type": "tag"
},
{
"dataType": "string",
"id": "responseStatusCode--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "responseStatusCode",
"type": "tag"
}
],
"softMax": 0,
"softMin": 0,
"stackedBarChart": false,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Maximum Memory Utilization",
"yAxisUnit": "none"
},
{
"bucketCount": 30,
"bucketWidth": 0,
"columnUnits": {},
"description": "",
"fillSpans": false,
"id": "2b4eac06-b426-4f78-b874-2e1734c4104b",
"isLogScale": false,
"isStacked": false,
"mergeAllActiveQueries": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "aws_ECS_MemoryUtilization_min--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "aws_ECS_MemoryUtilization_min",
"type": "Gauge"
},
"aggregateOperator": "min",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "cd4b8848",
"key": {
"dataType": "string",
"id": "cloud.region--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "cloud.region",
"type": "tag"
},
"op": "=",
"value": "$Region"
},
{
"id": "aa5115c6",
"key": {
"dataType": "string",
"id": "cloud.account.id--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "cloud.account.id",
"type": "tag"
},
"op": "=",
"value": "$Account"
},
{
"id": "f60677b6",
"key": {
"dataType": "string",
"id": "ClusterName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "ClusterName",
"type": "tag"
},
"op": "in",
"value": [
"$Cluster"
]
}
],
"op": "AND"
},
"functions": [],
"groupBy": [
{
"dataType": "string",
"id": "ServiceName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "ServiceName",
"type": "tag"
},
{
"dataType": "string",
"id": "ClusterName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "ClusterName",
"type": "tag"
}
],
"having": [],
"legend": "{{ServiceName}} ({{ClusterName}})",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "avg",
"spaceAggregation": "min",
"stepInterval": 60,
"timeAggregation": "min"
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "fb19342e-cbde-40d8-b12f-ad108698356b",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"selectedLogFields": [
{
"dataType": "string",
"name": "body",
"type": ""
},
{
"dataType": "string",
"name": "timestamp",
"type": ""
}
],
"selectedTracesFields": [
{
"dataType": "string",
"id": "serviceName--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "serviceName",
"type": "tag"
},
{
"dataType": "string",
"id": "name--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "name",
"type": "tag"
},
{
"dataType": "float64",
"id": "durationNano--float64--tag--true",
"isColumn": true,
"isJSON": false,
"key": "durationNano",
"type": "tag"
},
{
"dataType": "string",
"id": "httpMethod--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "httpMethod",
"type": "tag"
},
{
"dataType": "string",
"id": "responseStatusCode--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "responseStatusCode",
"type": "tag"
}
],
"softMax": 0,
"softMin": 0,
"stackedBarChart": false,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Minimum Memory Utilization",
"yAxisUnit": "none"
},
{
"bucketCount": 30,
"bucketWidth": 0,
"columnUnits": {},
"description": "",
"fillSpans": false,
"id": "5bea2bc0-13a2-4937-bccb-60ffe8a43ad5",
"isLogScale": false,
"isStacked": false,
"mergeAllActiveQueries": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "aws_ECS_CPUUtilization_max--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "aws_ECS_CPUUtilization_max",
"type": "Gauge"
},
"aggregateOperator": "max",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "2c13c8ee",
"key": {
"dataType": "string",
"id": "cloud.region--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "cloud.region",
"type": "tag"
},
"op": "=",
"value": "$Region"
},
{
"id": "f489f6a8",
"key": {
"dataType": "string",
"id": "cloud.account.id--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "cloud.account.id",
"type": "tag"
},
"op": "=",
"value": "$Account"
},
{
"id": "94012320",
"key": {
"dataType": "string",
"id": "ClusterName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "ClusterName",
"type": "tag"
},
"op": "in",
"value": [
"$Cluster"
]
}
],
"op": "AND"
},
"functions": [],
"groupBy": [
{
"dataType": "string",
"id": "ServiceName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "ServiceName",
"type": "tag"
},
{
"dataType": "string",
"id": "ClusterName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "ClusterName",
"type": "tag"
}
],
"having": [],
"legend": "{{ServiceName}} ({{ClusterName}})",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "avg",
"spaceAggregation": "max",
"stepInterval": 60,
"timeAggregation": "max"
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "273e0a76-c780-4b9a-9b03-2649d4227173",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"selectedLogFields": [
{
"dataType": "string",
"name": "body",
"type": ""
},
{
"dataType": "string",
"name": "timestamp",
"type": ""
}
],
"selectedTracesFields": [
{
"dataType": "string",
"id": "serviceName--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "serviceName",
"type": "tag"
},
{
"dataType": "string",
"id": "name--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "name",
"type": "tag"
},
{
"dataType": "float64",
"id": "durationNano--float64--tag--true",
"isColumn": true,
"isJSON": false,
"key": "durationNano",
"type": "tag"
},
{
"dataType": "string",
"id": "httpMethod--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "httpMethod",
"type": "tag"
},
{
"dataType": "string",
"id": "responseStatusCode--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "responseStatusCode",
"type": "tag"
}
],
"softMax": 0,
"softMin": 0,
"stackedBarChart": false,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Maximum CPU Utilization",
"yAxisUnit": "none"
},
{
"bucketCount": 30,
"bucketWidth": 0,
"columnUnits": {},
"description": "",
"fillSpans": false,
"id": "6fac67b0-50ec-4b43-ac4b-320a303d0369",
"isLogScale": false,
"isStacked": false,
"mergeAllActiveQueries": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "aws_ECS_CPUUtilization_min--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "aws_ECS_CPUUtilization_min",
"type": "Gauge"
},
"aggregateOperator": "min",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "758ba906",
"key": {
"dataType": "string",
"id": "cloud.region--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "cloud.region",
"type": "tag"
},
"op": "=",
"value": "$Region"
},
{
"id": "4ffe6bf7",
"key": {
"dataType": "string",
"id": "cloud.account.id--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "cloud.account.id",
"type": "tag"
},
"op": "=",
"value": "$Account"
},
{
"id": "53d98059",
"key": {
"dataType": "string",
"id": "ClusterName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "ClusterName",
"type": "tag"
},
"op": "in",
"value": [
"$Cluster"
]
}
],
"op": "AND"
},
"functions": [],
"groupBy": [
{
"dataType": "string",
"id": "ServiceName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "ServiceName",
"type": "tag"
},
{
"dataType": "string",
"id": "ClusterName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "ClusterName",
"type": "tag"
}
],
"having": [],
"legend": "{{ServiceName}} ({{ClusterName}})",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "avg",
"spaceAggregation": "min",
"stepInterval": 60,
"timeAggregation": "min"
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "c89482b3-5a98-4e2c-be0d-ef036d7dac05",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"selectedLogFields": [
{
"dataType": "string",
"name": "body",
"type": ""
},
{
"dataType": "string",
"name": "timestamp",
"type": ""
}
],
"selectedTracesFields": [
{
"dataType": "string",
"id": "serviceName--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "serviceName",
"type": "tag"
},
{
"dataType": "string",
"id": "name--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "name",
"type": "tag"
},
{
"dataType": "float64",
"id": "durationNano--float64--tag--true",
"isColumn": true,
"isJSON": false,
"key": "durationNano",
"type": "tag"
},
{
"dataType": "string",
"id": "httpMethod--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "httpMethod",
"type": "tag"
},
{
"dataType": "string",
"id": "responseStatusCode--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "responseStatusCode",
"type": "tag"
}
],
"softMax": 0,
"softMin": 0,
"stackedBarChart": false,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Minimum CPU Utilization",
"yAxisUnit": "none"
}
]
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,818 @@
{
"description": "View key AWS SNS metrics with an out of the box dashboard.",
"image": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iODBweCIgaGVpZ2h0PSI4MHB4IiB2aWV3Qm94PSIwIDAgODAgODAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDY0ICg5MzUzNykgLSBodHRwczovL3NrZXRjaC5jb20gLS0+CiAgICA8dGl0bGU+SWNvbi1BcmNoaXRlY3R1cmUvNjQvQXJjaF9BV1MtU2ltcGxlLU5vdGlmaWNhdGlvbi1TZXJ2aWNlXzY0PC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGRlZnM+CiAgICAgICAgPGxpbmVhckdyYWRpZW50IHgxPSIwJSIgeTE9IjEwMCUiIHgyPSIxMDAlIiB5Mj0iMCUiIGlkPSJsaW5lYXJHcmFkaWVudC0xIj4KICAgICAgICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI0IwMDg0RCIgb2Zmc2V0PSIwJSI+PC9zdG9wPgogICAgICAgICAgICA8c3RvcCBzdG9wLWNvbG9yPSIjRkY0RjhCIiBvZmZzZXQ9IjEwMCUiPjwvc3RvcD4KICAgICAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPC9kZWZzPgogICAgPGcgaWQ9Ikljb24tQXJjaGl0ZWN0dXJlLzY0L0FyY2hfQVdTLVNpbXBsZS1Ob3RpZmljYXRpb24tU2VydmljZV82NCIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9Ikljb24tQXJjaGl0ZWN0dXJlLUJHLzY0L0FwcGxpY2F0aW9uLUludGVncmF0aW9uIiBmaWxsPSJ1cmwoI2xpbmVhckdyYWRpZW50LTEpIj4KICAgICAgICAgICAgPHJlY3QgaWQ9IlJlY3RhbmdsZSIgeD0iMCIgeT0iMCIgd2lkdGg9IjgwIiBoZWlnaHQ9IjgwIj48L3JlY3Q+CiAgICAgICAgPC9nPgogICAgICAgIDxwYXRoIGQ9Ik0xNywzOCBDMTguMTAzLDM4IDE5LDM4Ljg5NyAxOSw0MCBDMTksNDEuMTAzIDE4LjEwMyw0MiAxNyw0MiBDMTUuODk3LDQyIDE1LDQxLjEwMyAxNSw0MCBDMTUsMzguODk3IDE1Ljg5NywzOCAxNywzOCBMMTcsMzggWiBNNDEsNjQgQzI5LjMxNCw2NCAxOS4yODksNTUuNDY2IDE3LjE5NCw0My45OCBDMTguOTY1LDQzLjg5NCAyMC40MjcsNDIuNjU5IDIwLjg1Nyw0MSBMMjcsNDEgTDI3LDM5IEwyMC44NTcsMzkgQzIwLjQyNywzNy4zNDIgMTguOTY2LDM2LjEwNyAxNy4xOTUsMzYuMDIgQzE5LjI4NSwyNC43MSAyOS41MTEsMTYgNDEsMTYgQzQ1LjMxMywxNiA0OS44MzIsMTcuNjIyIDU0LjQyOSwyMC44MjEgTDU1LjU3MSwxOS4xNzkgQzUwLjYzMywxNS43NDMgNDUuNzMsMTQgNDEsMTQgQzI4LjI3LDE0IDE2Ljk0OSwyMy44NjUgMTUuMDYzLDM2LjUyMSBDMTMuODM5LDM3LjIwNyAxMywzOC41IDEzLDQwIEMxMyw0MS41IDEzLjgzOSw0Mi43OTMgMTUuMDYzLDQzLjQ3OCBDMTYuOTcsNTYuMzQxIDI4LjA1Niw2NiA0MSw2NiBDNDYuNDA3LDY2IDUxLjk0Miw2NC4xNTcgNTYuNTg1LDYwLjgxMSBMNTUuNDE1LDU5LjE4OSBDNTEuMTEsNjIuMjkyIDQ1Ljk5MSw2NCA0MSw2NCBMNDEsNjQgWiBNMzAuMTAxLDM2LjQ0MiBDMzEuOTU1LDM2Ljg5NSAzNC4yNzUsMzcgMzYsMzcgQzM3LjY0MiwzNyAzOS44MjMsMzYuOTA1IDQxLjYyOSwzNi41MDYgTDM3LjEwNSw0NS41NTMgQzM3LjAzNiw0NS42OTEgMzcsNDUuODQ1IDM3LDQ2IEwzNyw1MC40NTMgQzM2LjE5OSw1MC45NjQgMzQuODMzLDUxLjgxMiAzNCw1MS45ODYgTDM0LDQ2IEMzNCw0NS44NjggMzMuOTc0LDQ1LjczNyAzMy45MjMsNDUuNjE1IEwzMC4xMDEsMzYuNDQyIFogTTM2LDMzIEM0MC4wMjUsMzMgNDIuMTc0LDMzLjYwNCA0Mi44NDEsMzQgQzQyLjE3NCwzNC4zOTYgNDAuMDI1LDM1IDM2LDM1IEMzMS45NzUsMzUgMjkuODI2LDM0LjM5NiAyOS4xNTksMzQgQzI5LjgyNiwzMy42MDQgMzEuOTc1LDMzIDM2LDMzIEwzNiwzMyBaIE0zMyw1NCBMMzQsNTQgQzM0LjA0Myw1NCAzNC4wODYsNTMuOTk3IDM0LjEyOCw1My45OTIgQzM1LjM1Miw1My44MzMgMzYuOTA5LDUyLjg4NyAzOC4yNzIsNTIuMDEzIEwzOC41MzUsNTEuODQ1IEMzOC44MjQsNTEuNjYxIDM5LDUxLjM0MiAzOSw1MSBMMzksNDYuMjM2IEw0NC41NTksMzUuMTIgQzQ0LjgzMywzNC44MDEgNDUsMzQuNDM0IDQ1LDM0IEM0NSwzMS4zOSAzOS4zNjEsMzEgMzYsMzEgQzMyLjYzOSwzMSAyNywzMS4zOSAyNywzNCBDMjcsMzQuMzY2IDI3LjEyLDM0LjY4NCAyNy4zMiwzNC45NjcgTDMyLDQ2LjIgTDMyLDUzIEMzMiw1My41NTIgMzIuNDQ3LDU0IDMzLDU0IEwzMyw1NCBaIE02Miw1MyBDNjMuMTAzLDUzIDY0LDUzLjg5NyA2NCw1NSBDNjQsNTYuMTAzIDYzLjEwMyw1NyA2Miw1NyBDNjAuODk3LDU3IDYwLDU2LjEwMyA2MCw1NSBDNjAsNTMuODk3IDYwLjg5Nyw1MyA2Miw1MyBMNjIsNTMgWiBNNjIsMjMgQzYzLjEwMywyMyA2NCwyMy44OTcgNjQsMjUgQzY0LDI2LjEwMyA2My4xMDMsMjcgNjIsMjcgQzYwLjg5NywyNyA2MCwyNi4xMDMgNjAsMjUgQzYwLDIzLjg5NyA2MC44OTcsMjMgNjIsMjMgTDYyLDIzIFogTTY0LDM4IEM2NS4xMDMsMzggNjYsMzguODk3IDY2LDQwIEM2Niw0MS4xMDMgNjUuMTAzLDQyIDY0LDQyIEM2Mi44OTcsNDIgNjIsNDEuMTAzIDYyLDQwIEM2MiwzOC44OTcgNjIuODk3LDM4IDY0LDM4IEw2NCwzOCBaIE01NCw0MSBMNjAuMTQzLDQxIEM2MC41ODksNDIuNzIgNjIuMTQyLDQ0IDY0LDQ0IEM2Ni4yMDYsNDQgNjgsNDIuMjA2IDY4LDQwIEM2OCwzNy43OTQgNjYuMjA2LDM2IDY0LDM2IEM2Mi4xNDIsMzYgNjAuNTg5LDM3LjI4IDYwLjE0MywzOSBMNTQsMzkgTDU0LDI2IEw1OC4xNDMsMjYgQzU4LjU4OSwyNy43MiA2MC4xNDIsMjkgNjIsMjkgQzY0LjIwNiwyOSA2NiwyNy4yMDYgNjYsMjUgQzY2LDIyLjc5NCA2NC4yMDYsMjEgNjIsMjEgQzYwLjE0MiwyMSA1OC41ODksMjIuMjggNTguMTQzLDI0IEw1MywyNCBDNTIuNDQ3LDI0IDUyLDI0LjQ0OCA1MiwyNSBMNTIsMzkgTDQ1LDM5IEw0NSw0MSBMNTIsNDEgTDUyLDU1IEM1Miw1NS41NTIgNTIuNDQ3LDU2IDUzLDU2IEw1OC4xNDMsNTYgQzU4LjU4OSw1Ny43MiA2MC4xNDIsNTkgNjIsNTkgQzY0LjIwNiw1OSA2Niw1Ny4yMDYgNjYsNTUgQzY2LDUyLjc5NCA2NC4yMDYsNTEgNjIsNTEgQzYwLjE0Miw1MSA1OC41ODksNTIuMjggNTguMTQzLDU0IEw1NCw1NCBMNTQsNDEgWiIgaWQ9IkFXUy1TaW1wbGUtTm90aWZpY2F0aW9uLVNlcnZpY2VfSWNvbl82NF9TcXVpZCIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgPC9nPgo8L3N2Zz4=",
"layout": [
{
"h": 6,
"i": "4eb87f89-0213-4773-9b06-6aecc6701898",
"moved": false,
"static": false,
"w": 6,
"x": 0,
"y": 0
},
{
"h": 6,
"i": "7a010b4e-ea7c-4a45-a9eb-93af650c45b4",
"moved": false,
"static": false,
"w": 6,
"x": 6,
"y": 0
},
{
"h": 6,
"i": "2299d4e3-6c40-4bf2-a550-c7bb8a7acd38",
"moved": false,
"static": false,
"w": 6,
"x": 0,
"y": 6
},
{
"h": 6,
"i": "16eec8b7-de1a-4039-b180-24c7a6704b6e",
"moved": false,
"static": false,
"w": 6,
"x": 6,
"y": 6
}
],
"panelMap": {},
"tags": [],
"title": "SNS Overview",
"uploadedGrafana": false,
"variables": {
"51f4fa2b-89c7-47c2-9795-f32cffaab985": {
"allSelected": false,
"customValue": "",
"description": "AWS Account ID",
"id": "51f4fa2b-89c7-47c2-9795-f32cffaab985",
"key": "51f4fa2b-89c7-47c2-9795-f32cffaab985",
"modificationUUID": "b7a6b06b-fa1f-4fb8-b70e-6bd9b350f29e",
"multiSelect": false,
"name": "Account",
"order": 0,
"queryValue": "SELECT DISTINCT JSONExtractString(labels, 'cloud.account.id') AS `cloud.account.id`\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'aws_SNS_PublishSize_count' GROUP BY `cloud.account.id`",
"showALLOption": false,
"sort": "DISABLED",
"textboxValue": "",
"type": "QUERY"
},
"9faf0f4b-b245-4b3c-83a3-60cfa76dfeb0": {
"allSelected": false,
"customValue": "",
"description": "Account Region",
"id": "9faf0f4b-b245-4b3c-83a3-60cfa76dfeb0",
"key": "9faf0f4b-b245-4b3c-83a3-60cfa76dfeb0",
"modificationUUID": "8428a5de-bfd1-4a69-9601-63e3041cd556",
"multiSelect": false,
"name": "Region",
"order": 1,
"queryValue": "SELECT DISTINCT JSONExtractString(labels, 'cloud.region') AS region\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'aws_SNS_PublishSize_count' AND JSONExtractString(labels, 'cloud.account.id') IN {{.Account}} GROUP BY region",
"showALLOption": false,
"sort": "ASC",
"textboxValue": "",
"type": "QUERY"
},
"bfbdbcbe-a168-4d81-b108-36339e249116": {
"allSelected": true,
"customValue": "",
"description": "SNS Topic Name",
"id": "bfbdbcbe-a168-4d81-b108-36339e249116",
"modificationUUID": "dfed7272-16dc-4eb6-99bf-7c82fc8e04f0",
"multiSelect": true,
"name": "Topic",
"order": 2,
"queryValue": "SELECT DISTINCT JSONExtractString(labels, 'TopicName') AS topic\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'aws_SNS_PublishSize_count' AND JSONExtractString(labels, 'cloud.account.id') IN {{.Account}} AND JSONExtractString(labels, 'cloud.region') IN {{.Region}}\nGROUP BY topic",
"showALLOption": true,
"sort": "ASC",
"textboxValue": "",
"type": "QUERY"
}
},
"version": "v4",
"widgets": [
{
"bucketCount": 30,
"bucketWidth": 0,
"columnUnits": {},
"description": "",
"fillSpans": false,
"id": "4eb87f89-0213-4773-9b06-6aecc6701898",
"isLogScale": false,
"isStacked": false,
"mergeAllActiveQueries": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "aws_SNS_NumberOfMessagesPublished_max--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "aws_SNS_NumberOfMessagesPublished_max",
"type": "Gauge"
},
"aggregateOperator": "max",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "8fd51b53",
"key": {
"dataType": "string",
"id": "TopicName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "TopicName",
"type": "tag"
},
"op": "in",
"value": [
"$Topic"
]
},
{
"id": "b18187c3",
"key": {
"dataType": "string",
"id": "cloud.region--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "cloud.region",
"type": "tag"
},
"op": "=",
"value": "$Region"
},
{
"id": "eebe4578",
"key": {
"dataType": "string",
"id": "cloud.account.id--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "cloud.account.id",
"type": "tag"
},
"op": "=",
"value": "$Account"
}
],
"op": "AND"
},
"functions": [],
"groupBy": [
{
"dataType": "string",
"id": "TopicName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "TopicName",
"type": "tag"
}
],
"having": [],
"legend": "{{TopicName}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "avg",
"spaceAggregation": "max",
"stepInterval": 60,
"timeAggregation": "max"
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "9c67615a-55f7-42da-835c-86922f2ff8bb",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"selectedLogFields": [
{
"dataType": "string",
"name": "body",
"type": ""
},
{
"dataType": "string",
"name": "timestamp",
"type": ""
}
],
"selectedTracesFields": [
{
"dataType": "string",
"id": "serviceName--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "serviceName",
"type": "tag"
},
{
"dataType": "string",
"id": "name--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "name",
"type": "tag"
},
{
"dataType": "float64",
"id": "durationNano--float64--tag--true",
"isColumn": true,
"isJSON": false,
"key": "durationNano",
"type": "tag"
},
{
"dataType": "string",
"id": "httpMethod--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "httpMethod",
"type": "tag"
},
{
"dataType": "string",
"id": "responseStatusCode--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "responseStatusCode",
"type": "tag"
}
],
"softMax": 0,
"softMin": 0,
"stackedBarChart": false,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Number of Messages Published",
"yAxisUnit": "none"
},
{
"bucketCount": 30,
"bucketWidth": 0,
"columnUnits": {},
"description": "",
"fillSpans": false,
"id": "7a010b4e-ea7c-4a45-a9eb-93af650c45b4",
"isLogScale": false,
"isStacked": false,
"mergeAllActiveQueries": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "aws_SNS_PublishSize_max--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "aws_SNS_PublishSize_max",
"type": "Gauge"
},
"aggregateOperator": "max",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "1aa0d1a9",
"key": {
"dataType": "string",
"id": "TopicName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "TopicName",
"type": "tag"
},
"op": "in",
"value": [
"$Topic"
]
},
{
"id": "62255cff",
"key": {
"dataType": "string",
"id": "cloud.region--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "cloud.region",
"type": "tag"
},
"op": "=",
"value": "$Region"
},
{
"id": "17c7153e",
"key": {
"dataType": "string",
"id": "cloud.account.id--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "cloud.account.id",
"type": "tag"
},
"op": "=",
"value": "$Account"
}
],
"op": "AND"
},
"functions": [],
"groupBy": [
{
"dataType": "string",
"id": "TopicName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "TopicName",
"type": "tag"
}
],
"having": [],
"legend": "{{TopicName}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "avg",
"spaceAggregation": "max",
"stepInterval": 60,
"timeAggregation": "max"
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "a635a15b-dfe6-4617-a82e-29d93e27deaf",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"selectedLogFields": [
{
"dataType": "string",
"name": "body",
"type": ""
},
{
"dataType": "string",
"name": "timestamp",
"type": ""
}
],
"selectedTracesFields": [
{
"dataType": "string",
"id": "serviceName--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "serviceName",
"type": "tag"
},
{
"dataType": "string",
"id": "name--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "name",
"type": "tag"
},
{
"dataType": "float64",
"id": "durationNano--float64--tag--true",
"isColumn": true,
"isJSON": false,
"key": "durationNano",
"type": "tag"
},
{
"dataType": "string",
"id": "httpMethod--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "httpMethod",
"type": "tag"
},
{
"dataType": "string",
"id": "responseStatusCode--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "responseStatusCode",
"type": "tag"
}
],
"softMax": 0,
"softMin": 0,
"stackedBarChart": false,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Published Message Size",
"yAxisUnit": "decbytes"
},
{
"bucketCount": 30,
"bucketWidth": 0,
"columnUnits": {},
"description": "",
"fillSpans": false,
"id": "2299d4e3-6c40-4bf2-a550-c7bb8a7acd38",
"isLogScale": false,
"isStacked": false,
"mergeAllActiveQueries": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "aws_SNS_NumberOfNotificationsDelivered_max--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "aws_SNS_NumberOfNotificationsDelivered_max",
"type": "Gauge"
},
"aggregateOperator": "max",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "c96a4ac0",
"key": {
"dataType": "string",
"id": "TopicName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "TopicName",
"type": "tag"
},
"op": "in",
"value": [
"$Topic"
]
},
{
"id": "8ca86829",
"key": {
"dataType": "string",
"id": "cloud.region--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "cloud.region",
"type": "tag"
},
"op": "=",
"value": "$Region"
},
{
"id": "8a444f66",
"key": {
"dataType": "string",
"id": "cloud.account.id--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "cloud.account.id",
"type": "tag"
},
"op": "=",
"value": "$Account"
}
],
"op": "AND"
},
"functions": [],
"groupBy": [
{
"dataType": "string",
"id": "TopicName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "TopicName",
"type": "tag"
}
],
"having": [],
"legend": "{{TopicName}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "avg",
"spaceAggregation": "max",
"stepInterval": 60,
"timeAggregation": "max"
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "0d2fc26c-9b21-4dfc-b631-64b7c8d3bd71",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"selectedLogFields": [
{
"dataType": "string",
"name": "body",
"type": ""
},
{
"dataType": "string",
"name": "timestamp",
"type": ""
}
],
"selectedTracesFields": [
{
"dataType": "string",
"id": "serviceName--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "serviceName",
"type": "tag"
},
{
"dataType": "string",
"id": "name--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "name",
"type": "tag"
},
{
"dataType": "float64",
"id": "durationNano--float64--tag--true",
"isColumn": true,
"isJSON": false,
"key": "durationNano",
"type": "tag"
},
{
"dataType": "string",
"id": "httpMethod--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "httpMethod",
"type": "tag"
},
{
"dataType": "string",
"id": "responseStatusCode--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "responseStatusCode",
"type": "tag"
}
],
"softMax": 0,
"softMin": 0,
"stackedBarChart": false,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Number of Notifications Delivered",
"yAxisUnit": "none"
},
{
"bucketCount": 30,
"bucketWidth": 0,
"columnUnits": {},
"description": "",
"fillSpans": false,
"id": "16eec8b7-de1a-4039-b180-24c7a6704b6e",
"isLogScale": false,
"isStacked": false,
"mergeAllActiveQueries": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "aws_SNS_NumberOfNotificationsFailed_max--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "aws_SNS_NumberOfNotificationsFailed_max",
"type": "Gauge"
},
"aggregateOperator": "max",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "6175f3d5",
"key": {
"dataType": "string",
"id": "TopicName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "TopicName",
"type": "tag"
},
"op": "in",
"value": [
"$Topic"
]
},
{
"id": "e2084931",
"key": {
"dataType": "string",
"id": "cloud.region--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "cloud.region",
"type": "tag"
},
"op": "=",
"value": "$Region"
},
{
"id": "0b05209a",
"key": {
"dataType": "string",
"id": "cloud.account.id--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "cloud.account.id",
"type": "tag"
},
"op": "=",
"value": "$Account"
}
],
"op": "AND"
},
"functions": [],
"groupBy": [
{
"dataType": "string",
"id": "TopicName--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "TopicName",
"type": "tag"
}
],
"having": [],
"legend": "{{TopicName}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "avg",
"spaceAggregation": "max",
"stepInterval": 60,
"timeAggregation": "max"
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "526247af-6ac9-42ff-83e9-cce0e32a9e63",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"selectedLogFields": [
{
"dataType": "string",
"name": "body",
"type": ""
},
{
"dataType": "string",
"name": "timestamp",
"type": ""
}
],
"selectedTracesFields": [
{
"dataType": "string",
"id": "serviceName--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "serviceName",
"type": "tag"
},
{
"dataType": "string",
"id": "name--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "name",
"type": "tag"
},
{
"dataType": "float64",
"id": "durationNano--float64--tag--true",
"isColumn": true,
"isJSON": false,
"key": "durationNano",
"type": "tag"
},
{
"dataType": "string",
"id": "httpMethod--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "httpMethod",
"type": "tag"
},
{
"dataType": "string",
"id": "responseStatusCode--string--tag--true",
"isColumn": true,
"isJSON": false,
"key": "responseStatusCode",
"type": "tag"
}
],
"softMax": 0,
"softMin": 0,
"stackedBarChart": false,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Number of Notifications Failed",
"yAxisUnit": "none"
}
]
}

File diff suppressed because one or more lines are too long

View File

@ -55,7 +55,6 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/app/queryBuilder" "github.com/SigNoz/signoz/pkg/query-service/app/queryBuilder"
tracesV3 "github.com/SigNoz/signoz/pkg/query-service/app/traces/v3" tracesV3 "github.com/SigNoz/signoz/pkg/query-service/app/traces/v3"
tracesV4 "github.com/SigNoz/signoz/pkg/query-service/app/traces/v4" tracesV4 "github.com/SigNoz/signoz/pkg/query-service/app/traces/v4"
"github.com/SigNoz/signoz/pkg/query-service/auth"
"github.com/SigNoz/signoz/pkg/query-service/contextlinks" "github.com/SigNoz/signoz/pkg/query-service/contextlinks"
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/query-service/postprocess" "github.com/SigNoz/signoz/pkg/query-service/postprocess"
@ -255,7 +254,7 @@ func NewAPIHandler(opts APIHandlerOpts) (*APIHandler, error) {
aH.queryBuilder = queryBuilder.NewQueryBuilder(builderOpts) aH.queryBuilder = queryBuilder.NewQueryBuilder(builderOpts)
// TODO(nitya): remote this in later for multitenancy. // TODO(nitya): remote this in later for multitenancy.
orgs, err := opts.Signoz.Modules.Organization.GetAll(context.Background()) orgs, err := opts.Signoz.Modules.OrgGetter.ListByOwnedKeyRange(context.Background())
if err != nil { if err != nil {
zap.L().Warn("unexpected error while fetching orgs while initializing base api handler", zap.Error(err)) zap.L().Warn("unexpected error while fetching orgs while initializing base api handler", zap.Error(err))
} }
@ -2062,9 +2061,9 @@ func (aH *APIHandler) registerUser(w http.ResponseWriter, r *http.Request) {
return return
} }
_, apiErr := auth.Register(context.Background(), &req, aH.Signoz.Alertmanager, aH.Signoz.Modules.Organization, aH.Signoz.Modules.User, aH.Signoz.Modules.QuickFilter) _, errv2 := aH.Signoz.Modules.User.Register(r.Context(), &req)
if apiErr != nil { if errv2 != nil {
RespondError(w, apiErr, nil) render.Error(w, errv2)
return return
} }
@ -5230,3 +5229,30 @@ func (aH *APIHandler) getDomainInfo(w http.ResponseWriter, r *http.Request) {
} }
aH.Respond(w, resp) aH.Respond(w, resp)
} }
// RegisterTraceFunnelsRoutes adds trace funnels routes
func (aH *APIHandler) RegisterTraceFunnelsRoutes(router *mux.Router, am *middleware.AuthZ) {
// Main trace funnels router
traceFunnelsRouter := router.PathPrefix("/api/v1/trace-funnels").Subrouter()
// API endpoints
traceFunnelsRouter.HandleFunc("/new",
am.EditAccess(aH.Signoz.Handlers.TraceFunnel.New)).
Methods(http.MethodPost)
traceFunnelsRouter.HandleFunc("/list",
am.ViewAccess(aH.Signoz.Handlers.TraceFunnel.List)).
Methods(http.MethodGet)
traceFunnelsRouter.HandleFunc("/steps/update",
am.EditAccess(aH.Signoz.Handlers.TraceFunnel.UpdateSteps)).
Methods(http.MethodPut)
traceFunnelsRouter.HandleFunc("/{funnel_id}",
am.ViewAccess(aH.Signoz.Handlers.TraceFunnel.Get)).
Methods(http.MethodGet)
traceFunnelsRouter.HandleFunc("/{funnel_id}",
am.EditAccess(aH.Signoz.Handlers.TraceFunnel.Delete)).
Methods(http.MethodDelete)
traceFunnelsRouter.HandleFunc("/{funnel_id}",
am.EditAccess(aH.Signoz.Handlers.TraceFunnel.UpdateFunnel)).
Methods(http.MethodPut)
}

View File

@ -7,6 +7,8 @@ import (
"strings" "strings"
"unicode" "unicode"
"github.com/SigNoz/signoz/pkg/query-service/constants"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -176,6 +178,19 @@ func HydrateFileUris(spec interface{}, fs embed.FS, basedir string) (interface{}
if specMap, ok := spec.(map[string]interface{}); ok { if specMap, ok := spec.(map[string]interface{}); ok {
result := map[string]interface{}{} result := map[string]interface{}{}
for k, v := range specMap { for k, v := range specMap {
// Check if this is a dashboards slice and if dot metrics are enabled
if k == "dashboards" && constants.IsDotMetricsEnabled {
if dashboards, ok := v.([]interface{}); ok {
for i, dashboard := range dashboards {
if dashboardUri, ok := dashboard.(string); ok {
if strings.HasPrefix(dashboardUri, "file://") {
dashboards[i] = strings.Replace(dashboardUri, ".json", "_dot.json", 1)
}
}
}
v = dashboards
}
}
hydrated, err := HydrateFileUris(v, fs, basedir) hydrated, err := HydrateFileUris(v, fs, basedir)
if err != nil { if err != nil {
return nil, err return nil, err
@ -200,7 +215,6 @@ func HydrateFileUris(spec interface{}, fs embed.FS, basedir string) (interface{}
} }
return spec, nil return spec, nil
} }
func readFileIfUri(fs embed.FS, maybeFileUri string, basedir string) (interface{}, error) { func readFileIfUri(fs embed.FS, maybeFileUri string, basedir string) (interface{}, error) {

View File

@ -0,0 +1,797 @@
{
"id": "mongo-overview",
"description": "This dashboard provides a high-level overview of your MongoDB. It includes read/write performance, most-used replicas, collection metrics etc...",
"layout": [
{
"h": 3,
"i": "0c3d2b15-89be-4d62-a821-b26d93332ed3",
"moved": false,
"static": false,
"w": 6,
"x": 6,
"y": 3
},
{
"h": 3,
"i": "14504a3c-4a05-4d22-bab3-e22e94f51380",
"moved": false,
"static": false,
"w": 6,
"x": 0,
"y": 6
},
{
"h": 3,
"i": "dcfb3829-c3f2-44bb-907d-8dc8a6dc4aab",
"moved": false,
"static": false,
"w": 6,
"x": 0,
"y": 3
},
{
"h": 3,
"i": "bfc9e80b-02bf-4122-b3da-3dd943d35012",
"moved": false,
"static": false,
"w": 6,
"x": 6,
"y": 0
},
{
"h": 3,
"i": "4c07a7d2-893a-46c2-bcdb-a19b6efeac3a",
"moved": false,
"static": false,
"w": 6,
"x": 0,
"y": 0
},
{
"h": 3,
"i": "a5a64eec-1034-4aa6-8cb1-05673c4426c6",
"moved": false,
"static": false,
"w": 6,
"x": 6,
"y": 6
},
{
"h": 3,
"i": "503af589-ef4d-4fe3-8934-c8f7eb480d9a",
"moved": false,
"static": false,
"w": 6,
"x": 0,
"y": 9
}
],
"name": "",
"tags": [
"mongo",
"database"
],
"title": "Mongo overview",
"variables": {
"a2c21714-a814-4d31-9b56-7367c3208801": {
"allSelected": true,
"customValue": "",
"description": "List of hosts sending mongo metrics",
"id": "a2c21714-a814-4d31-9b56-7367c3208801",
"modificationUUID": "448e675a-4531-45b1-b434-a9ee809470d6",
"multiSelect": true,
"name": "host.name",
"order": 0,
"queryValue": "SELECT JSONExtractString(labels, 'host.name') AS `host.name`\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'mongodb_memory_usage'\nGROUP BY `host.name`",
"selectedValue": [
"Srikanths-MacBook-Pro.local"
],
"showALLOption": true,
"sort": "ASC",
"textboxValue": "",
"type": "QUERY"
}
},
"widgets": [
{
"description": "Total number of operations",
"fillSpans": false,
"id": "4c07a7d2-893a-46c2-bcdb-a19b6efeac3a",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb.operation.count--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb.operation.count",
"type": "Sum"
},
"aggregateOperator": "sum_rate",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "a468a30b",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
"dataType": "string",
"id": "operation--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "operation",
"type": "tag"
}
],
"having": [],
"legend": "{{operation}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "7da5d899-8b06-4139-9a89-47baf9551ff8",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Operations count",
"yAxisUnit": "none"
},
{
"description": "The total time spent performing operations.",
"fillSpans": false,
"id": "bfc9e80b-02bf-4122-b3da-3dd943d35012",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb.operation.time--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb.operation.time",
"type": "Sum"
},
"aggregateOperator": "sum_rate",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "31be3166",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
"dataType": "string",
"id": "operation--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "operation",
"type": "tag"
}
],
"having": [],
"legend": "{{operation}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "2ca35957-894a-46ae-a2a6-95d7e400d8e1",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Total operations time",
"yAxisUnit": "ms"
},
{
"description": "The number of cache operations",
"fillSpans": false,
"id": "dcfb3829-c3f2-44bb-907d-8dc8a6dc4aab",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb.cache.operations--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb.cache.operations",
"type": "Sum"
},
"aggregateOperator": "sum_rate",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "01b45814",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
"dataType": "string",
"id": "type--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "type",
"type": "tag"
}
],
"having": [],
"legend": "{{type}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "bb439198-dcf5-4767-b0d0-ab5785159b8d",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Cache operations",
"yAxisUnit": "none"
},
{
"description": "",
"fillSpans": false,
"id": "14504a3c-4a05-4d22-bab3-e22e94f51380",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb.operation.latency.time--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb.operation.latency.time",
"type": "Gauge"
},
"aggregateOperator": "max",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "2e165319",
"key": {
"dataType": "string",
"id": "operation--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "operation",
"type": "tag"
},
"op": "=",
"value": "read"
},
{
"id": "888e920b",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [],
"having": [],
"legend": "Latency",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "4a9cafe8-778b-476c-b825-c04e165bf285",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Read latency",
"yAxisUnit": "µs"
},
{
"description": "",
"fillSpans": false,
"id": "a5a64eec-1034-4aa6-8cb1-05673c4426c6",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb.operation.latency.time--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb.operation.latency.time",
"type": "Gauge"
},
"aggregateOperator": "max",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "53b37ca7",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
},
{
"id": "9862c46c",
"key": {
"dataType": "string",
"id": "operation--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "operation",
"type": "tag"
},
"op": "=",
"value": "write"
}
],
"op": "AND"
},
"groupBy": [],
"having": [],
"legend": "Latency",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "446827eb-a4f2-4ff3-966b-fb65288c983b",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Write latency",
"yAxisUnit": "µs"
},
{
"description": "",
"fillSpans": false,
"id": "503af589-ef4d-4fe3-8934-c8f7eb480d9a",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb.operation.latency.time--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb.operation.latency.time",
"type": "Gauge"
},
"aggregateOperator": "max",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "c33ad4b6",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
},
{
"id": "c70ecfd0",
"key": {
"dataType": "string",
"id": "operation--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "operation",
"type": "tag"
},
"op": "=",
"value": "command"
}
],
"op": "AND"
},
"groupBy": [],
"having": [],
"legend": "Latency",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "7b7b977d-0921-4552-8cfe-d82dfde63ef4",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Command latency",
"yAxisUnit": "µs"
},
{
"description": "",
"fillSpans": false,
"id": "0c3d2b15-89be-4d62-a821-b26d93332ed3",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb.network.io.receive--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb.network.io.receive",
"type": "Sum"
},
"aggregateOperator": "avg",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "5c9d7fe3",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
}
],
"having": [],
"legend": "Bytes received :: {{host.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
},
{
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb.network.io.transmit--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb.network.io.transmit",
"type": "Sum"
},
"aggregateOperator": "avg",
"dataSource": "metrics",
"disabled": false,
"expression": "B",
"filters": {
"items": [
{
"id": "96520885",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
}
],
"having": [],
"legend": "Bytes transmitted :: {{host.name}}",
"limit": null,
"orderBy": [],
"queryName": "B",
"reduceTo": "sum",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "41eea5bc-f9cf-45c2-92fb-ef226d6b540b",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Network IO",
"yAxisUnit": "bytes"
}
]
}

View File

@ -0,0 +1,924 @@
{
"id": "redis-overview",
"description": "This dashboard shows the Redis instance overview. It includes latency, hit/miss rate, connections, and memory information.\n",
"layout": [
{
"h": 3,
"i": "d4c164bc-8fc2-4dbc-aadd-8d17479ca649",
"moved": false,
"static": false,
"w": 6,
"x": 0,
"y": 9
},
{
"h": 3,
"i": "2fbaef0d-3cdb-4ce3-aa3c-9bbbb41786d9",
"moved": false,
"static": false,
"w": 6,
"x": 3,
"y": 6
},
{
"h": 3,
"i": "f5ee1511-0d2b-4404-9ce0-e991837decc2",
"moved": false,
"static": false,
"w": 6,
"x": 6,
"y": 3
},
{
"h": 3,
"i": "b19c7058-b806-4ea2-974a-ca555b168991",
"moved": false,
"static": false,
"w": 6,
"x": 0,
"y": 3
},
{
"h": 3,
"i": "bf0deeeb-e926-4234-944c-82bacd96af47",
"moved": false,
"static": false,
"w": 6,
"x": 6,
"y": 0
},
{
"h": 3,
"i": "a77227c7-16f5-4353-952e-b183c715a61c",
"moved": false,
"static": false,
"w": 6,
"x": 0,
"y": 0
},
{
"h": 3,
"i": "9698cee2-b1f3-4c0b-8c9f-3da4f0e05f17",
"moved": false,
"static": false,
"w": 6,
"x": 6,
"y": 9
},
{
"h": 3,
"i": "64a5f303-d7db-44ff-9a0e-948e5c653320",
"moved": false,
"static": false,
"w": 6,
"x": 0,
"y": 12
},
{
"h": 3,
"i": "3e80a918-69af-4c9a-bc57-a94e1d41b05c",
"moved": false,
"static": false,
"w": 6,
"x": 6,
"y": 12
}
],
"name": "",
"tags": [
"redis",
"database"
],
"title": "Redis overview",
"variables": {
"94f19b3c-ad9f-4b47-a9b2-f312c09fa965": {
"allSelected": true,
"customValue": "",
"description": "List of hosts sending Redis metrics",
"id": "94f19b3c-ad9f-4b47-a9b2-f312c09fa965",
"key": "94f19b3c-ad9f-4b47-a9b2-f312c09fa965",
"modificationUUID": "4c5b0c03-9cbc-425b-8d8e-7152e5c39ba8",
"multiSelect": true,
"name": "host.name",
"order": 0,
"queryValue": "SELECT JSONExtractString(labels, 'host.name') AS `host.name`\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'redis.cpu.time'\nGROUP BY `host.name`",
"selectedValue": [
"Srikanths-MacBook-Pro.local"
],
"showALLOption": true,
"sort": "ASC",
"textboxValue": "",
"type": "QUERY"
}
},
"widgets": [
{
"description": "Rate successful lookup of keys in the main dictionary",
"fillSpans": false,
"id": "a77227c7-16f5-4353-952e-b183c715a61c",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "redis.keyspace.hits--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "redis.keyspace.hits",
"type": "Sum"
},
"aggregateOperator": "sum_rate",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "e99669ea",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [],
"having": [],
"legend": "Hit/s across all hosts",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "42c9c117-bfaf-49f7-b528-aad099392295",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Hits/s",
"yAxisUnit": "none"
},
{
"description": "Number of clients pending on a blocking call",
"fillSpans": false,
"id": "bf0deeeb-e926-4234-944c-82bacd96af47",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "redis.clients.blocked--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "redis.clients.blocked",
"type": "Sum"
},
"aggregateOperator": "sum",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "97247f25",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [],
"having": [],
"legend": "Blocked clients across all hosts",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "b77a9e11-fb98-4a95-88a8-c3ad25c14369",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Clients blocked",
"yAxisUnit": "none"
},
{
"description": "",
"fillSpans": false,
"id": "b19c7058-b806-4ea2-974a-ca555b168991",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "",
"id": "redis.db.keys------false",
"isColumn": false,
"key": "redis.db.keys",
"type": ""
},
"aggregateOperator": "sum",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [],
"op": "AND"
},
"groupBy": [],
"having": [],
"legend": "",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "b77a9e11-fb98-4a95-88a8-c3ad25c14369",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Keyspace Keys",
"yAxisUnit": "none"
},
{
"description": "Number of changes since the last dump",
"fillSpans": false,
"id": "f5ee1511-0d2b-4404-9ce0-e991837decc2",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "redis.rdb.changes_since_last_save--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "redis.rdb.changes_since_last_save",
"type": "Sum"
},
"aggregateOperator": "sum",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "d4aef346",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [],
"having": [],
"legend": "Number of unsaved changes",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "32cedddf-606d-4de1-8c1d-4b7049e6430c",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Unsaved changes",
"yAxisUnit": "none"
},
{
"description": "",
"fillSpans": false,
"id": "2fbaef0d-3cdb-4ce3-aa3c-9bbbb41786d9",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "redis.commands--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "redis.commands",
"type": "Gauge"
},
"aggregateOperator": "sum",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "458dc402",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [],
"having": [],
"legend": "ops/s",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "c70de4dd-a68a-42df-a249-6610c296709c",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Command/s",
"yAxisUnit": "ops"
},
{
"description": "",
"fillSpans": false,
"id": "d4c164bc-8fc2-4dbc-aadd-8d17479ca649",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "redis.memory.used--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "redis.memory.used",
"type": "Gauge"
},
"aggregateOperator": "sum",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "394a537e",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
}
],
"having": [],
"legend": "Used::{{host.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
},
{
"aggregateAttribute": {
"dataType": "float64",
"id": "redis.maxmemory--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "redis.maxmemory",
"type": "Gauge"
},
"aggregateOperator": "max",
"dataSource": "metrics",
"disabled": false,
"expression": "B",
"filters": {
"items": [
{
"id": "0c0754da",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
}
],
"having": [],
"legend": "Max::{{host.name}}",
"limit": null,
"orderBy": [],
"queryName": "B",
"reduceTo": "sum",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "2f47df76-f09e-4152-8623-971f0fe66bfe",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Memory usage",
"yAxisUnit": "bytes"
},
{
"description": "",
"fillSpans": false,
"id": "9698cee2-b1f3-4c0b-8c9f-3da4f0e05f17",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "redis.memory.rss--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "redis.memory.rss",
"type": "Gauge"
},
"aggregateOperator": "sum",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "4dc9ae49",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
}
],
"having": [],
"legend": "Rss::{{host.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "fddd043c-1385-481c-9f4c-381f261e1dd9",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "RSS Memory",
"yAxisUnit": "bytes"
},
{
"description": "",
"fillSpans": false,
"id": "64a5f303-d7db-44ff-9a0e-948e5c653320",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "redis.memory.fragmentation_ratio--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "redis.memory.fragmentation_ratio",
"type": "Gauge"
},
"aggregateOperator": "avg",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "79dc25f3",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
}
],
"having": [],
"legend": "Rss::{{host.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "3e802b07-0249-4d79-a5c7-6580ab535ad0",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Fragmentation ratio",
"yAxisUnit": "short"
},
{
"description": "Number of evicted keys due to maxmemory limit",
"fillSpans": false,
"id": "3e80a918-69af-4c9a-bc57-a94e1d41b05c",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregateAttribute": {
"dataType": "float64",
"id": "redis.keys.evicted--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "redis.keys.evicted",
"type": "Sum"
},
"aggregateOperator": "sum_rate",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filters": {
"items": [
{
"id": "53d189ac",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
}
],
"having": [],
"legend": "Rss::{{host.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "15d1d9d7-eb10-464b-aa7b-33ff211996f7",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Eviction rate",
"yAxisUnit": "short"
}
]
}

View File

@ -3,12 +3,18 @@ package integrations
import ( import (
"context" "context"
"testing" "testing"
"time"
"github.com/SigNoz/signoz/pkg/emailing" "github.com/SigNoz/signoz/pkg/alertmanager"
"github.com/SigNoz/signoz/pkg/emailing/noopemailing" "github.com/SigNoz/signoz/pkg/alertmanager/alertmanagerserver"
"github.com/SigNoz/signoz/pkg/alertmanager/signozalertmanager"
"github.com/SigNoz/signoz/pkg/emailing/emailingtest"
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest" "github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization" "github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
"github.com/SigNoz/signoz/pkg/modules/user/impluser" "github.com/SigNoz/signoz/pkg/sharder"
"github.com/SigNoz/signoz/pkg/sharder/noopsharder"
"github.com/SigNoz/signoz/pkg/signoz"
"github.com/SigNoz/signoz/pkg/types/authtypes"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -19,11 +25,14 @@ func TestIntegrationLifecycle(t *testing.T) {
mgr, store := NewTestIntegrationsManager(t) mgr, store := NewTestIntegrationsManager(t)
ctx := context.Background() ctx := context.Background()
organizationModule := implorganization.NewModule(implorganization.NewStore(store))
providerSettings := instrumentationtest.New().ToProviderSettings() providerSettings := instrumentationtest.New().ToProviderSettings()
emailing, _ := noopemailing.New(context.Background(), providerSettings, emailing.Config{}) sharder, _ := noopsharder.New(context.TODO(), providerSettings, sharder.Config{})
userModule := impluser.NewModule(impluser.NewStore(store, providerSettings), nil, emailing, providerSettings) orgGetter := implorganization.NewGetter(implorganization.NewStore(store), sharder)
user, apiErr := createTestUser(organizationModule, userModule) alertmanager, _ := signozalertmanager.New(context.TODO(), providerSettings, alertmanager.Config{Provider: "signoz", Signoz: alertmanager.Signoz{PollInterval: 10 * time.Second, Config: alertmanagerserver.NewConfig()}}, store, orgGetter)
jwt := authtypes.NewJWT("", 1*time.Hour, 1*time.Hour)
emailing := emailingtest.New()
modules := signoz.NewModules(store, jwt, emailing, providerSettings, orgGetter, alertmanager)
user, apiErr := createTestUser(modules.OrgSetter, modules.User)
if apiErr != nil { if apiErr != nil {
t.Fatalf("could not create test user: %v", apiErr) t.Fatalf("could not create test user: %v", apiErr)
} }

View File

@ -30,7 +30,7 @@ func NewTestIntegrationsManager(t *testing.T) (*Manager, sqlstore.SQLStore) {
}, testDB }, testDB
} }
func createTestUser(organizationModule organization.Module, userModule user.Module) (*types.User, *model.ApiError) { func createTestUser(organizationModule organization.Setter, userModule user.Module) (*types.User, *model.ApiError) {
// Create a test user for auth // Create a test user for auth
ctx := context.Background() ctx := context.Background()
organization := types.NewOrganization("test") organization := types.NewOrganization("test")

View File

@ -15,6 +15,7 @@ import (
"github.com/SigNoz/signoz/pkg/apis/fields" "github.com/SigNoz/signoz/pkg/apis/fields"
"github.com/SigNoz/signoz/pkg/http/middleware" "github.com/SigNoz/signoz/pkg/http/middleware"
"github.com/SigNoz/signoz/pkg/licensing/nooplicensing" "github.com/SigNoz/signoz/pkg/licensing/nooplicensing"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/prometheus" "github.com/SigNoz/signoz/pkg/prometheus"
"github.com/SigNoz/signoz/pkg/query-service/agentConf" "github.com/SigNoz/signoz/pkg/query-service/agentConf"
"github.com/SigNoz/signoz/pkg/query-service/app/clickhouseReader" "github.com/SigNoz/signoz/pkg/query-service/app/clickhouseReader"
@ -101,6 +102,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
serverOptions.SigNoz.SQLStore, serverOptions.SigNoz.SQLStore,
serverOptions.SigNoz.TelemetryStore, serverOptions.SigNoz.TelemetryStore,
serverOptions.SigNoz.Prometheus, serverOptions.SigNoz.Prometheus,
serverOptions.SigNoz.Modules.OrgGetter,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -194,7 +196,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
&opAmpModel.AllAgents, agentConfMgr, &opAmpModel.AllAgents, agentConfMgr,
) )
orgs, err := apiHandler.Signoz.Modules.Organization.GetAll(context.Background()) orgs, err := apiHandler.Signoz.Modules.OrgGetter.ListByOwnedKeyRange(context.Background())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -212,14 +214,14 @@ func (s *Server) createPrivateServer(api *APIHandler) (*http.Server, error) {
r := NewRouter() r := NewRouter()
r.Use(middleware.NewAuth(s.serverOptions.Jwt, []string{"Authorization", "Sec-WebSocket-Protocol"}).Wrap) r.Use(middleware.NewAuth(s.serverOptions.Jwt, []string{"Authorization", "Sec-WebSocket-Protocol"}, s.serverOptions.SigNoz.Sharder, s.serverOptions.SigNoz.Instrumentation.Logger()).Wrap)
r.Use(middleware.NewTimeout(s.serverOptions.SigNoz.Instrumentation.Logger(), r.Use(middleware.NewTimeout(s.serverOptions.SigNoz.Instrumentation.Logger(),
s.serverOptions.Config.APIServer.Timeout.ExcludedRoutes, s.serverOptions.Config.APIServer.Timeout.ExcludedRoutes,
s.serverOptions.Config.APIServer.Timeout.Default, s.serverOptions.Config.APIServer.Timeout.Default,
s.serverOptions.Config.APIServer.Timeout.Max, s.serverOptions.Config.APIServer.Timeout.Max,
).Wrap) ).Wrap)
r.Use(middleware.NewAnalytics().Wrap) r.Use(middleware.NewAnalytics().Wrap)
r.Use(middleware.NewAPIKey(s.serverOptions.SigNoz.SQLStore, []string{"SIGNOZ-API-KEY"}, s.serverOptions.SigNoz.Instrumentation.Logger()).Wrap) r.Use(middleware.NewAPIKey(s.serverOptions.SigNoz.SQLStore, []string{"SIGNOZ-API-KEY"}, s.serverOptions.SigNoz.Instrumentation.Logger(), s.serverOptions.SigNoz.Sharder).Wrap)
r.Use(middleware.NewLogging(s.serverOptions.SigNoz.Instrumentation.Logger(), s.serverOptions.Config.APIServer.Logging.ExcludedRoutes).Wrap) r.Use(middleware.NewLogging(s.serverOptions.SigNoz.Instrumentation.Logger(), s.serverOptions.Config.APIServer.Logging.ExcludedRoutes).Wrap)
api.RegisterPrivateRoutes(r) api.RegisterPrivateRoutes(r)
@ -243,14 +245,14 @@ func (s *Server) createPrivateServer(api *APIHandler) (*http.Server, error) {
func (s *Server) createPublicServer(api *APIHandler, web web.Web) (*http.Server, error) { func (s *Server) createPublicServer(api *APIHandler, web web.Web) (*http.Server, error) {
r := NewRouter() r := NewRouter()
r.Use(middleware.NewAuth(s.serverOptions.Jwt, []string{"Authorization", "Sec-WebSocket-Protocol"}).Wrap) r.Use(middleware.NewAuth(s.serverOptions.Jwt, []string{"Authorization", "Sec-WebSocket-Protocol"}, s.serverOptions.SigNoz.Sharder, s.serverOptions.SigNoz.Instrumentation.Logger()).Wrap)
r.Use(middleware.NewTimeout(s.serverOptions.SigNoz.Instrumentation.Logger(), r.Use(middleware.NewTimeout(s.serverOptions.SigNoz.Instrumentation.Logger(),
s.serverOptions.Config.APIServer.Timeout.ExcludedRoutes, s.serverOptions.Config.APIServer.Timeout.ExcludedRoutes,
s.serverOptions.Config.APIServer.Timeout.Default, s.serverOptions.Config.APIServer.Timeout.Default,
s.serverOptions.Config.APIServer.Timeout.Max, s.serverOptions.Config.APIServer.Timeout.Max,
).Wrap) ).Wrap)
r.Use(middleware.NewAnalytics().Wrap) r.Use(middleware.NewAnalytics().Wrap)
r.Use(middleware.NewAPIKey(s.serverOptions.SigNoz.SQLStore, []string{"SIGNOZ-API-KEY"}, s.serverOptions.SigNoz.Instrumentation.Logger()).Wrap) r.Use(middleware.NewAPIKey(s.serverOptions.SigNoz.SQLStore, []string{"SIGNOZ-API-KEY"}, s.serverOptions.SigNoz.Instrumentation.Logger(), s.serverOptions.SigNoz.Sharder).Wrap)
r.Use(middleware.NewLogging(s.serverOptions.SigNoz.Instrumentation.Logger(), s.serverOptions.Config.APIServer.Logging.ExcludedRoutes).Wrap) r.Use(middleware.NewLogging(s.serverOptions.SigNoz.Instrumentation.Logger(), s.serverOptions.Config.APIServer.Logging.ExcludedRoutes).Wrap)
am := middleware.NewAuthZ(s.serverOptions.SigNoz.Instrumentation.Logger()) am := middleware.NewAuthZ(s.serverOptions.SigNoz.Instrumentation.Logger())
@ -267,6 +269,7 @@ func (s *Server) createPublicServer(api *APIHandler, web web.Web) (*http.Server,
api.RegisterMessagingQueuesRoutes(r, am) api.RegisterMessagingQueuesRoutes(r, am)
api.RegisterThirdPartyApiRoutes(r, am) api.RegisterThirdPartyApiRoutes(r, am)
api.MetricExplorerRoutes(r, am) api.MetricExplorerRoutes(r, am)
api.RegisterTraceFunnelsRoutes(r, am)
c := cors.New(cors.Options{ c := cors.New(cors.Options{
AllowedOrigins: []string{"*"}, AllowedOrigins: []string{"*"},
@ -416,6 +419,7 @@ func makeRulesManager(
sqlstore sqlstore.SQLStore, sqlstore sqlstore.SQLStore,
telemetryStore telemetrystore.TelemetryStore, telemetryStore telemetrystore.TelemetryStore,
prometheus prometheus.Prometheus, prometheus prometheus.Prometheus,
orgGetter organization.Getter,
) (*rules.Manager, error) { ) (*rules.Manager, error) {
// create manager opts // create manager opts
managerOpts := &rules.ManagerOptions{ managerOpts := &rules.ManagerOptions{
@ -428,6 +432,7 @@ func makeRulesManager(
Cache: cache, Cache: cache,
EvalDelay: constants.GetEvalDelay(), EvalDelay: constants.GetEvalDelay(),
SQLStore: sqlstore, SQLStore: sqlstore,
OrgGetter: orgGetter,
} }
// create Manager // create Manager

View File

@ -87,7 +87,7 @@ func existsSubQueryForFixedColumn(key v3.AttributeKey, op v3.FilterOperator) (st
} }
} }
func buildTracesFilterQuery(fs *v3.FilterSet) (string, error) { func BuildTracesFilterQuery(fs *v3.FilterSet) (string, error) {
var conditions []string var conditions []string
if fs != nil && len(fs.Items) != 0 { if fs != nil && len(fs.Items) != 0 {
@ -167,7 +167,7 @@ func handleEmptyValuesInGroupBy(groupBy []v3.AttributeKey) (string, error) {
Operator: "AND", Operator: "AND",
Items: filterItems, Items: filterItems,
} }
return buildTracesFilterQuery(&filterSet) return BuildTracesFilterQuery(&filterSet)
} }
return "", nil return "", nil
} }
@ -248,7 +248,7 @@ func buildTracesQuery(start, end, step int64, mq *v3.BuilderQuery, panelType v3.
timeFilter := fmt.Sprintf("(timestamp >= '%d' AND timestamp <= '%d') AND (ts_bucket_start >= %d AND ts_bucket_start <= %d)", tracesStart, tracesEnd, bucketStart, bucketEnd) timeFilter := fmt.Sprintf("(timestamp >= '%d' AND timestamp <= '%d') AND (ts_bucket_start >= %d AND ts_bucket_start <= %d)", tracesStart, tracesEnd, bucketStart, bucketEnd)
filterSubQuery, err := buildTracesFilterQuery(mq.Filters) filterSubQuery, err := BuildTracesFilterQuery(mq.Filters)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -211,7 +211,7 @@ func Test_buildTracesFilterQuery(t *testing.T) {
want: "", want: "",
}, },
{ {
name: "Test buildTracesFilterQuery in, nin", name: "Test BuildTracesFilterQuery in, nin",
args: args{ args: args{
fs: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{ fs: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "method", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}, Value: []interface{}{"GET", "POST"}, Operator: v3.FilterOperatorIn}, {Key: v3.AttributeKey{Key: "method", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}, Value: []interface{}{"GET", "POST"}, Operator: v3.FilterOperatorIn},
@ -226,7 +226,7 @@ func Test_buildTracesFilterQuery(t *testing.T) {
wantErr: false, wantErr: false,
}, },
{ {
name: "Test buildTracesFilterQuery not eq, neq, gt, lt, gte, lte", name: "Test BuildTracesFilterQuery not eq, neq, gt, lt, gte, lte",
args: args{ args: args{
fs: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{ fs: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "duration", DataType: v3.AttributeKeyDataTypeInt64, Type: v3.AttributeKeyTypeTag}, Value: 102, Operator: v3.FilterOperatorEqual}, {Key: v3.AttributeKey{Key: "duration", DataType: v3.AttributeKeyDataTypeInt64, Type: v3.AttributeKeyTypeTag}, Value: 102, Operator: v3.FilterOperatorEqual},
@ -274,13 +274,13 @@ func Test_buildTracesFilterQuery(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
got, err := buildTracesFilterQuery(tt.args.fs) got, err := BuildTracesFilterQuery(tt.args.fs)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("buildTracesFilterQuery() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("BuildTracesFilterQuery() error = %v, wantErr %v", err, tt.wantErr)
return return
} }
if got != tt.want { if got != tt.want {
t.Errorf("buildTracesFilterQuery() = %v, want %v", got, tt.want) t.Errorf("BuildTracesFilterQuery() = %v, want %v", got, tt.want)
} }
}) })
} }

View File

@ -1,65 +0,0 @@
package auth
import (
"context"
"github.com/SigNoz/signoz/pkg/alertmanager"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/modules/quickfilter"
"github.com/SigNoz/signoz/pkg/modules/user"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/types"
)
func RegisterOrgAndFirstUser(ctx context.Context, req *types.PostableRegisterOrgAndAdmin, organizationModule organization.Module, userModule user.Module) (*types.User, *model.ApiError) {
if req.Email == "" {
return nil, model.BadRequest(model.ErrEmailRequired{})
}
if req.Password == "" {
return nil, model.BadRequest(model.ErrPasswordRequired{})
}
organization := types.NewOrganization(req.OrgDisplayName)
err := organizationModule.Create(ctx, organization)
if err != nil {
return nil, model.InternalError(err)
}
user, err := types.NewUser(req.Name, req.Email, types.RoleAdmin.String(), organization.ID.StringValue())
if err != nil {
return nil, model.InternalError(err)
}
password, err := types.NewFactorPassword(req.Password)
if err != nil {
return nil, model.InternalError(err)
}
user, err = userModule.CreateUserWithPassword(ctx, user, password)
if err != nil {
return nil, model.InternalError(err)
}
return user, nil
}
// First user registration
func Register(ctx context.Context, req *types.PostableRegisterOrgAndAdmin, alertmanager alertmanager.Alertmanager, organizationModule organization.Module, userModule user.Module, quickfiltermodule quickfilter.Module) (*types.User, *model.ApiError) {
user, err := RegisterOrgAndFirstUser(ctx, req, organizationModule, userModule)
if err != nil {
return nil, err
}
if err := alertmanager.SetDefaultConfig(ctx, user.OrgID); err != nil {
return nil, model.InternalError(err)
}
if err := quickfiltermodule.SetDefaultConfig(ctx, valuer.MustNewUUID(user.OrgID)); err != nil {
return nil, model.InternalError(err)
}
return user, nil
}

View File

@ -12,6 +12,7 @@ import (
"github.com/SigNoz/signoz/pkg/factory" "github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/licensing" "github.com/SigNoz/signoz/pkg/licensing"
"github.com/SigNoz/signoz/pkg/licensing/nooplicensing" "github.com/SigNoz/signoz/pkg/licensing/nooplicensing"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/query-service/app" "github.com/SigNoz/signoz/pkg/query-service/app"
"github.com/SigNoz/signoz/pkg/query-service/constants" "github.com/SigNoz/signoz/pkg/query-service/constants"
"github.com/SigNoz/signoz/pkg/signoz" "github.com/SigNoz/signoz/pkg/signoz"
@ -121,7 +122,7 @@ func main() {
zeus.Config{}, zeus.Config{},
noopzeus.NewProviderFactory(), noopzeus.NewProviderFactory(),
licensing.Config{}, licensing.Config{},
func(_ sqlstore.SQLStore, _ zeus.Zeus) factory.ProviderFactory[licensing.Licensing, licensing.Config] { func(_ sqlstore.SQLStore, _ zeus.Zeus, _ organization.Getter) factory.ProviderFactory[licensing.Licensing, licensing.Config] {
return nooplicensing.NewFactory() return nooplicensing.NewFactory()
}, },
signoz.NewEmailingProviderFactories(), signoz.NewEmailingProviderFactories(),

View File

@ -1,36 +0,0 @@
package model
import "fmt"
// custom errors related to registration
type ErrFeatureUnavailable struct {
Key string
}
func (errFeatureUnavailable ErrFeatureUnavailable) Error() string {
return fmt.Sprintf("feature unavailable: %s", errFeatureUnavailable.Key)
}
type ErrEmailRequired struct{}
func (errEmailRequired ErrEmailRequired) Error() string {
return "email is required"
}
type ErrPasswordRequired struct{}
func (errPasswordRequired ErrPasswordRequired) Error() string {
return "password is required"
}
type ErrSignupFailed struct{}
func (errSignupFailed ErrSignupFailed) Error() string {
return "failed to register user"
}
type ErrNoOrgFound struct{}
func (errNoOrgFound ErrNoOrgFound) Error() string {
return "no org found"
}

View File

@ -19,6 +19,7 @@ import (
"github.com/SigNoz/signoz/pkg/alertmanager" "github.com/SigNoz/signoz/pkg/alertmanager"
"github.com/SigNoz/signoz/pkg/cache" "github.com/SigNoz/signoz/pkg/cache"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/prometheus" "github.com/SigNoz/signoz/pkg/prometheus"
"github.com/SigNoz/signoz/pkg/query-service/interfaces" "github.com/SigNoz/signoz/pkg/query-service/interfaces"
"github.com/SigNoz/signoz/pkg/query-service/model" "github.com/SigNoz/signoz/pkg/query-service/model"
@ -95,6 +96,7 @@ type ManagerOptions struct {
PrepareTestRuleFunc func(opts PrepareTestRuleOptions) (int, *model.ApiError) PrepareTestRuleFunc func(opts PrepareTestRuleOptions) (int, *model.ApiError)
Alertmanager alertmanager.Alertmanager Alertmanager alertmanager.Alertmanager
SQLStore sqlstore.SQLStore SQLStore sqlstore.SQLStore
OrgGetter organization.Getter
} }
// The Manager manages recording and alerting rules. // The Manager manages recording and alerting rules.
@ -116,6 +118,7 @@ type Manager struct {
alertmanager alertmanager.Alertmanager alertmanager alertmanager.Alertmanager
sqlstore sqlstore.SQLStore sqlstore sqlstore.SQLStore
orgGetter organization.Getter
} }
func defaultOptions(o *ManagerOptions) *ManagerOptions { func defaultOptions(o *ManagerOptions) *ManagerOptions {
@ -210,6 +213,7 @@ func NewManager(o *ManagerOptions) (*Manager, error) {
prepareTestRuleFunc: o.PrepareTestRuleFunc, prepareTestRuleFunc: o.PrepareTestRuleFunc,
alertmanager: o.Alertmanager, alertmanager: o.Alertmanager,
sqlstore: o.SQLStore, sqlstore: o.SQLStore,
orgGetter: o.OrgGetter,
} }
return m, nil return m, nil
@ -239,14 +243,14 @@ func (m *Manager) Pause(b bool) {
} }
func (m *Manager) initiate(ctx context.Context) error { func (m *Manager) initiate(ctx context.Context) error {
orgIDs, err := m.ruleStore.ListOrgs(ctx) orgs, err := m.orgGetter.ListByOwnedKeyRange(ctx)
if err != nil { if err != nil {
return err return err
} }
var loadErrors []error var loadErrors []error
for _, orgID := range orgIDs { for _, org := range orgs {
storedRules, err := m.ruleStore.GetStoredRules(ctx, orgID.StringValue()) storedRules, err := m.ruleStore.GetStoredRules(ctx, org.ID.StringValue())
if err != nil { if err != nil {
return err return err
} }
@ -279,7 +283,7 @@ func (m *Manager) initiate(ctx context.Context) error {
} }
} }
if !parsedRule.Disabled { if !parsedRule.Disabled {
err := m.addTask(ctx, orgID, parsedRule, taskName) err := m.addTask(ctx, org.ID, parsedRule, taskName)
if err != nil { if err != nil {
zap.L().Error("failed to load the rule definition", zap.String("name", taskName), zap.Error(err)) zap.L().Error("failed to load the rule definition", zap.String("name", taskName), zap.Error(err))
} }

View File

@ -11,8 +11,12 @@ import (
"testing" "testing"
"time" "time"
"github.com/SigNoz/signoz/pkg/emailing" "github.com/SigNoz/signoz/pkg/alertmanager"
"github.com/SigNoz/signoz/pkg/emailing/noopemailing" "github.com/SigNoz/signoz/pkg/alertmanager/alertmanagerserver"
"github.com/SigNoz/signoz/pkg/alertmanager/signozalertmanager"
"github.com/SigNoz/signoz/pkg/emailing/emailingtest"
"github.com/SigNoz/signoz/pkg/sharder"
"github.com/SigNoz/signoz/pkg/sharder/noopsharder"
"github.com/SigNoz/signoz/pkg/types/authtypes" "github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/http/middleware" "github.com/SigNoz/signoz/pkg/http/middleware"
@ -304,16 +308,22 @@ func NewFilterSuggestionsTestBed(t *testing.T) *FilterSuggestionsTestBed {
mockClickhouse.MatchExpectationsInOrder(false) mockClickhouse.MatchExpectationsInOrder(false)
providerSettings := instrumentationtest.New().ToProviderSettings() providerSettings := instrumentationtest.New().ToProviderSettings()
emailing, _ := noopemailing.New(context.Background(), providerSettings, emailing.Config{}) sharder, err := noopsharder.New(context.TODO(), providerSettings, sharder.Config{})
require.NoError(t, err)
orgGetter := implorganization.NewGetter(implorganization.NewStore(testDB), sharder)
alertmanager, err := signozalertmanager.New(context.TODO(), providerSettings, alertmanager.Config{Signoz: alertmanager.Signoz{PollInterval: 10 * time.Second, Config: alertmanagerserver.NewConfig()}}, testDB, orgGetter)
require.NoError(t, err)
jwt := authtypes.NewJWT("", 1*time.Hour, 1*time.Hour) jwt := authtypes.NewJWT("", 1*time.Hour, 1*time.Hour)
modules := signoz.NewModules(testDB, jwt, emailing, providerSettings) emailing := emailingtest.New()
modules := signoz.NewModules(testDB, jwt, emailing, providerSettings, orgGetter, alertmanager)
handlers := signoz.NewHandlers(modules)
apiHandler, err := app.NewAPIHandler(app.APIHandlerOpts{ apiHandler, err := app.NewAPIHandler(app.APIHandlerOpts{
Reader: reader, Reader: reader,
JWT: jwt, JWT: jwt,
Signoz: &signoz.SigNoz{ Signoz: &signoz.SigNoz{
Modules: modules, Modules: modules,
Handlers: signoz.NewHandlers(modules), Handlers: handlers,
}, },
}) })
if err != nil { if err != nil {
@ -322,13 +332,12 @@ func NewFilterSuggestionsTestBed(t *testing.T) *FilterSuggestionsTestBed {
router := app.NewRouter() router := app.NewRouter()
//add the jwt middleware //add the jwt middleware
router.Use(middleware.NewAuth(jwt, []string{"Authorization", "Sec-WebSocket-Protocol"}).Wrap) router.Use(middleware.NewAuth(jwt, []string{"Authorization", "Sec-WebSocket-Protocol"}, sharder, instrumentationtest.New().Logger()).Wrap)
am := middleware.NewAuthZ(instrumentationtest.New().Logger()) am := middleware.NewAuthZ(instrumentationtest.New().Logger())
apiHandler.RegisterRoutes(router, am) apiHandler.RegisterRoutes(router, am)
apiHandler.RegisterQueryRangeV3Routes(router, am) apiHandler.RegisterQueryRangeV3Routes(router, am)
organizationModule := implorganization.NewModule(implorganization.NewStore(testDB)) user, apiErr := createTestUser(modules.OrgSetter, modules.User)
user, apiErr := createTestUser(organizationModule, modules.User)
if apiErr != nil { if apiErr != nil {
t.Fatalf("could not create a test user: %v", apiErr) t.Fatalf("could not create a test user: %v", apiErr)
} }

View File

@ -11,8 +11,10 @@ import (
"testing" "testing"
"time" "time"
"github.com/SigNoz/signoz/pkg/emailing" "github.com/SigNoz/signoz/pkg/alertmanager"
"github.com/SigNoz/signoz/pkg/emailing/noopemailing" "github.com/SigNoz/signoz/pkg/alertmanager/alertmanagerserver"
"github.com/SigNoz/signoz/pkg/alertmanager/signozalertmanager"
"github.com/SigNoz/signoz/pkg/emailing/emailingtest"
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest" "github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization" "github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
"github.com/SigNoz/signoz/pkg/modules/user" "github.com/SigNoz/signoz/pkg/modules/user"
@ -26,6 +28,8 @@ 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/query-service/queryBuilderToExpr" "github.com/SigNoz/signoz/pkg/query-service/queryBuilderToExpr"
"github.com/SigNoz/signoz/pkg/query-service/utils" "github.com/SigNoz/signoz/pkg/query-service/utils"
"github.com/SigNoz/signoz/pkg/sharder"
"github.com/SigNoz/signoz/pkg/sharder/noopsharder"
"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/types" "github.com/SigNoz/signoz/pkg/types"
@ -480,9 +484,14 @@ func NewTestbedWithoutOpamp(t *testing.T, sqlStore sqlstore.SQLStore) *LogPipeli
} }
providerSettings := instrumentationtest.New().ToProviderSettings() providerSettings := instrumentationtest.New().ToProviderSettings()
emailing, _ := noopemailing.New(context.Background(), providerSettings, emailing.Config{}) sharder, err := noopsharder.New(context.TODO(), providerSettings, sharder.Config{})
jwt := authtypes.NewJWT("", 10*time.Minute, 30*time.Minute) require.NoError(t, err)
modules := signoz.NewModules(sqlStore, jwt, emailing, providerSettings) orgGetter := implorganization.NewGetter(implorganization.NewStore(sqlStore), sharder)
alertmanager, err := signozalertmanager.New(context.TODO(), providerSettings, alertmanager.Config{Signoz: alertmanager.Signoz{PollInterval: 10 * time.Second, Config: alertmanagerserver.NewConfig()}}, sqlStore, orgGetter)
require.NoError(t, err)
jwt := authtypes.NewJWT("", 1*time.Hour, 1*time.Hour)
emailing := emailingtest.New()
modules := signoz.NewModules(sqlStore, jwt, emailing, providerSettings, orgGetter, alertmanager)
handlers := signoz.NewHandlers(modules) handlers := signoz.NewHandlers(modules)
apiHandler, err := app.NewAPIHandler(app.APIHandlerOpts{ apiHandler, err := app.NewAPIHandler(app.APIHandlerOpts{
@ -497,8 +506,7 @@ func NewTestbedWithoutOpamp(t *testing.T, sqlStore sqlstore.SQLStore) *LogPipeli
t.Fatalf("could not create a new ApiHandler: %v", err) t.Fatalf("could not create a new ApiHandler: %v", err)
} }
organizationModule := implorganization.NewModule(implorganization.NewStore(sqlStore)) user, apiErr := createTestUser(modules.OrgSetter, modules.User)
user, apiErr := createTestUser(organizationModule, modules.User)
if apiErr != nil { if apiErr != nil {
t.Fatalf("could not create a test user: %v", apiErr) t.Fatalf("could not create a test user: %v", apiErr)
} }

View File

@ -9,8 +9,12 @@ import (
"testing" "testing"
"time" "time"
"github.com/SigNoz/signoz/pkg/emailing" "github.com/SigNoz/signoz/pkg/alertmanager"
"github.com/SigNoz/signoz/pkg/emailing/noopemailing" "github.com/SigNoz/signoz/pkg/alertmanager/alertmanagerserver"
"github.com/SigNoz/signoz/pkg/alertmanager/signozalertmanager"
"github.com/SigNoz/signoz/pkg/emailing/emailingtest"
"github.com/SigNoz/signoz/pkg/sharder"
"github.com/SigNoz/signoz/pkg/sharder/noopsharder"
"github.com/SigNoz/signoz/pkg/types/authtypes" "github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/http/middleware" "github.com/SigNoz/signoz/pkg/http/middleware"
@ -365,9 +369,14 @@ func NewCloudIntegrationsTestBed(t *testing.T, testDB sqlstore.SQLStore) *CloudI
mockClickhouse.MatchExpectationsInOrder(false) mockClickhouse.MatchExpectationsInOrder(false)
providerSettings := instrumentationtest.New().ToProviderSettings() providerSettings := instrumentationtest.New().ToProviderSettings()
emailing, _ := noopemailing.New(context.Background(), providerSettings, emailing.Config{}) sharder, err := noopsharder.New(context.TODO(), providerSettings, sharder.Config{})
jwt := authtypes.NewJWT("", 10*time.Minute, 30*time.Minute) require.NoError(t, err)
modules := signoz.NewModules(testDB, jwt, emailing, providerSettings) orgGetter := implorganization.NewGetter(implorganization.NewStore(testDB), sharder)
alertmanager, err := signozalertmanager.New(context.TODO(), providerSettings, alertmanager.Config{Signoz: alertmanager.Signoz{PollInterval: 10 * time.Second, Config: alertmanagerserver.NewConfig()}}, testDB, orgGetter)
require.NoError(t, err)
jwt := authtypes.NewJWT("", 1*time.Hour, 1*time.Hour)
emailing := emailingtest.New()
modules := signoz.NewModules(testDB, jwt, emailing, providerSettings, orgGetter, alertmanager)
handlers := signoz.NewHandlers(modules) handlers := signoz.NewHandlers(modules)
apiHandler, err := app.NewAPIHandler(app.APIHandlerOpts{ apiHandler, err := app.NewAPIHandler(app.APIHandlerOpts{
@ -384,13 +393,12 @@ func NewCloudIntegrationsTestBed(t *testing.T, testDB sqlstore.SQLStore) *CloudI
} }
router := app.NewRouter() router := app.NewRouter()
router.Use(middleware.NewAuth(jwt, []string{"Authorization", "Sec-WebSocket-Protocol"}).Wrap) router.Use(middleware.NewAuth(jwt, []string{"Authorization", "Sec-WebSocket-Protocol"}, sharder, instrumentationtest.New().Logger()).Wrap)
am := middleware.NewAuthZ(instrumentationtest.New().Logger()) am := middleware.NewAuthZ(instrumentationtest.New().Logger())
apiHandler.RegisterRoutes(router, am) apiHandler.RegisterRoutes(router, am)
apiHandler.RegisterCloudIntegrationsRoutes(router, am) apiHandler.RegisterCloudIntegrationsRoutes(router, am)
organizationModule := implorganization.NewModule(implorganization.NewStore(testDB)) user, apiErr := createTestUser(modules.OrgSetter, modules.User)
user, apiErr := createTestUser(organizationModule, modules.User)
if apiErr != nil { if apiErr != nil {
t.Fatalf("could not create a test user: %v", apiErr) t.Fatalf("could not create a test user: %v", apiErr)
} }

View File

@ -9,9 +9,10 @@ import (
"testing" "testing"
"time" "time"
"github.com/SigNoz/signoz/pkg/emailing" "github.com/SigNoz/signoz/pkg/alertmanager"
"github.com/SigNoz/signoz/pkg/emailing/noopemailing" "github.com/SigNoz/signoz/pkg/alertmanager/alertmanagerserver"
"github.com/SigNoz/signoz/pkg/alertmanager/signozalertmanager"
"github.com/SigNoz/signoz/pkg/emailing/emailingtest"
"github.com/SigNoz/signoz/pkg/http/middleware" "github.com/SigNoz/signoz/pkg/http/middleware"
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest" "github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization" "github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
@ -22,6 +23,8 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/model" "github.com/SigNoz/signoz/pkg/query-service/model"
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/query-service/utils" "github.com/SigNoz/signoz/pkg/query-service/utils"
"github.com/SigNoz/signoz/pkg/sharder"
"github.com/SigNoz/signoz/pkg/sharder/noopsharder"
"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/types" "github.com/SigNoz/signoz/pkg/types"
@ -571,9 +574,14 @@ func NewIntegrationsTestBed(t *testing.T, testDB sqlstore.SQLStore) *Integration
} }
providerSettings := instrumentationtest.New().ToProviderSettings() providerSettings := instrumentationtest.New().ToProviderSettings()
emailing, _ := noopemailing.New(context.Background(), providerSettings, emailing.Config{}) sharder, err := noopsharder.New(context.TODO(), providerSettings, sharder.Config{})
jwt := authtypes.NewJWT("", 10*time.Minute, 30*time.Minute) require.NoError(t, err)
modules := signoz.NewModules(testDB, jwt, emailing, providerSettings) orgGetter := implorganization.NewGetter(implorganization.NewStore(testDB), sharder)
alertmanager, err := signozalertmanager.New(context.TODO(), providerSettings, alertmanager.Config{Signoz: alertmanager.Signoz{PollInterval: 10 * time.Second, Config: alertmanagerserver.NewConfig()}}, testDB, orgGetter)
require.NoError(t, err)
jwt := authtypes.NewJWT("", 1*time.Hour, 1*time.Hour)
emailing := emailingtest.New()
modules := signoz.NewModules(testDB, jwt, emailing, providerSettings, orgGetter, alertmanager)
handlers := signoz.NewHandlers(modules) handlers := signoz.NewHandlers(modules)
apiHandler, err := app.NewAPIHandler(app.APIHandlerOpts{ apiHandler, err := app.NewAPIHandler(app.APIHandlerOpts{
@ -592,13 +600,12 @@ func NewIntegrationsTestBed(t *testing.T, testDB sqlstore.SQLStore) *Integration
} }
router := app.NewRouter() router := app.NewRouter()
router.Use(middleware.NewAuth(jwt, []string{"Authorization", "Sec-WebSocket-Protocol"}).Wrap) router.Use(middleware.NewAuth(jwt, []string{"Authorization", "Sec-WebSocket-Protocol"}, sharder, instrumentationtest.New().Logger()).Wrap)
am := middleware.NewAuthZ(instrumentationtest.New().Logger()) am := middleware.NewAuthZ(instrumentationtest.New().Logger())
apiHandler.RegisterRoutes(router, am) apiHandler.RegisterRoutes(router, am)
apiHandler.RegisterIntegrationRoutes(router, am) apiHandler.RegisterIntegrationRoutes(router, am)
organizationModule := implorganization.NewModule(implorganization.NewStore(testDB)) user, apiErr := createTestUser(modules.OrgSetter, modules.User)
user, apiErr := createTestUser(organizationModule, modules.User)
if apiErr != nil { if apiErr != nil {
t.Fatalf("could not create a test user: %v", apiErr) t.Fatalf("could not create a test user: %v", apiErr)
} }

View File

@ -147,11 +147,11 @@ func makeTestSignozLog(
return testLog return testLog
} }
func createTestUser(organizationModule organization.Module, userModule user.Module) (*types.User, *model.ApiError) { func createTestUser(orgSetter organization.Setter, userModule user.Module) (*types.User, *model.ApiError) {
// Create a test user for auth // Create a test user for auth
ctx := context.Background() ctx := context.Background()
organization := types.NewOrganization("test") organization := types.NewOrganization("test")
err := organizationModule.Create(ctx, organization) err := orgSetter.Create(ctx, organization)
if err != nil { if err != nil {
return nil, model.InternalError(err) return nil, model.InternalError(err)
} }

View File

@ -88,9 +88,9 @@ func buildSingleFilterCondition(key string, op v3.FilterOperator, fmtVal string,
case v3.FilterOperatorLessThanOrEq: case v3.FilterOperatorLessThanOrEq:
return fmt.Sprintf("%s <= %s", keyCondition, fmtVal), nil return fmt.Sprintf("%s <= %s", keyCondition, fmtVal), nil
case v3.FilterOperatorContains: case v3.FilterOperatorContains:
return fmt.Sprintf("like(%s, %s)", keyCondition, fmtVal), nil return fmt.Sprintf("ilike(%s, %s)", keyCondition, fmtVal), nil
case v3.FilterOperatorNotContains: case v3.FilterOperatorNotContains:
return fmt.Sprintf("notLike(%s, %s)", keyCondition, fmtVal), nil return fmt.Sprintf("notILike(%s, %s)", keyCondition, fmtVal), nil
case v3.FilterOperatorExists: case v3.FilterOperatorExists:
return fmt.Sprintf("has(JSONExtractKeys(labels), '%s')", key), nil return fmt.Sprintf("has(JSONExtractKeys(labels), '%s')", key), nil
case v3.FilterOperatorNotExists: case v3.FilterOperatorNotExists:

View File

@ -67,6 +67,7 @@ func NewTestSqliteDB(t *testing.T) (sqlStore sqlstore.SQLStore, testDBFilePath s
sqlmigration.NewAuthRefactorFactory(sqlStore), sqlmigration.NewAuthRefactorFactory(sqlStore),
sqlmigration.NewMigratePATToFactorAPIKey(sqlStore), sqlmigration.NewMigratePATToFactorAPIKey(sqlStore),
sqlmigration.NewUpdateApiMonitoringFiltersFactory(sqlStore), sqlmigration.NewUpdateApiMonitoringFiltersFactory(sqlStore),
sqlmigration.NewAddKeyOrganizationFactory(sqlStore),
), ),
) )
if err != nil { if err != nil {

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"github.com/SigNoz/signoz/pkg/sqlstore" "github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types"
ruletypes "github.com/SigNoz/signoz/pkg/types/ruletypes" ruletypes "github.com/SigNoz/signoz/pkg/types/ruletypes"
"github.com/SigNoz/signoz/pkg/valuer" "github.com/SigNoz/signoz/pkg/valuer"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
@ -118,27 +117,3 @@ func (r *rule) GetRuleUUID(ctx context.Context, ruleID int) (*ruletypes.RuleHist
} }
return ruleHistory, nil return ruleHistory, nil
} }
func (r *rule) ListOrgs(ctx context.Context) ([]valuer.UUID, error) {
orgIDStrs := make([]string, 0)
err := r.sqlstore.
BunDB().
NewSelect().
Model(new(types.Organization)).
Column("id").
Scan(ctx, &orgIDStrs)
if err != nil {
return nil, err
}
orgIDs := make([]valuer.UUID, len(orgIDStrs))
for idx, orgIDStr := range orgIDStrs {
orgID, err := valuer.NewUUID(orgIDStr)
if err != nil {
return nil, err
}
orgIDs[idx] = orgID
}
return orgIDs, nil
}

32
pkg/sharder/config.go Normal file
View File

@ -0,0 +1,32 @@
package sharder
import (
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/valuer"
)
type Config struct {
Provider string `mapstructure:"provider"`
Single Single `mapstructure:"single"`
}
type Single struct {
OrgID valuer.UUID `mapstructure:"org_id"`
}
func NewConfigFactory() factory.ConfigFactory {
return factory.NewConfigFactory(factory.MustNewName("sharder"), newConfig)
}
func newConfig() factory.Config {
return &Config{
Provider: "noop",
Single: Single{
OrgID: valuer.UUID{},
},
}
}
func (c Config) Validate() error {
return nil
}

View File

@ -0,0 +1,33 @@
package noopsharder
import (
"context"
"math"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/sharder"
)
type provider struct {
settings factory.ScopedProviderSettings
}
func NewFactory() factory.ProviderFactory[sharder.Sharder, sharder.Config] {
return factory.NewProviderFactory(factory.MustNewName("noop"), New)
}
func New(ctx context.Context, providerSettings factory.ProviderSettings, config sharder.Config) (sharder.Sharder, error) {
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/sharder/noopsharder")
return &provider{
settings: settings,
}, nil
}
func (provider *provider) GetMyOwnedKeyRange(ctx context.Context) (uint32, uint32, error) {
return 0, math.MaxUint32, nil
}
func (provider *provider) IsMyOwnedKey(ctx context.Context, key uint32) error {
return nil
}

13
pkg/sharder/sharder.go Normal file
View File

@ -0,0 +1,13 @@
package sharder
import (
"context"
)
type Sharder interface {
// Returns the keys owned by the current instance.
GetMyOwnedKeyRange(context.Context) (uint32, uint32, error)
// Returns true if the key is owned by the current instance.
IsMyOwnedKey(context.Context, uint32) error
}

View File

@ -0,0 +1,43 @@
package singlesharder
import (
"context"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/sharder"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/valuer"
)
type provider struct {
settings factory.ScopedProviderSettings
orgID valuer.UUID
orgIDKey uint32
}
func NewFactory() factory.ProviderFactory[sharder.Sharder, sharder.Config] {
return factory.NewProviderFactory(factory.MustNewName("single"), New)
}
func New(ctx context.Context, providerSettings factory.ProviderSettings, config sharder.Config) (sharder.Sharder, error) {
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/sharder/singlesharder")
return &provider{
settings: settings,
orgID: config.Single.OrgID,
orgIDKey: types.NewOrganizationKey(config.Single.OrgID),
}, nil
}
func (provider *provider) GetMyOwnedKeyRange(ctx context.Context) (uint32, uint32, error) {
return provider.orgIDKey, provider.orgIDKey, nil
}
func (provider *provider) IsMyOwnedKey(ctx context.Context, key uint32) error {
if key == provider.orgIDKey {
return nil
}
return errors.Newf(errors.TypeForbidden, errors.CodeForbidden, "key %d for org %s is not owned by my current instance", key, provider.orgID)
}

View File

@ -17,6 +17,7 @@ import (
"github.com/SigNoz/signoz/pkg/factory" "github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/instrumentation" "github.com/SigNoz/signoz/pkg/instrumentation"
"github.com/SigNoz/signoz/pkg/prometheus" "github.com/SigNoz/signoz/pkg/prometheus"
"github.com/SigNoz/signoz/pkg/sharder"
"github.com/SigNoz/signoz/pkg/sqlmigration" "github.com/SigNoz/signoz/pkg/sqlmigration"
"github.com/SigNoz/signoz/pkg/sqlmigrator" "github.com/SigNoz/signoz/pkg/sqlmigrator"
"github.com/SigNoz/signoz/pkg/sqlstore" "github.com/SigNoz/signoz/pkg/sqlstore"
@ -62,6 +63,9 @@ type Config struct {
// Emailing config // Emailing config
Emailing emailing.Config `mapstructure:"emailing" yaml:"emailing"` Emailing emailing.Config `mapstructure:"emailing" yaml:"emailing"`
// Sharder config
Sharder sharder.Config `mapstructure:"sharder" yaml:"sharder"`
} }
// DeprecatedFlags are the flags that are deprecated and scheduled for removal. // DeprecatedFlags are the flags that are deprecated and scheduled for removal.
@ -86,6 +90,7 @@ func NewConfig(ctx context.Context, resolverConfig config.ResolverConfig, deprec
prometheus.NewConfigFactory(), prometheus.NewConfigFactory(),
alertmanager.NewConfigFactory(), alertmanager.NewConfigFactory(),
emailing.NewConfigFactory(), emailing.NewConfigFactory(),
sharder.NewConfigFactory(),
} }
conf, err := config.New(ctx, resolverConfig, configFactories) conf, err := config.New(ctx, resolverConfig, configFactories)

View File

@ -13,6 +13,8 @@ import (
"github.com/SigNoz/signoz/pkg/modules/quickfilter/implquickfilter" "github.com/SigNoz/signoz/pkg/modules/quickfilter/implquickfilter"
"github.com/SigNoz/signoz/pkg/modules/savedview" "github.com/SigNoz/signoz/pkg/modules/savedview"
"github.com/SigNoz/signoz/pkg/modules/savedview/implsavedview" "github.com/SigNoz/signoz/pkg/modules/savedview/implsavedview"
"github.com/SigNoz/signoz/pkg/modules/tracefunnel"
"github.com/SigNoz/signoz/pkg/modules/tracefunnel/impltracefunnel"
"github.com/SigNoz/signoz/pkg/modules/user" "github.com/SigNoz/signoz/pkg/modules/user"
"github.com/SigNoz/signoz/pkg/modules/user/impluser" "github.com/SigNoz/signoz/pkg/modules/user/impluser"
) )
@ -25,16 +27,18 @@ type Handlers struct {
Apdex apdex.Handler Apdex apdex.Handler
Dashboard dashboard.Handler Dashboard dashboard.Handler
QuickFilter quickfilter.Handler QuickFilter quickfilter.Handler
TraceFunnel tracefunnel.Handler
} }
func NewHandlers(modules Modules) Handlers { func NewHandlers(modules Modules) Handlers {
return Handlers{ return Handlers{
Organization: implorganization.NewHandler(modules.Organization), Organization: implorganization.NewHandler(modules.OrgGetter, modules.OrgSetter),
Preference: implpreference.NewHandler(modules.Preference), Preference: implpreference.NewHandler(modules.Preference),
User: impluser.NewHandler(modules.User), User: impluser.NewHandler(modules.User),
SavedView: implsavedview.NewHandler(modules.SavedView), SavedView: implsavedview.NewHandler(modules.SavedView),
Apdex: implapdex.NewHandler(modules.Apdex), Apdex: implapdex.NewHandler(modules.Apdex),
Dashboard: impldashboard.NewHandler(modules.Dashboard), Dashboard: impldashboard.NewHandler(modules.Dashboard),
QuickFilter: implquickfilter.NewHandler(modules.QuickFilter), QuickFilter: implquickfilter.NewHandler(modules.QuickFilter),
TraceFunnel: impltracefunnel.NewHandler(modules.TraceFunnel),
} }
} }

View File

@ -1,28 +1,40 @@
package signoz package signoz
import ( import (
"context"
"reflect" "reflect"
"testing" "testing"
"time" "time"
"github.com/DATA-DOG/go-sqlmock" "github.com/DATA-DOG/go-sqlmock"
"github.com/SigNoz/signoz/pkg/alertmanager"
"github.com/SigNoz/signoz/pkg/alertmanager/signozalertmanager"
"github.com/SigNoz/signoz/pkg/emailing/emailingtest" "github.com/SigNoz/signoz/pkg/emailing/emailingtest"
"github.com/SigNoz/signoz/pkg/factory/factorytest" "github.com/SigNoz/signoz/pkg/factory/factorytest"
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
"github.com/SigNoz/signoz/pkg/sharder"
"github.com/SigNoz/signoz/pkg/sharder/noopsharder"
"github.com/SigNoz/signoz/pkg/sqlstore" "github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/sqlstore/sqlstoretest" "github.com/SigNoz/signoz/pkg/sqlstore/sqlstoretest"
"github.com/SigNoz/signoz/pkg/types/authtypes" "github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
// This is a test to ensure that all fields of the handlers are initialized. // This is a test to ensure that all fields of the handlers are initialized.
// It also helps us catch these errors at compile time instead of runtime. // It also helps us catch these errors at compile time instead of runtime.
func TestNewHandlers(t *testing.T) { func TestNewHandlers(t *testing.T) {
sqlstore := sqlstoretest.New(sqlstore.Config{Provider: "sqlite"}, sqlmock.QueryMatcherEqual) sqlstore := sqlstoretest.New(sqlstore.Config{Provider: "sqlite"}, sqlmock.QueryMatcherEqual)
providerSettings := factorytest.NewSettings()
sharder, err := noopsharder.New(context.TODO(), providerSettings, sharder.Config{})
require.NoError(t, err)
orgGetter := implorganization.NewGetter(implorganization.NewStore(sqlstore), sharder)
alertmanager, err := signozalertmanager.New(context.TODO(), providerSettings, alertmanager.Config{}, sqlstore, orgGetter)
require.NoError(t, err)
jwt := authtypes.NewJWT("", 1*time.Hour, 1*time.Hour) jwt := authtypes.NewJWT("", 1*time.Hour, 1*time.Hour)
emailing := emailingtest.New() emailing := emailingtest.New()
providerSettings := factorytest.NewSettings() modules := NewModules(sqlstore, jwt, emailing, providerSettings, orgGetter, alertmanager)
modules := NewModules(sqlstore, jwt, emailing, providerSettings)
handlers := NewHandlers(modules) handlers := NewHandlers(modules)
reflectVal := reflect.ValueOf(handlers) reflectVal := reflect.ValueOf(handlers)

View File

@ -1,6 +1,7 @@
package signoz package signoz
import ( import (
"github.com/SigNoz/signoz/pkg/alertmanager"
"github.com/SigNoz/signoz/pkg/emailing" "github.com/SigNoz/signoz/pkg/emailing"
"github.com/SigNoz/signoz/pkg/factory" "github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/modules/apdex" "github.com/SigNoz/signoz/pkg/modules/apdex"
@ -15,6 +16,8 @@ import (
"github.com/SigNoz/signoz/pkg/modules/quickfilter/implquickfilter" "github.com/SigNoz/signoz/pkg/modules/quickfilter/implquickfilter"
"github.com/SigNoz/signoz/pkg/modules/savedview" "github.com/SigNoz/signoz/pkg/modules/savedview"
"github.com/SigNoz/signoz/pkg/modules/savedview/implsavedview" "github.com/SigNoz/signoz/pkg/modules/savedview/implsavedview"
"github.com/SigNoz/signoz/pkg/modules/tracefunnel"
"github.com/SigNoz/signoz/pkg/modules/tracefunnel/impltracefunnel"
"github.com/SigNoz/signoz/pkg/modules/user" "github.com/SigNoz/signoz/pkg/modules/user"
"github.com/SigNoz/signoz/pkg/modules/user/impluser" "github.com/SigNoz/signoz/pkg/modules/user/impluser"
"github.com/SigNoz/signoz/pkg/sqlstore" "github.com/SigNoz/signoz/pkg/sqlstore"
@ -23,23 +26,37 @@ import (
) )
type Modules struct { type Modules struct {
Organization organization.Module OrgGetter organization.Getter
Preference preference.Module OrgSetter organization.Setter
User user.Module Preference preference.Module
SavedView savedview.Module User user.Module
Apdex apdex.Module SavedView savedview.Module
Dashboard dashboard.Module Apdex apdex.Module
QuickFilter quickfilter.Module Dashboard dashboard.Module
QuickFilter quickfilter.Module
TraceFunnel tracefunnel.Module
} }
func NewModules(sqlstore sqlstore.SQLStore, jwt *authtypes.JWT, emailing emailing.Emailing, providerSettings factory.ProviderSettings) Modules { func NewModules(
sqlstore sqlstore.SQLStore,
jwt *authtypes.JWT,
emailing emailing.Emailing,
providerSettings factory.ProviderSettings,
orgGetter organization.Getter,
alertmanager alertmanager.Alertmanager,
) Modules {
quickfilter := implquickfilter.NewModule(implquickfilter.NewStore(sqlstore))
orgSetter := implorganization.NewSetter(implorganization.NewStore(sqlstore), alertmanager, quickfilter)
user := impluser.NewModule(impluser.NewStore(sqlstore, providerSettings), jwt, emailing, providerSettings, orgSetter)
return Modules{ return Modules{
Organization: implorganization.NewModule(implorganization.NewStore(sqlstore)), OrgGetter: orgGetter,
Preference: implpreference.NewModule(implpreference.NewStore(sqlstore), preferencetypes.NewDefaultPreferenceMap()), OrgSetter: orgSetter,
SavedView: implsavedview.NewModule(sqlstore), Preference: implpreference.NewModule(implpreference.NewStore(sqlstore), preferencetypes.NewDefaultPreferenceMap()),
Apdex: implapdex.NewModule(sqlstore), SavedView: implsavedview.NewModule(sqlstore),
Dashboard: impldashboard.NewModule(sqlstore), Apdex: implapdex.NewModule(sqlstore),
User: impluser.NewModule(impluser.NewStore(sqlstore, providerSettings), jwt, emailing, providerSettings), Dashboard: impldashboard.NewModule(sqlstore),
QuickFilter: implquickfilter.NewModule(implquickfilter.NewStore(sqlstore)), User: user,
QuickFilter: quickfilter,
TraceFunnel: impltracefunnel.NewModule(impltracefunnel.NewStore(sqlstore)),
} }
} }

View File

@ -1,27 +1,39 @@
package signoz package signoz
import ( import (
"context"
"reflect" "reflect"
"testing" "testing"
"time" "time"
"github.com/DATA-DOG/go-sqlmock" "github.com/DATA-DOG/go-sqlmock"
"github.com/SigNoz/signoz/pkg/alertmanager"
"github.com/SigNoz/signoz/pkg/alertmanager/signozalertmanager"
"github.com/SigNoz/signoz/pkg/emailing/emailingtest" "github.com/SigNoz/signoz/pkg/emailing/emailingtest"
"github.com/SigNoz/signoz/pkg/factory/factorytest" "github.com/SigNoz/signoz/pkg/factory/factorytest"
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
"github.com/SigNoz/signoz/pkg/sharder"
"github.com/SigNoz/signoz/pkg/sharder/noopsharder"
"github.com/SigNoz/signoz/pkg/sqlstore" "github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/sqlstore/sqlstoretest" "github.com/SigNoz/signoz/pkg/sqlstore/sqlstoretest"
"github.com/SigNoz/signoz/pkg/types/authtypes" "github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
// This is a test to ensure that all fields of the modules are initialized. // This is a test to ensure that all fields of the modules are initialized.
// It also helps us catch these errors at compile time instead of runtime. // It also helps us catch these errors at compile time instead of runtime.
func TestNewModules(t *testing.T) { func TestNewModules(t *testing.T) {
sqlstore := sqlstoretest.New(sqlstore.Config{Provider: "sqlite"}, sqlmock.QueryMatcherEqual) sqlstore := sqlstoretest.New(sqlstore.Config{Provider: "sqlite"}, sqlmock.QueryMatcherEqual)
providerSettings := factorytest.NewSettings()
sharder, err := noopsharder.New(context.TODO(), providerSettings, sharder.Config{})
require.NoError(t, err)
orgGetter := implorganization.NewGetter(implorganization.NewStore(sqlstore), sharder)
alertmanager, err := signozalertmanager.New(context.TODO(), providerSettings, alertmanager.Config{}, sqlstore, orgGetter)
require.NoError(t, err)
jwt := authtypes.NewJWT("", 1*time.Hour, 1*time.Hour) jwt := authtypes.NewJWT("", 1*time.Hour, 1*time.Hour)
emailing := emailingtest.New() emailing := emailingtest.New()
providerSettings := factorytest.NewSettings() modules := NewModules(sqlstore, jwt, emailing, providerSettings, orgGetter, alertmanager)
modules := NewModules(sqlstore, jwt, emailing, providerSettings)
reflectVal := reflect.ValueOf(modules) reflectVal := reflect.ValueOf(modules)
for i := 0; i < reflectVal.NumField(); i++ { for i := 0; i < reflectVal.NumField(); i++ {

View File

@ -11,8 +11,12 @@ import (
"github.com/SigNoz/signoz/pkg/emailing/noopemailing" "github.com/SigNoz/signoz/pkg/emailing/noopemailing"
"github.com/SigNoz/signoz/pkg/emailing/smtpemailing" "github.com/SigNoz/signoz/pkg/emailing/smtpemailing"
"github.com/SigNoz/signoz/pkg/factory" "github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/prometheus" "github.com/SigNoz/signoz/pkg/prometheus"
"github.com/SigNoz/signoz/pkg/prometheus/clickhouseprometheus" "github.com/SigNoz/signoz/pkg/prometheus/clickhouseprometheus"
"github.com/SigNoz/signoz/pkg/sharder"
"github.com/SigNoz/signoz/pkg/sharder/noopsharder"
"github.com/SigNoz/signoz/pkg/sharder/singlesharder"
"github.com/SigNoz/signoz/pkg/sqlmigration" "github.com/SigNoz/signoz/pkg/sqlmigration"
"github.com/SigNoz/signoz/pkg/sqlstore" "github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/sqlstore/sqlitesqlstore" "github.com/SigNoz/signoz/pkg/sqlstore/sqlitesqlstore"
@ -83,6 +87,8 @@ func NewSQLMigrationProviderFactories(sqlstore sqlstore.SQLStore) factory.NamedM
sqlmigration.NewUpdateLicenseFactory(sqlstore), sqlmigration.NewUpdateLicenseFactory(sqlstore),
sqlmigration.NewMigratePATToFactorAPIKey(sqlstore), sqlmigration.NewMigratePATToFactorAPIKey(sqlstore),
sqlmigration.NewUpdateApiMonitoringFiltersFactory(sqlstore), sqlmigration.NewUpdateApiMonitoringFiltersFactory(sqlstore),
sqlmigration.NewAddKeyOrganizationFactory(sqlstore),
sqlmigration.NewAddTraceFunnelsFactory(sqlstore),
) )
} }
@ -98,10 +104,10 @@ func NewPrometheusProviderFactories(telemetryStore telemetrystore.TelemetryStore
) )
} }
func NewAlertmanagerProviderFactories(sqlstore sqlstore.SQLStore) factory.NamedMap[factory.ProviderFactory[alertmanager.Alertmanager, alertmanager.Config]] { func NewAlertmanagerProviderFactories(sqlstore sqlstore.SQLStore, orgGetter organization.Getter) factory.NamedMap[factory.ProviderFactory[alertmanager.Alertmanager, alertmanager.Config]] {
return factory.MustNewNamedMap( return factory.MustNewNamedMap(
legacyalertmanager.NewFactory(sqlstore), legacyalertmanager.NewFactory(sqlstore, orgGetter),
signozalertmanager.NewFactory(sqlstore), signozalertmanager.NewFactory(sqlstore, orgGetter),
) )
} }
@ -111,3 +117,10 @@ func NewEmailingProviderFactories() factory.NamedMap[factory.ProviderFactory[ema
smtpemailing.NewFactory(), smtpemailing.NewFactory(),
) )
} }
func NewSharderProviderFactories() factory.NamedMap[factory.ProviderFactory[sharder.Sharder, sharder.Config]] {
return factory.MustNewNamedMap(
singlesharder.NewFactory(),
noopsharder.NewFactory(),
)
}

View File

@ -4,6 +4,7 @@ import (
"testing" "testing"
"github.com/DATA-DOG/go-sqlmock" "github.com/DATA-DOG/go-sqlmock"
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
"github.com/SigNoz/signoz/pkg/sqlstore" "github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/sqlstore/sqlstoretest" "github.com/SigNoz/signoz/pkg/sqlstore/sqlstoretest"
"github.com/SigNoz/signoz/pkg/telemetrystore" "github.com/SigNoz/signoz/pkg/telemetrystore"
@ -40,10 +41,15 @@ func TestNewProviderFactories(t *testing.T) {
}) })
assert.NotPanics(t, func() { assert.NotPanics(t, func() {
NewAlertmanagerProviderFactories(sqlstoretest.New(sqlstore.Config{Provider: "sqlite"}, sqlmock.QueryMatcherEqual)) orgGetter := implorganization.NewGetter(implorganization.NewStore(sqlstoretest.New(sqlstore.Config{Provider: "sqlite"}, sqlmock.QueryMatcherEqual)), nil)
NewAlertmanagerProviderFactories(sqlstoretest.New(sqlstore.Config{Provider: "sqlite"}, sqlmock.QueryMatcherEqual), orgGetter)
}) })
assert.NotPanics(t, func() { assert.NotPanics(t, func() {
NewEmailingProviderFactories() NewEmailingProviderFactories()
}) })
assert.NotPanics(t, func() {
NewSharderProviderFactories()
})
} }

View File

@ -9,7 +9,10 @@ import (
"github.com/SigNoz/signoz/pkg/factory" "github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/instrumentation" "github.com/SigNoz/signoz/pkg/instrumentation"
"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/implorganization"
"github.com/SigNoz/signoz/pkg/prometheus" "github.com/SigNoz/signoz/pkg/prometheus"
"github.com/SigNoz/signoz/pkg/sharder"
"github.com/SigNoz/signoz/pkg/sqlmigration" "github.com/SigNoz/signoz/pkg/sqlmigration"
"github.com/SigNoz/signoz/pkg/sqlmigrator" "github.com/SigNoz/signoz/pkg/sqlmigrator"
"github.com/SigNoz/signoz/pkg/sqlstore" "github.com/SigNoz/signoz/pkg/sqlstore"
@ -33,6 +36,7 @@ type SigNoz struct {
Zeus zeus.Zeus Zeus zeus.Zeus
Licensing licensing.Licensing Licensing licensing.Licensing
Emailing emailing.Emailing Emailing emailing.Emailing
Sharder sharder.Sharder
Modules Modules Modules Modules
Handlers Handlers Handlers Handlers
} }
@ -44,7 +48,7 @@ func New(
zeusConfig zeus.Config, zeusConfig zeus.Config,
zeusProviderFactory factory.ProviderFactory[zeus.Zeus, zeus.Config], zeusProviderFactory factory.ProviderFactory[zeus.Zeus, zeus.Config],
licenseConfig licensing.Config, licenseConfig licensing.Config,
licenseProviderFactoryCb func(sqlstore.SQLStore, zeus.Zeus) factory.ProviderFactory[licensing.Licensing, licensing.Config], licenseProviderFactoryCb func(sqlstore.SQLStore, zeus.Zeus, organization.Getter) factory.ProviderFactory[licensing.Licensing, licensing.Config],
emailingProviderFactories factory.NamedMap[factory.ProviderFactory[emailing.Emailing, emailing.Config]], emailingProviderFactories factory.NamedMap[factory.ProviderFactory[emailing.Emailing, emailing.Config]],
cacheProviderFactories factory.NamedMap[factory.ProviderFactory[cache.Cache, cache.Config]], cacheProviderFactories factory.NamedMap[factory.ProviderFactory[cache.Cache, cache.Config]],
webProviderFactories factory.NamedMap[factory.ProviderFactory[web.Web, web.Config]], webProviderFactories factory.NamedMap[factory.ProviderFactory[web.Web, web.Config]],
@ -162,19 +166,34 @@ func New(
return nil, err return nil, err
} }
// Initialize sharder from the available sharder provider factories
sharder, err := factory.NewProviderFromNamedMap(
ctx,
providerSettings,
config.Sharder,
NewSharderProviderFactories(),
config.Sharder.Provider,
)
if err != nil {
return nil, err
}
// Initialize organization getter
orgGetter := implorganization.NewGetter(implorganization.NewStore(sqlstore), sharder)
// Initialize alertmanager from the available alertmanager provider factories // Initialize alertmanager from the available alertmanager provider factories
alertmanager, err := factory.NewProviderFromNamedMap( alertmanager, err := factory.NewProviderFromNamedMap(
ctx, ctx,
providerSettings, providerSettings,
config.Alertmanager, config.Alertmanager,
NewAlertmanagerProviderFactories(sqlstore), NewAlertmanagerProviderFactories(sqlstore, orgGetter),
config.Alertmanager.Provider, config.Alertmanager.Provider,
) )
if err != nil { if err != nil {
return nil, err return nil, err
} }
licensingProviderFactory := licenseProviderFactoryCb(sqlstore, zeus) licensingProviderFactory := licenseProviderFactoryCb(sqlstore, zeus, orgGetter)
licensing, err := licensingProviderFactory.New( licensing, err := licensingProviderFactory.New(
ctx, ctx,
providerSettings, providerSettings,
@ -185,7 +204,7 @@ func New(
} }
// Initialize all modules // Initialize all modules
modules := NewModules(sqlstore, jwt, emailing, providerSettings) modules := NewModules(sqlstore, jwt, emailing, providerSettings, orgGetter, alertmanager)
// Initialize all handlers for the modules // Initialize all handlers for the modules
handlers := NewHandlers(modules) handlers := NewHandlers(modules)
@ -212,6 +231,7 @@ func New(
Zeus: zeus, Zeus: zeus,
Licensing: licensing, Licensing: licensing,
Emailing: emailing, Emailing: emailing,
Sharder: sharder,
Modules: modules, Modules: modules,
Handlers: handlers, Handlers: handlers,
}, nil }, nil

View File

@ -0,0 +1,112 @@
package sqlmigration
import (
"context"
"hash/fnv"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/uptrace/bun"
"github.com/uptrace/bun/migrate"
)
type addKeyOrganization struct {
sqlstore sqlstore.SQLStore
}
func NewAddKeyOrganizationFactory(sqlstore sqlstore.SQLStore) factory.ProviderFactory[SQLMigration, Config] {
return factory.NewProviderFactory(factory.MustNewName("add_key_organization"), func(ctx context.Context, providerSettings factory.ProviderSettings, config Config) (SQLMigration, error) {
return newAddKeyOrganization(ctx, providerSettings, config, sqlstore)
})
}
func newAddKeyOrganization(_ context.Context, _ factory.ProviderSettings, _ Config, sqlstore sqlstore.SQLStore) (SQLMigration, error) {
return &addKeyOrganization{
sqlstore: sqlstore,
}, nil
}
func (migration *addKeyOrganization) Register(migrations *migrate.Migrations) error {
if err := migrations.Register(migration.Up, migration.Down); err != nil {
return err
}
return nil
}
func (migration *addKeyOrganization) Up(ctx context.Context, db *bun.DB) error {
ok, err := migration.sqlstore.Dialect().ColumnExists(ctx, db, "organizations", "key")
if err != nil {
return err
}
if ok {
return nil
}
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer func() {
_ = tx.Rollback()
}()
if _, err := tx.
NewAddColumn().
Table("organizations").
ColumnExpr("key BIGINT").
Exec(ctx); err != nil {
return err
}
var existingOrgIDs []string
if err := tx.NewSelect().
Table("organizations").
Column("id").
Scan(ctx, &existingOrgIDs); err != nil {
return err
}
for _, orgID := range existingOrgIDs {
key := migration.getHash(ctx, orgID)
if _, err := tx.
NewUpdate().
Table("organizations").
Set("key = ?", key).
Where("id = ?", orgID).
Exec(ctx); err != nil {
return err
}
}
if _, err := tx.
NewCreateIndex().
Unique().
IfNotExists().
Index("idx_unique_key").
Table("organizations").
Column("key").
Exec(ctx); err != nil {
return err
}
if err := tx.Commit(); err != nil {
return err
}
return nil
}
func (migration *addKeyOrganization) Down(ctx context.Context, db *bun.DB) error {
return nil
}
func (migration *addKeyOrganization) getHash(_ context.Context, orgID string) uint32 {
hasher := fnv.New32a()
// Hasher never returns err.
_, _ = hasher.Write([]byte(orgID))
return hasher.Sum32()
}

View File

@ -0,0 +1,89 @@
package sqlmigration
import (
"context"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/uptrace/bun"
"github.com/uptrace/bun/migrate"
)
// funnel Core Data Structure (funnel and funnelStep)
type funnel struct {
bun.BaseModel `bun:"table:trace_funnel"`
types.Identifiable // funnel id
types.TimeAuditable
types.UserAuditable
Name string `json:"funnel_name" bun:"name,type:text,notnull"` // funnel name
Description string `json:"description" bun:"description,type:text"` // funnel description
OrgID valuer.UUID `json:"org_id" bun:"org_id,type:varchar,notnull"`
Steps []funnelStep `json:"steps" bun:"steps,type:text,notnull"`
Tags string `json:"tags" bun:"tags,type:text"`
CreatedByUser *types.User `json:"user" bun:"rel:belongs-to,join:created_by=id"`
}
type funnelStep struct {
types.Identifiable
Name string `json:"name,omitempty"` // step name
Description string `json:"description,omitempty"` // step description
Order int64 `json:"step_order"`
ServiceName string `json:"service_name"`
SpanName string `json:"span_name"`
Filters string `json:"filters,omitempty"`
LatencyPointer string `json:"latency_pointer,omitempty"`
LatencyType string `json:"latency_type,omitempty"`
HasErrors bool `json:"has_errors"`
}
type addTraceFunnels struct {
sqlstore sqlstore.SQLStore
}
func NewAddTraceFunnelsFactory(sqlstore sqlstore.SQLStore) factory.ProviderFactory[SQLMigration, Config] {
return factory.NewProviderFactory(factory.MustNewName("add_trace_funnels"), func(ctx context.Context, providerSettings factory.ProviderSettings, config Config) (SQLMigration, error) {
return newAddTraceFunnels(ctx, providerSettings, config, sqlstore)
})
}
func newAddTraceFunnels(_ context.Context, _ factory.ProviderSettings, _ Config, sqlstore sqlstore.SQLStore) (SQLMigration, error) {
return &addTraceFunnels{sqlstore: sqlstore}, nil
}
func (migration *addTraceFunnels) Register(migrations *migrate.Migrations) error {
if err := migrations.Register(migration.Up, migration.Down); err != nil {
return err
}
return nil
}
func (migration *addTraceFunnels) Up(ctx context.Context, db *bun.DB) error {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer func() {
_ = tx.Rollback()
}()
_, err = tx.NewCreateTable().
Model(new(funnel)).
ForeignKey(`("org_id") REFERENCES "organizations" ("id") ON DELETE CASCADE`).
IfNotExists().
Exec(ctx)
if err != nil {
return err
}
err = tx.Commit()
if err != nil {
return err
}
return nil
}
func (migration *addTraceFunnels) Down(ctx context.Context, db *bun.DB) error {
return nil
}

View File

@ -369,9 +369,6 @@ type ConfigStore interface {
// Get returns the config for the given orgID // Get returns the config for the given orgID
Get(context.Context, string) (*Config, error) Get(context.Context, string) (*Config, error)
// ListOrgs returns the list of orgs
ListOrgs(context.Context) ([]string, error)
// CreateChannel creates a new channel. // CreateChannel creates a new channel.
CreateChannel(context.Context, *Channel, ...StoreOption) error CreateChannel(context.Context, *Channel, ...StoreOption) error

View File

@ -0,0 +1,15 @@
package apdextypes
import (
"github.com/SigNoz/signoz/pkg/types"
"github.com/uptrace/bun"
)
type Settings struct {
bun.BaseModel `bun:"table:apdex_setting"`
types.Identifiable
OrgID string `bun:"org_id,type:text" json:"orgId"`
ServiceName string `bun:"service_name,type:text" json:"serviceName"`
Threshold float64 `bun:"threshold,type:float,notnull" json:"threshold"`
ExcludeStatusCodes string `bun:"exclude_status_codes,type:text,notnull" json:"excludeStatusCodes"`
}

View File

@ -87,9 +87,6 @@ func GetActiveLicenseFromStorableLicenses(storableLicenses []*StorableLicense, o
return nil, err return nil, err
} }
if license.Status == "INVALID" {
continue
}
if activeLicense == nil && if activeLicense == nil &&
(license.ValidFrom != 0) && (license.ValidFrom != 0) &&
(license.ValidUntil == -1 || license.ValidUntil > time.Now().Unix()) { (license.ValidUntil == -1 || license.ValidUntil > time.Now().Unix()) {
@ -383,7 +380,4 @@ type Store interface {
GetFeature(context.Context, string) (*featuretypes.StorableFeature, error) GetFeature(context.Context, string) (*featuretypes.StorableFeature, error)
GetAllFeatures(context.Context) ([]*featuretypes.StorableFeature, error) GetAllFeatures(context.Context) ([]*featuretypes.StorableFeature, error)
UpdateFeature(context.Context, *featuretypes.StorableFeature) error UpdateFeature(context.Context, *featuretypes.StorableFeature) error
// ListOrganizations returns the list of orgs
ListOrganizations(context.Context) ([]valuer.UUID, error)
} }

View File

@ -2,6 +2,7 @@ package types
import ( import (
"context" "context"
"hash/fnv"
"time" "time"
"github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/errors"
@ -20,13 +21,15 @@ type Organization struct {
Identifiable Identifiable
Name string `bun:"name,type:text,nullzero" json:"name"` Name string `bun:"name,type:text,nullzero" json:"name"`
Alias string `bun:"alias,type:text,nullzero" json:"alias"` Alias string `bun:"alias,type:text,nullzero" json:"alias"`
Key uint32 `bun:"key,type:bigint,notnull" json:"key"`
DisplayName string `bun:"display_name,type:text,notnull" json:"displayName"` DisplayName string `bun:"display_name,type:text,notnull" json:"displayName"`
} }
func NewOrganization(displayName string) *Organization { func NewOrganization(displayName string) *Organization {
id := valuer.GenerateUUID()
return &Organization{ return &Organization{
Identifiable: Identifiable{ Identifiable: Identifiable{
ID: valuer.GenerateUUID(), ID: id,
}, },
TimeAuditable: TimeAuditable{ TimeAuditable: TimeAuditable{
CreatedAt: time.Now(), CreatedAt: time.Now(),
@ -34,22 +37,23 @@ func NewOrganization(displayName string) *Organization {
}, },
// Name: "default/main", TODO: take the call and uncomment this later // Name: "default/main", TODO: take the call and uncomment this later
DisplayName: displayName, DisplayName: displayName,
Key: NewOrganizationKey(id),
} }
} }
type ApdexSettings struct { func NewOrganizationKey(orgID valuer.UUID) uint32 {
bun.BaseModel `bun:"table:apdex_setting"` hasher := fnv.New32a()
Identifiable
OrgID string `bun:"org_id,type:text" json:"orgId"` // Hasher never returns err.
ServiceName string `bun:"service_name,type:text" json:"serviceName"` _, _ = hasher.Write([]byte(orgID.String()))
Threshold float64 `bun:"threshold,type:float,notnull" json:"threshold"` return hasher.Sum32()
ExcludeStatusCodes string `bun:"exclude_status_codes,type:text,notnull" json:"excludeStatusCodes"`
} }
type OrganizationStore interface { type OrganizationStore interface {
Create(context.Context, *Organization) error Create(context.Context, *Organization) error
Get(context.Context, valuer.UUID) (*Organization, error) Get(context.Context, valuer.UUID) (*Organization, error)
GetAll(context.Context) ([]*Organization, error) GetAll(context.Context) ([]*Organization, error)
ListByKeyRange(context.Context, uint32, uint32) ([]*Organization, error)
Update(context.Context, *Organization) error Update(context.Context, *Organization) error
Delete(context.Context, valuer.UUID) error Delete(context.Context, valuer.UUID) error
} }

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