chore(savedview|apdex|dashboard): create modules and handlers (#7960)

* chore(savedview): refactor into module and handler

* chore(rule): move telemetry inside telemetry

* chore(apdex): refactor apdex and delete dao

* chore(dashboard): create a dashboard module

* chore(ee): get rid of the init nonesense

* chore(dashboard): fix err and apierror confusion

* chore: address comments
This commit is contained in:
Vibhu Pandey 2025-05-17 00:15:00 +05:30 committed by GitHub
parent 6821efeb99
commit da084b4686
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
44 changed files with 1318 additions and 1335 deletions

View File

@ -66,7 +66,6 @@ func NewAPIHandler(opts APIHandlerOptions, signoz *signoz.SigNoz) (*APIHandler,
baseHandler, err := baseapp.NewAPIHandler(baseapp.APIHandlerOpts{
Reader: opts.DataConnector,
PreferSpanMetrics: opts.PreferSpanMetrics,
AppDao: opts.AppDao,
RuleManager: opts.RulesManager,
FeatureFlags: opts.FeatureFlags,
IntegrationsController: opts.IntegrationsController,

View File

@ -6,7 +6,6 @@ import (
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/http/render"
"github.com/SigNoz/signoz/pkg/query-service/app/dashboards"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/gorilla/mux"
)
@ -41,9 +40,9 @@ func (ah *APIHandler) lockUnlockDashboard(w http.ResponseWriter, r *http.Request
return
}
dashboard, apiErr := dashboards.GetDashboard(r.Context(), claims.OrgID, uuid)
if apiErr != nil {
render.Error(w, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to get dashboard"))
dashboard, err := ah.Signoz.Modules.Dashboard.Get(r.Context(), claims.OrgID, uuid)
if err != nil {
render.Error(w, err)
return
}
@ -53,9 +52,9 @@ func (ah *APIHandler) lockUnlockDashboard(w http.ResponseWriter, r *http.Request
}
// Lock/Unlock the dashboard
apiErr = dashboards.LockUnlockDashboard(r.Context(), claims.OrgID, uuid, lock)
if apiErr != nil {
render.Error(w, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to lock/unlock dashboard"))
err = ah.Signoz.Modules.Dashboard.LockUnlock(r.Context(), claims.OrgID, uuid, lock)
if err != nil {
render.Error(w, err)
return
}

View File

@ -15,7 +15,7 @@ import (
"github.com/SigNoz/signoz/ee/query-service/app/api"
"github.com/SigNoz/signoz/ee/query-service/app/db"
"github.com/SigNoz/signoz/ee/query-service/constants"
"github.com/SigNoz/signoz/ee/query-service/dao"
"github.com/SigNoz/signoz/ee/query-service/dao/sqlite"
"github.com/SigNoz/signoz/ee/query-service/integrations/gateway"
"github.com/SigNoz/signoz/ee/query-service/rules"
"github.com/SigNoz/signoz/pkg/alertmanager"
@ -36,8 +36,6 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/agentConf"
baseapp "github.com/SigNoz/signoz/pkg/query-service/app"
"github.com/SigNoz/signoz/pkg/query-service/app/cloudintegrations"
"github.com/SigNoz/signoz/pkg/query-service/app/dashboards"
baseexplorer "github.com/SigNoz/signoz/pkg/query-service/app/explorer"
"github.com/SigNoz/signoz/pkg/query-service/app/integrations"
"github.com/SigNoz/signoz/pkg/query-service/app/logparsingpipeline"
"github.com/SigNoz/signoz/pkg/query-service/app/opamp"
@ -92,19 +90,7 @@ func (s Server) HealthCheckStatus() chan healthcheck.Status {
// NewServer creates and initializes Server
func NewServer(serverOptions *ServerOptions) (*Server, error) {
modelDao, err := dao.InitDao(serverOptions.SigNoz.SQLStore)
if err != nil {
return nil, err
}
if err := baseexplorer.InitWithDSN(serverOptions.SigNoz.SQLStore); err != nil {
return nil, err
}
if err := dashboards.InitDB(serverOptions.SigNoz.SQLStore); err != nil {
return nil, err
}
modelDao := sqlite.NewModelDao(serverOptions.SigNoz.SQLStore)
gatewayProxy, err := gateway.NewProxy(serverOptions.GatewayUrl, gateway.RoutePrefix)
if err != nil {
return nil, err
@ -116,9 +102,6 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
return nil, err
}
// set license manager as feature flag provider in dao
modelDao.SetFlagProvider(lm)
fluxIntervalForTraceDetail, err := time.ParseDuration(serverOptions.FluxIntervalForTraceDetail)
if err != nil {
return nil, err
@ -197,6 +180,11 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
telemetry.GetInstance().SetReader(reader)
telemetry.GetInstance().SetSqlStore(serverOptions.SigNoz.SQLStore)
telemetry.GetInstance().SetSaasOperator(constants.SaasSegmentKey)
telemetry.GetInstance().SetSavedViewsInfoCallback(telemetry.GetSavedViewsInfo)
telemetry.GetInstance().SetAlertsInfoCallback(telemetry.GetAlertsInfo)
telemetry.GetInstance().SetGetUsersCallback(telemetry.GetUsers)
telemetry.GetInstance().SetUserCountCallback(telemetry.GetUserCount)
telemetry.GetInstance().SetDashboardsInfoCallback(telemetry.GetDashboardsInfo)
fluxInterval, err := time.ParseDuration(serverOptions.FluxInterval)
if err != nil {

View File

@ -1,10 +0,0 @@
package dao
import (
"github.com/SigNoz/signoz/ee/query-service/dao/sqlite"
"github.com/SigNoz/signoz/pkg/sqlstore"
)
func InitDao(sqlStore sqlstore.SQLStore) (ModelDao, error) {
return sqlite.InitDB(sqlStore)
}

View File

@ -5,23 +5,13 @@ import (
"net/url"
eeTypes "github.com/SigNoz/signoz/ee/types"
basedao "github.com/SigNoz/signoz/pkg/query-service/dao"
baseint "github.com/SigNoz/signoz/pkg/query-service/interfaces"
basemodel "github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/google/uuid"
"github.com/uptrace/bun"
)
type ModelDao interface {
basedao.ModelDao
// SetFlagProvider sets the feature lookup provider
SetFlagProvider(flags baseint.FeatureLookup)
DB() *bun.DB
// auth methods
GetDomainFromSsoResponse(ctx context.Context, relayState *url.URL) (*types.GettableOrgDomain, error)

View File

@ -70,7 +70,7 @@ func (m *modelDao) GetDomainFromSsoResponse(ctx context.Context, relayState *url
func (m *modelDao) GetDomainByName(ctx context.Context, name string) (*types.GettableOrgDomain, basemodel.BaseApiError) {
stored := types.StorableOrgDomain{}
err := m.DB().NewSelect().
err := m.sqlStore.BunDB().NewSelect().
Model(&stored).
Where("name = ?", name).
Limit(1).
@ -94,7 +94,7 @@ func (m *modelDao) GetDomainByName(ctx context.Context, name string) (*types.Get
func (m *modelDao) GetDomain(ctx context.Context, id uuid.UUID) (*types.GettableOrgDomain, basemodel.BaseApiError) {
stored := types.StorableOrgDomain{}
err := m.DB().NewSelect().
err := m.sqlStore.BunDB().NewSelect().
Model(&stored).
Where("id = ?", id).
Limit(1).
@ -119,7 +119,7 @@ func (m *modelDao) ListDomains(ctx context.Context, orgId string) ([]types.Getta
domains := []types.GettableOrgDomain{}
stored := []types.StorableOrgDomain{}
err := m.DB().NewSelect().
err := m.sqlStore.BunDB().NewSelect().
Model(&stored).
Where("org_id = ?", orgId).
Scan(ctx)
@ -167,7 +167,7 @@ func (m *modelDao) CreateDomain(ctx context.Context, domain *types.GettableOrgDo
TimeAuditable: ossTypes.TimeAuditable{CreatedAt: time.Now(), UpdatedAt: time.Now()},
}
_, err = m.DB().NewInsert().
_, err = m.sqlStore.BunDB().NewInsert().
Model(&storableDomain).
Exec(ctx)
@ -201,7 +201,7 @@ func (m *modelDao) UpdateDomain(ctx context.Context, domain *types.GettableOrgDo
TimeAuditable: ossTypes.TimeAuditable{UpdatedAt: time.Now()},
}
_, err = m.DB().NewUpdate().
_, err = m.sqlStore.BunDB().NewUpdate().
Model(storableDomain).
Column("data", "updated_at").
WherePK().
@ -224,7 +224,7 @@ func (m *modelDao) DeleteDomain(ctx context.Context, id uuid.UUID) basemodel.Bas
}
storableDomain := &types.StorableOrgDomain{ID: id}
_, err := m.DB().NewDelete().
_, err := m.sqlStore.BunDB().NewDelete().
Model(storableDomain).
WherePK().
Exec(ctx)
@ -251,7 +251,7 @@ func (m *modelDao) GetDomainByEmail(ctx context.Context, email string) (*types.G
parsedDomain := components[1]
stored := types.StorableOrgDomain{}
err := m.DB().NewSelect().
err := m.sqlStore.BunDB().NewSelect().
Model(&stored).
Where("name = ?", parsedDomain).
Limit(1).

View File

@ -1,50 +1,18 @@
package sqlite
import (
"fmt"
"github.com/SigNoz/signoz/pkg/modules/user"
"github.com/SigNoz/signoz/pkg/modules/user/impluser"
basedao "github.com/SigNoz/signoz/pkg/query-service/dao"
basedsql "github.com/SigNoz/signoz/pkg/query-service/dao/sqlite"
baseint "github.com/SigNoz/signoz/pkg/query-service/interfaces"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/uptrace/bun"
)
type modelDao struct {
*basedsql.ModelDaoSqlite
flags baseint.FeatureLookup
userModule user.Module
}
// SetFlagProvider sets the feature lookup provider
func (m *modelDao) SetFlagProvider(flags baseint.FeatureLookup) {
m.flags = flags
}
// CheckFeature confirms if a feature is available
func (m *modelDao) checkFeature(key string) error {
if m.flags == nil {
return fmt.Errorf("flag provider not set")
}
return m.flags.CheckFeature(key)
sqlStore sqlstore.SQLStore
}
// InitDB creates and extends base model DB repository
func InitDB(sqlStore sqlstore.SQLStore) (*modelDao, error) {
dao, err := basedsql.InitDB(sqlStore)
if err != nil {
return nil, err
}
// set package variable so dependent base methods (e.g. AuthCache) will work
basedao.SetDB(dao)
func NewModelDao(sqlStore sqlstore.SQLStore) *modelDao {
userModule := impluser.NewModule(impluser.NewStore(sqlStore))
m := &modelDao{ModelDaoSqlite: dao, userModule: userModule}
return m, nil
}
func (m *modelDao) DB() *bun.DB {
return m.ModelDaoSqlite.DB()
return &modelDao{userModule: userModule, sqlStore: sqlStore}
}

View File

@ -17,7 +17,7 @@ import (
func (m *modelDao) CreatePAT(ctx context.Context, orgID string, p types.GettablePAT) (types.GettablePAT, basemodel.BaseApiError) {
p.StorablePersonalAccessToken.OrgID = orgID
p.StorablePersonalAccessToken.ID = valuer.GenerateUUID()
_, err := m.DB().NewInsert().
_, err := m.sqlStore.BunDB().NewInsert().
Model(&p.StorablePersonalAccessToken).
Exec(ctx)
if err != nil {
@ -50,7 +50,7 @@ func (m *modelDao) CreatePAT(ctx context.Context, orgID string, p types.Gettable
}
func (m *modelDao) UpdatePAT(ctx context.Context, orgID string, p types.GettablePAT, id valuer.UUID) basemodel.BaseApiError {
_, err := m.DB().NewUpdate().
_, err := m.sqlStore.BunDB().NewUpdate().
Model(&p.StorablePersonalAccessToken).
Column("role", "name", "updated_at", "updated_by_user_id").
Where("id = ?", id.StringValue()).
@ -67,7 +67,7 @@ func (m *modelDao) UpdatePAT(ctx context.Context, orgID string, p types.Gettable
func (m *modelDao) ListPATs(ctx context.Context, orgID string) ([]types.GettablePAT, basemodel.BaseApiError) {
pats := []types.StorablePersonalAccessToken{}
if err := m.DB().NewSelect().
if err := m.sqlStore.BunDB().NewSelect().
Model(&pats).
Where("revoked = false").
Where("org_id = ?", orgID).
@ -134,7 +134,7 @@ func (m *modelDao) ListPATs(ctx context.Context, orgID string) ([]types.Gettable
func (m *modelDao) RevokePAT(ctx context.Context, orgID string, id valuer.UUID, userID string) basemodel.BaseApiError {
updatedAt := time.Now().Unix()
_, err := m.DB().NewUpdate().
_, err := m.sqlStore.BunDB().NewUpdate().
Model(&types.StorablePersonalAccessToken{}).
Set("revoked = ?", true).
Set("updated_by_user_id = ?", userID).
@ -152,7 +152,7 @@ func (m *modelDao) RevokePAT(ctx context.Context, orgID string, id valuer.UUID,
func (m *modelDao) GetPAT(ctx context.Context, token string) (*types.GettablePAT, basemodel.BaseApiError) {
pats := []types.StorablePersonalAccessToken{}
if err := m.DB().NewSelect().
if err := m.sqlStore.BunDB().NewSelect().
Model(&pats).
Where("token = ?", token).
Where("revoked = false").
@ -177,7 +177,7 @@ func (m *modelDao) GetPAT(ctx context.Context, token string) (*types.GettablePAT
func (m *modelDao) GetPATByID(ctx context.Context, orgID string, id valuer.UUID) (*types.GettablePAT, basemodel.BaseApiError) {
pats := []types.StorablePersonalAccessToken{}
if err := m.DB().NewSelect().
if err := m.sqlStore.BunDB().NewSelect().
Model(&pats).
Where("id = ?", id.StringValue()).
Where("org_id = ?", orgID).

View File

@ -0,0 +1,20 @@
package apdex
import (
"context"
"net/http"
"github.com/SigNoz/signoz/pkg/types"
)
type Module interface {
Get(context.Context, string, []string) ([]*types.ApdexSettings, error)
Set(context.Context, string, *types.ApdexSettings) error
}
type Handler interface {
Get(http.ResponseWriter, *http.Request)
Set(http.ResponseWriter, *http.Request)
}

View File

@ -0,0 +1,66 @@
package implapdex
import (
"context"
"encoding/json"
"net/http"
"strings"
"time"
"github.com/SigNoz/signoz/pkg/http/render"
"github.com/SigNoz/signoz/pkg/modules/apdex"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/authtypes"
)
type handler struct {
module apdex.Module
}
func NewHandler(module apdex.Module) apdex.Handler {
return &handler{module: module}
}
func (handler *handler) Set(rw http.ResponseWriter, req *http.Request) {
ctx, cancel := context.WithTimeout(req.Context(), 10*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(rw, err)
return
}
var apdexSettings types.ApdexSettings
if err := json.NewDecoder(req.Body).Decode(&apdexSettings); err != nil {
render.Error(rw, err)
return
}
if err := handler.module.Set(ctx, claims.OrgID, &apdexSettings); err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusOK, map[string]string{"data": "apdex score updated successfully"})
}
func (handler *handler) Get(rw http.ResponseWriter, req *http.Request) {
ctx, cancel := context.WithTimeout(req.Context(), 10*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(req.Context())
if err != nil {
render.Error(rw, err)
return
}
services := req.URL.Query().Get("services")
apdexSettings, err := handler.module.Get(ctx, claims.OrgID, strings.Split(strings.TrimSpace(services), ","))
if err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusOK, apdexSettings)
}

View File

@ -1,28 +1,43 @@
package sqlite
package implapdex
import (
"context"
"github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/modules/apdex"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/uptrace/bun"
)
const defaultApdexThreshold = 0.5
const (
defaultApdexThreshold float64 = 0.5
)
func (mds *ModelDaoSqlite) GetApdexSettings(ctx context.Context, orgID string, services []string) ([]types.ApdexSettings, *model.ApiError) {
var apdexSettings []types.ApdexSettings
type module struct {
sqlstore sqlstore.SQLStore
}
err := mds.bundb.NewSelect().
func NewModule(sqlstore sqlstore.SQLStore) apdex.Module {
return &module{
sqlstore: sqlstore,
}
}
func (module *module) Get(ctx context.Context, orgID string, services []string) ([]*types.ApdexSettings, error) {
var apdexSettings []*types.ApdexSettings
err := module.
sqlstore.
BunDB().
NewSelect().
Model(&apdexSettings).
Where("org_id = ?", orgID).
Where("service_name IN (?)", bun.In(services)).
Scan(ctx)
if err != nil {
return nil, &model.ApiError{
Err: err,
}
return nil, module.sqlstore.WrapNotFoundErrf(err, errors.CodeNotFound, "apdex settings not found for services %v", services)
}
// add default apdex settings for services that don't have any
@ -36,7 +51,7 @@ func (mds *ModelDaoSqlite) GetApdexSettings(ctx context.Context, orgID string, s
}
if !found {
apdexSettings = append(apdexSettings, types.ApdexSettings{
apdexSettings = append(apdexSettings, &types.ApdexSettings{
ServiceName: service,
Threshold: defaultApdexThreshold,
})
@ -46,21 +61,21 @@ func (mds *ModelDaoSqlite) GetApdexSettings(ctx context.Context, orgID string, s
return apdexSettings, nil
}
func (mds *ModelDaoSqlite) SetApdexSettings(ctx context.Context, orgID string, apdexSettings *types.ApdexSettings) *model.ApiError {
// Set the org_id from the parameter since it's required for the foreign key constraint
func (module *module) Set(ctx context.Context, orgID string, apdexSettings *types.ApdexSettings) error {
apdexSettings.OrgID = orgID
apdexSettings.Identifiable.ID = valuer.GenerateUUID()
_, err := mds.bundb.NewInsert().
_, err := module.
sqlstore.
BunDB().
NewInsert().
Model(apdexSettings).
On("CONFLICT (org_id, service_name) DO UPDATE").
Set("threshold = EXCLUDED.threshold").
Set("exclude_status_codes = EXCLUDED.exclude_status_codes").
Exec(ctx)
if err != nil {
return &model.ApiError{
Err: err,
}
return err
}
return nil

View File

@ -0,0 +1,28 @@
package dashboard
import (
"context"
"net/http"
"github.com/SigNoz/signoz/pkg/types"
)
type Module interface {
Create(ctx context.Context, orgID string, email string, data map[string]interface{}) (*types.Dashboard, error)
List(ctx context.Context, orgID string) ([]*types.Dashboard, error)
Delete(ctx context.Context, orgID, uuid string) error
Get(ctx context.Context, orgID, uuid string) (*types.Dashboard, error)
GetByMetricNames(ctx context.Context, orgID string, metricNames []string) (map[string][]map[string]string, error)
Update(ctx context.Context, orgID, userEmail, uuid string, data map[string]interface{}) (*types.Dashboard, error)
LockUnlock(ctx context.Context, orgID, uuid string, lock bool) error
}
type Handler interface {
Delete(http.ResponseWriter, *http.Request)
}

View File

@ -0,0 +1,41 @@
package impldashboard
import (
"context"
"net/http"
"time"
"github.com/SigNoz/signoz/pkg/http/render"
"github.com/SigNoz/signoz/pkg/modules/dashboard"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/gorilla/mux"
)
type handler struct {
module dashboard.Module
}
func NewHandler(module dashboard.Module) dashboard.Handler {
return &handler{module: module}
}
func (handler *handler) Delete(rw http.ResponseWriter, req *http.Request) {
ctx, cancel := context.WithTimeout(req.Context(), 10*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(rw, err)
return
}
uuid := mux.Vars(req)["uuid"]
err = handler.module.Delete(ctx, claims.OrgID, uuid)
if err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusOK, nil)
}

View File

@ -0,0 +1,331 @@
package impldashboard
import (
"context"
"encoding/json"
"strings"
"time"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/modules/dashboard"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types"
"github.com/google/uuid"
)
type module struct {
sqlstore sqlstore.SQLStore
}
func NewModule(sqlstore sqlstore.SQLStore) dashboard.Module {
return &module{
sqlstore: sqlstore,
}
}
// CreateDashboard creates a new dashboard
func (module *module) Create(ctx context.Context, orgID string, email string, data map[string]interface{}) (*types.Dashboard, error) {
dash := &types.Dashboard{
Data: data,
}
dash.OrgID = orgID
dash.CreatedAt = time.Now()
dash.CreatedBy = email
dash.UpdatedAt = time.Now()
dash.UpdatedBy = email
dash.UpdateSlug()
dash.UUID = uuid.New().String()
if data["uuid"] != nil {
dash.UUID = data["uuid"].(string)
}
err := module.
sqlstore.
BunDB().
NewInsert().
Model(dash).
Returning("id").
Scan(ctx, &dash.ID)
if err != nil {
return nil, module.sqlstore.WrapAlreadyExistsErrf(err, errors.CodeAlreadyExists, "dashboard with uuid %s already exists", dash.UUID)
}
return dash, nil
}
func (module *module) List(ctx context.Context, orgID string) ([]*types.Dashboard, error) {
dashboards := []*types.Dashboard{}
err := module.
sqlstore.
BunDB().
NewSelect().
Model(&dashboards).
Where("org_id = ?", orgID).
Scan(ctx)
if err != nil {
return nil, err
}
return dashboards, nil
}
func (module *module) Delete(ctx context.Context, orgID, uuid string) error {
dashboard, err := module.Get(ctx, orgID, uuid)
if err != nil {
return err
}
if dashboard.Locked != nil && *dashboard.Locked == 1 {
return errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "dashboard is locked, please unlock the dashboard to be able to delete it")
}
result, err := module.
sqlstore.
BunDB().
NewDelete().
Model(&types.Dashboard{}).
Where("org_id = ?", orgID).
Where("uuid = ?", uuid).
Exec(ctx)
if err != nil {
return err
}
affectedRows, err := result.RowsAffected()
if err != nil {
return err
}
if affectedRows == 0 {
return errors.Newf(errors.TypeNotFound, errors.CodeNotFound, "no dashboard found with uuid: %s", uuid)
}
return nil
}
func (module *module) Get(ctx context.Context, orgID, uuid string) (*types.Dashboard, error) {
dashboard := types.Dashboard{}
err := module.
sqlstore.
BunDB().
NewSelect().
Model(&dashboard).
Where("org_id = ?", orgID).
Where("uuid = ?", uuid).
Scan(ctx)
if err != nil {
return nil, module.sqlstore.WrapNotFoundErrf(err, errors.CodeNotFound, "dashboard with uuid %s not found", uuid)
}
return &dashboard, nil
}
func (module *module) Update(ctx context.Context, orgID, userEmail, uuid string, data map[string]interface{}) (*types.Dashboard, error) {
mapData, err := json.Marshal(data)
if err != nil {
return nil, err
}
dashboard, err := module.Get(ctx, orgID, uuid)
if err != nil {
return nil, err
}
if dashboard.Locked != nil && *dashboard.Locked == 1 {
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "dashboard is locked, please unlock the dashboard to be able to edit it")
}
// if the total count of panels has reduced by more than 1,
// return error
existingIds := getWidgetIds(dashboard.Data)
newIds := getWidgetIds(data)
differenceIds := getIdDifference(existingIds, newIds)
if len(differenceIds) > 1 {
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "deleting more than one panel is not supported")
}
dashboard.UpdatedAt = time.Now()
dashboard.UpdatedBy = userEmail
dashboard.Data = data
_, err = module.sqlstore.
BunDB().
NewUpdate().
Model(dashboard).
Set("updated_at = ?", dashboard.UpdatedAt).
Set("updated_by = ?", userEmail).
Set("data = ?", mapData).
Where("uuid = ?", dashboard.UUID).Exec(ctx)
if err != nil {
return nil, err
}
return dashboard, nil
}
func (module *module) LockUnlock(ctx context.Context, orgID, uuid string, lock bool) error {
dashboard, err := module.Get(ctx, orgID, uuid)
if err != nil {
return err
}
var lockValue int
if lock {
lockValue = 1
} else {
lockValue = 0
}
_, err = module.
sqlstore.
BunDB().
NewUpdate().
Model(dashboard).
Set("locked = ?", lockValue).
Where("org_id = ?", orgID).
Where("uuid = ?", uuid).
Exec(ctx)
if err != nil {
return err
}
return nil
}
func (module *module) GetByMetricNames(ctx context.Context, orgID string, metricNames []string) (map[string][]map[string]string, error) {
dashboards := []types.Dashboard{}
err := module.
sqlstore.
BunDB().
NewSelect().
Model(&dashboards).
Where("org_id = ?", orgID).
Scan(ctx)
if err != nil {
return nil, err
}
// Initialize result map for each metric
result := make(map[string][]map[string]string)
// Process the JSON data in Go
for _, dashboard := range dashboards {
var dashData = dashboard.Data
dashTitle, _ := dashData["title"].(string)
widgets, ok := dashData["widgets"].([]interface{})
if !ok {
continue
}
for _, w := range widgets {
widget, ok := w.(map[string]interface{})
if !ok {
continue
}
widgetTitle, _ := widget["title"].(string)
widgetID, _ := widget["id"].(string)
query, ok := widget["query"].(map[string]interface{})
if !ok {
continue
}
builder, ok := query["builder"].(map[string]interface{})
if !ok {
continue
}
queryData, ok := builder["queryData"].([]interface{})
if !ok {
continue
}
for _, qd := range queryData {
data, ok := qd.(map[string]interface{})
if !ok {
continue
}
if dataSource, ok := data["dataSource"].(string); !ok || dataSource != "metrics" {
continue
}
aggregateAttr, ok := data["aggregateAttribute"].(map[string]interface{})
if !ok {
continue
}
if key, ok := aggregateAttr["key"].(string); ok {
// Check if this metric is in our list of interest
for _, metricName := range metricNames {
if strings.TrimSpace(key) == metricName {
result[metricName] = append(result[metricName], map[string]string{
"dashboard_id": dashboard.UUID,
"widget_name": widgetTitle,
"widget_id": widgetID,
"dashboard_name": dashTitle,
})
}
}
}
}
}
}
return result, nil
}
func getWidgetIds(data map[string]interface{}) []string {
widgetIds := []string{}
if data != nil && data["widgets"] != nil {
widgets, ok := data["widgets"]
if ok {
data, ok := widgets.([]interface{})
if ok {
for _, widget := range data {
sData, ok := widget.(map[string]interface{})
if ok && sData["query"] != nil && sData["id"] != nil {
id, ok := sData["id"].(string)
if ok {
widgetIds = append(widgetIds, id)
}
}
}
}
}
}
return widgetIds
}
func getIdDifference(existingIds []string, newIds []string) []string {
// Convert newIds array to a map for faster lookups
newIdsMap := make(map[string]bool)
for _, id := range newIds {
newIdsMap[id] = true
}
// Initialize a map to keep track of elements in the difference array
differenceMap := make(map[string]bool)
// Initialize the difference array
difference := []string{}
// Iterate through existingIds
for _, id := range existingIds {
// If the id is not found in newIds, and it's not already in the difference array
if _, found := newIdsMap[id]; !found && !differenceMap[id] {
difference = append(difference, id)
differenceMap[id] = true // Mark the id as seen in the difference array
}
}
return difference
}

View File

@ -0,0 +1,165 @@
package implsavedview
import (
"context"
"encoding/json"
"net/http"
"time"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/http/render"
"github.com/SigNoz/signoz/pkg/modules/savedview"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/gorilla/mux"
)
type handler struct {
module savedview.Module
}
func NewHandler(module savedview.Module) savedview.Handler {
return &handler{module: module}
}
func (handler *handler) Create(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(w, err)
return
}
var view v3.SavedView
if err := json.NewDecoder(r.Body).Decode(&view); err != nil {
render.Error(w, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to decode request body"))
return
}
// validate the query
if err := view.Validate(); err != nil {
render.Error(w, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to validate request body"))
return
}
uuid, err := handler.module.CreateView(ctx, claims.OrgID, view)
if err != nil {
render.Error(w, err)
return
}
render.Success(w, http.StatusOK, uuid)
}
func (handler *handler) Get(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(w, err)
return
}
viewID := mux.Vars(r)["viewId"]
viewUUID, err := valuer.NewUUID(viewID)
if err != nil {
render.Error(w, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to parse view id"))
return
}
view, err := handler.module.GetView(ctx, claims.OrgID, viewUUID)
if err != nil {
render.Error(w, err)
return
}
render.Success(w, http.StatusOK, view)
}
func (handler *handler) Update(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(w, err)
return
}
viewID := mux.Vars(r)["viewId"]
viewUUID, err := valuer.NewUUID(viewID)
if err != nil {
render.Error(w, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to parse view id"))
return
}
var view v3.SavedView
if err := json.NewDecoder(r.Body).Decode(&view); err != nil {
render.Error(w, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to decode request body"))
return
}
// validate the query
if err := view.Validate(); err != nil {
render.Error(w, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to validate request body"))
return
}
err = handler.module.UpdateView(ctx, claims.OrgID, viewUUID, view)
if err != nil {
render.Error(w, err)
return
}
render.Success(w, http.StatusOK, view)
}
func (handler *handler) Delete(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(w, err)
return
}
viewID := mux.Vars(r)["viewId"]
viewUUID, err := valuer.NewUUID(viewID)
if err != nil {
render.Error(w, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to parse view id"))
return
}
err = handler.module.DeleteView(ctx, claims.OrgID, viewUUID)
if err != nil {
render.Error(w, err)
return
}
render.Success(w, http.StatusOK, nil)
}
func (handler *handler) List(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(w, err)
return
}
sourcePage := r.URL.Query().Get("sourcePage")
name := r.URL.Query().Get("name")
category := r.URL.Query().Get("category")
queries, err := handler.module.GetViewsForFilters(r.Context(), claims.OrgID, sourcePage, name, category)
if err != nil {
render.Error(w, err)
return
}
render.Success(w, http.StatusOK, queries)
}

View File

@ -1,75 +1,35 @@
package explorer
package implsavedview
import (
"context"
"encoding/json"
"fmt"
"slices"
"strings"
"time"
"github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/modules/savedview"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/query-service/telemetry"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/valuer"
"go.uber.org/zap"
)
var store sqlstore.SQLStore
// InitWithDSN sets up setting up the connection pool global variable.
func InitWithDSN(sqlStore sqlstore.SQLStore) error {
store = sqlStore
telemetry.GetInstance().SetSavedViewsInfoCallback(GetSavedViewsInfo)
return nil
type module struct {
sqlstore sqlstore.SQLStore
}
func InitWithDB(sqlStore sqlstore.SQLStore) {
store = sqlStore
func NewModule(sqlstore sqlstore.SQLStore) savedview.Module {
return &module{sqlstore: sqlstore}
}
func GetViews(ctx context.Context, orgID string) ([]*v3.SavedView, error) {
var views []types.SavedView
err := store.BunDB().NewSelect().Model(&views).Where("org_id = ?", orgID).Scan(ctx)
if err != nil {
return nil, fmt.Errorf("error in getting saved views: %s", err.Error())
}
var savedViews []*v3.SavedView
for _, view := range views {
var compositeQuery v3.CompositeQuery
err = json.Unmarshal([]byte(view.Data), &compositeQuery)
if err != nil {
return nil, fmt.Errorf("error in unmarshalling explorer query data: %s", err.Error())
}
savedViews = append(savedViews, &v3.SavedView{
ID: view.ID,
Name: view.Name,
Category: view.Category,
CreatedAt: view.CreatedAt,
CreatedBy: view.CreatedBy,
UpdatedAt: view.UpdatedAt,
UpdatedBy: view.UpdatedBy,
Tags: strings.Split(view.Tags, ","),
SourcePage: view.SourcePage,
CompositeQuery: &compositeQuery,
ExtraData: view.ExtraData,
})
}
return savedViews, nil
}
func GetViewsForFilters(ctx context.Context, orgID string, sourcePage string, name string, category string) ([]*v3.SavedView, error) {
func (module *module) GetViewsForFilters(ctx context.Context, orgID string, sourcePage string, name string, category string) ([]*v3.SavedView, error) {
var views []types.SavedView
var err error
if len(category) == 0 {
err = store.BunDB().NewSelect().Model(&views).Where("org_id = ? AND source_page = ? AND name LIKE ?", orgID, sourcePage, "%"+name+"%").Scan(ctx)
err = module.sqlstore.BunDB().NewSelect().Model(&views).Where("org_id = ? AND source_page = ? AND name LIKE ?", orgID, sourcePage, "%"+name+"%").Scan(ctx)
} else {
err = store.BunDB().NewSelect().Model(&views).Where("org_id = ? AND source_page = ? AND category LIKE ? AND name LIKE ?", orgID, sourcePage, "%"+category+"%", "%"+name+"%").Scan(ctx)
err = module.sqlstore.BunDB().NewSelect().Model(&views).Where("org_id = ? AND source_page = ? AND category LIKE ? AND name LIKE ?", orgID, sourcePage, "%"+category+"%", "%"+name+"%").Scan(ctx)
}
if err != nil {
return nil, fmt.Errorf("error in getting saved views: %s", err.Error())
@ -98,7 +58,7 @@ func GetViewsForFilters(ctx context.Context, orgID string, sourcePage string, na
return savedViews, nil
}
func CreateView(ctx context.Context, orgID string, view v3.SavedView) (valuer.UUID, error) {
func (module *module) CreateView(ctx context.Context, orgID string, view v3.SavedView) (valuer.UUID, error) {
data, err := json.Marshal(view.CompositeQuery)
if err != nil {
return valuer.UUID{}, fmt.Errorf("error in marshalling explorer query data: %s", err.Error())
@ -137,16 +97,16 @@ func CreateView(ctx context.Context, orgID string, view v3.SavedView) (valuer.UU
ExtraData: view.ExtraData,
}
_, err = store.BunDB().NewInsert().Model(&dbView).Exec(ctx)
_, err = module.sqlstore.BunDB().NewInsert().Model(&dbView).Exec(ctx)
if err != nil {
return valuer.UUID{}, fmt.Errorf("error in creating saved view: %s", err.Error())
}
return uuid, nil
}
func GetView(ctx context.Context, orgID string, uuid valuer.UUID) (*v3.SavedView, error) {
func (module *module) GetView(ctx context.Context, orgID string, uuid valuer.UUID) (*v3.SavedView, error) {
var view types.SavedView
err := store.BunDB().NewSelect().Model(&view).Where("org_id = ? AND id = ?", orgID, uuid.StringValue()).Scan(ctx)
err := module.sqlstore.BunDB().NewSelect().Model(&view).Where("org_id = ? AND id = ?", orgID, uuid.StringValue()).Scan(ctx)
if err != nil {
return nil, fmt.Errorf("error in getting saved view: %s", err.Error())
}
@ -171,7 +131,7 @@ func GetView(ctx context.Context, orgID string, uuid valuer.UUID) (*v3.SavedView
}, nil
}
func UpdateView(ctx context.Context, orgID string, uuid valuer.UUID, view v3.SavedView) error {
func (module *module) UpdateView(ctx context.Context, orgID string, uuid valuer.UUID, view v3.SavedView) error {
data, err := json.Marshal(view.CompositeQuery)
if err != nil {
return fmt.Errorf("error in marshalling explorer query data: %s", err.Error())
@ -185,7 +145,7 @@ func UpdateView(ctx context.Context, orgID string, uuid valuer.UUID, view v3.Sav
updatedAt := time.Now()
updatedBy := claims.Email
_, err = store.BunDB().NewUpdate().
_, err = module.sqlstore.BunDB().NewUpdate().
Model(&types.SavedView{}).
Set("updated_at = ?, updated_by = ?, name = ?, category = ?, source_page = ?, tags = ?, data = ?, extra_data = ?",
updatedAt, updatedBy, view.Name, view.Category, view.SourcePage, strings.Join(view.Tags, ","), data, view.ExtraData).
@ -198,8 +158,8 @@ func UpdateView(ctx context.Context, orgID string, uuid valuer.UUID, view v3.Sav
return nil
}
func DeleteView(ctx context.Context, orgID string, uuid valuer.UUID) error {
_, err := store.BunDB().NewDelete().
func (module *module) DeleteView(ctx context.Context, orgID string, uuid valuer.UUID) error {
_, err := module.sqlstore.BunDB().NewDelete().
Model(&types.SavedView{}).
Where("id = ?", uuid.StringValue()).
Where("org_id = ?", orgID).
@ -209,43 +169,3 @@ func DeleteView(ctx context.Context, orgID string, uuid valuer.UUID) error {
}
return nil
}
func GetSavedViewsInfo(ctx context.Context) (*model.SavedViewsInfo, error) {
savedViewsInfo := model.SavedViewsInfo{}
// get single org ID from db
var orgIDs []string
err := store.BunDB().NewSelect().Model((*types.Organization)(nil)).Column("id").Scan(ctx, &orgIDs)
if err != nil {
return nil, fmt.Errorf("error in getting org IDs: %s", err.Error())
}
if len(orgIDs) != 1 {
zap.S().Warn("GetSavedViewsInfo: Zero or multiple org IDs found in the database", zap.Int("orgIDs", len(orgIDs)))
return &savedViewsInfo, nil
}
savedViews, err := GetViews(ctx, orgIDs[0])
if err != nil {
zap.S().Debug("Error in fetching saved views info: ", err)
return &savedViewsInfo, err
}
savedViewsInfo.TotalSavedViews = len(savedViews)
for _, view := range savedViews {
if view.SourcePage == "traces" {
savedViewsInfo.TracesSavedViews += 1
} else if view.SourcePage == "logs" {
savedViewsInfo.LogsSavedViews += 1
for _, query := range view.CompositeQuery.BuilderQueries {
if query.Filters != nil {
for _, item := range query.Filters.Items {
if slices.Contains([]string{"contains", "ncontains", "like", "nlike"}, string(item.Operator)) {
if item.Key.Key != "body" {
savedViewsInfo.LogsSavedViewWithContainsOp += 1
}
}
}
}
}
}
}
return &savedViewsInfo, nil
}

View File

@ -0,0 +1,38 @@
package savedview
import (
"context"
"net/http"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/valuer"
)
type Module interface {
GetViewsForFilters(ctx context.Context, orgID string, sourcePage string, name string, category string) ([]*v3.SavedView, error)
CreateView(ctx context.Context, orgID string, view v3.SavedView) (valuer.UUID, error)
GetView(ctx context.Context, orgID string, uuid valuer.UUID) (*v3.SavedView, error)
UpdateView(ctx context.Context, orgID string, uuid valuer.UUID, view v3.SavedView) error
DeleteView(ctx context.Context, orgID string, uuid valuer.UUID) error
}
type Handler interface {
// Creates the saved view
Create(http.ResponseWriter, *http.Request)
// Gets the saved view
Get(http.ResponseWriter, *http.Request)
// Updates the saved view
Update(http.ResponseWriter, *http.Request)
// Deletes the saved view
Delete(http.ResponseWriter, *http.Request)
// Lists the saved views
List(http.ResponseWriter, *http.Request)
}

View File

@ -1,46 +0,0 @@
package app
import (
"net/http"
"strings"
"github.com/SigNoz/signoz/pkg/http/render"
"github.com/SigNoz/signoz/pkg/query-service/dao"
"github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/types/authtypes"
)
func (aH *APIHandler) setApdexSettings(w http.ResponseWriter, r *http.Request) {
claims, errv2 := authtypes.ClaimsFromContext(r.Context())
if errv2 != nil {
render.Error(w, errv2)
return
}
req, err := parseSetApdexScoreRequest(r)
if aH.HandleError(w, err, http.StatusBadRequest) {
return
}
if err := dao.DB().SetApdexSettings(r.Context(), claims.OrgID, req); err != nil {
RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorInternal}, nil)
return
}
aH.WriteJSON(w, r, map[string]string{"data": "apdex score updated successfully"})
}
func (aH *APIHandler) getApdexSettings(w http.ResponseWriter, r *http.Request) {
services := r.URL.Query().Get("services")
claims, errv2 := authtypes.ClaimsFromContext(r.Context())
if errv2 != nil {
render.Error(w, errv2)
return
}
apdexSet, err := dao.DB().GetApdexSettings(r.Context(), claims.OrgID, strings.Split(strings.TrimSpace(services), ","))
if err != nil {
RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorInternal}, nil)
return
}
aH.WriteJSON(w, r, apdexSet)
}

View File

@ -521,9 +521,9 @@ func (c *Controller) UpdateServiceConfig(
// All dashboards that are available based on cloud integrations configuration
// across all cloud providers
func (c *Controller) AvailableDashboards(ctx context.Context, orgId string) (
[]types.Dashboard, *model.ApiError,
[]*types.Dashboard, *model.ApiError,
) {
allDashboards := []types.Dashboard{}
allDashboards := []*types.Dashboard{}
for _, provider := range []string{"aws"} {
providerDashboards, apiErr := c.AvailableDashboardsForCloudProvider(ctx, orgId, provider)
@ -541,7 +541,7 @@ func (c *Controller) AvailableDashboards(ctx context.Context, orgId string) (
func (c *Controller) AvailableDashboardsForCloudProvider(
ctx context.Context, orgID string, cloudProvider string,
) ([]types.Dashboard, *model.ApiError) {
) ([]*types.Dashboard, *model.ApiError) {
accountRecords, apiErr := c.accountsRepo.listConnected(ctx, orgID, cloudProvider)
if apiErr != nil {
@ -573,14 +573,14 @@ func (c *Controller) AvailableDashboardsForCloudProvider(
return nil, apiErr
}
svcDashboards := []types.Dashboard{}
svcDashboards := []*types.Dashboard{}
for _, svc := range allServices {
serviceDashboardsCreatedAt := servicesWithAvailableMetrics[svc.Id]
if serviceDashboardsCreatedAt != nil {
for _, d := range svc.Assets.Dashboards {
isLocked := 1
author := fmt.Sprintf("%s-integration", cloudProvider)
svcDashboards = append(svcDashboards, types.Dashboard{
svcDashboards = append(svcDashboards, &types.Dashboard{
UUID: c.dashboardUuid(cloudProvider, svc.Id, d.Id),
Locked: &isLocked,
Data: *d.Definition,
@ -619,7 +619,7 @@ func (c *Controller) GetDashboardById(
for _, d := range allDashboards {
if d.UUID == dashboardUuid {
return &d, nil
return d, nil
}
}

View File

@ -1,532 +0,0 @@
package dashboards
import (
"context"
"encoding/json"
"fmt"
"regexp"
"slices"
"strings"
"time"
"github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types"
"github.com/google/uuid"
"github.com/SigNoz/signoz/pkg/query-service/telemetry"
"go.uber.org/zap"
)
// This time the global variable is unexported.
var store sqlstore.SQLStore
// User for mapping job,instance from grafana
var (
instanceEQRE = regexp.MustCompile("instance(?s)=(?s)\\\"{{.instance}}\\\"")
nodeEQRE = regexp.MustCompile("instance(?s)=(?s)\\\"{{.node}}\\\"")
jobEQRE = regexp.MustCompile("job(?s)=(?s)\\\"{{.job}}\\\"")
instanceRERE = regexp.MustCompile("instance(?s)=~(?s)\\\"{{.instance}}\\\"")
nodeRERE = regexp.MustCompile("instance(?s)=~(?s)\\\"{{.node}}\\\"")
jobRERE = regexp.MustCompile("job(?s)=~(?s)\\\"{{.job}}\\\"")
)
// InitDB sets up setting up the connection pool global variable.
func InitDB(sqlStore sqlstore.SQLStore) error {
store = sqlStore
telemetry.GetInstance().SetDashboardsInfoCallback(GetDashboardsInfo)
return nil
}
// CreateDashboard creates a new dashboard
func CreateDashboard(ctx context.Context, orgID string, email string, data map[string]interface{}) (*types.Dashboard, *model.ApiError) {
dash := &types.Dashboard{
Data: data,
}
dash.OrgID = orgID
dash.CreatedAt = time.Now()
dash.CreatedBy = email
dash.UpdatedAt = time.Now()
dash.UpdatedBy = email
dash.UpdateSlug()
dash.UUID = uuid.New().String()
if data["uuid"] != nil {
dash.UUID = data["uuid"].(string)
}
err := store.BunDB().NewInsert().Model(dash).Returning("id").Scan(ctx, &dash.ID)
if err != nil {
zap.L().Error("Error in inserting dashboard data: ", zap.Any("dashboard", dash), zap.Error(err))
return nil, &model.ApiError{Typ: model.ErrorExec, Err: err}
}
return dash, nil
}
func GetDashboards(ctx context.Context, orgID string) ([]types.Dashboard, *model.ApiError) {
dashboards := []types.Dashboard{}
err := store.BunDB().NewSelect().Model(&dashboards).Where("org_id = ?", orgID).Scan(ctx)
if err != nil {
return nil, &model.ApiError{Typ: model.ErrorExec, Err: err}
}
return dashboards, nil
}
func DeleteDashboard(ctx context.Context, orgID, uuid string) *model.ApiError {
dashboard, dErr := GetDashboard(ctx, orgID, uuid)
if dErr != nil {
zap.L().Error("Error in getting dashboard: ", zap.String("uuid", uuid), zap.Any("error", dErr))
return dErr
}
if dashboard.Locked != nil && *dashboard.Locked == 1 {
return model.BadRequest(fmt.Errorf("dashboard is locked, please unlock the dashboard to be able to delete it"))
}
result, err := store.BunDB().NewDelete().Model(&types.Dashboard{}).Where("org_id = ?", orgID).Where("uuid = ?", uuid).Exec(ctx)
if err != nil {
return &model.ApiError{Typ: model.ErrorExec, Err: err}
}
affectedRows, err := result.RowsAffected()
if err != nil {
return &model.ApiError{Typ: model.ErrorExec, Err: err}
}
if affectedRows == 0 {
return &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("no dashboard found with uuid: %s", uuid)}
}
return nil
}
func GetDashboard(ctx context.Context, orgID, uuid string) (*types.Dashboard, *model.ApiError) {
dashboard := types.Dashboard{}
err := store.BunDB().NewSelect().Model(&dashboard).Where("org_id = ?", orgID).Where("uuid = ?", uuid).Scan(ctx)
if err != nil {
return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("no dashboard found with uuid: %s", uuid)}
}
return &dashboard, nil
}
func UpdateDashboard(ctx context.Context, orgID, userEmail, uuid string, data map[string]interface{}) (*types.Dashboard, *model.ApiError) {
mapData, err := json.Marshal(data)
if err != nil {
zap.L().Error("Error in marshalling data field in dashboard: ", zap.Any("data", data), zap.Error(err))
return nil, &model.ApiError{Typ: model.ErrorBadData, Err: err}
}
dashboard, apiErr := GetDashboard(ctx, orgID, uuid)
if apiErr != nil {
return nil, apiErr
}
if dashboard.Locked != nil && *dashboard.Locked == 1 {
return nil, model.BadRequest(fmt.Errorf("dashboard is locked, please unlock the dashboard to be able to edit it"))
}
// if the total count of panels has reduced by more than 1,
// return error
existingIds := getWidgetIds(dashboard.Data)
newIds := getWidgetIds(data)
differenceIds := getIdDifference(existingIds, newIds)
if len(differenceIds) > 1 {
return nil, model.BadRequest(fmt.Errorf("deleting more than one panel is not supported"))
}
dashboard.UpdatedAt = time.Now()
dashboard.UpdatedBy = userEmail
dashboard.Data = data
_, err = store.BunDB().NewUpdate().Model(dashboard).Set("updated_at = ?", dashboard.UpdatedAt).Set("updated_by = ?", userEmail).Set("data = ?", mapData).Where("uuid = ?", dashboard.UUID).Exec(ctx)
if err != nil {
zap.L().Error("Error in inserting dashboard data", zap.Any("data", data), zap.Error(err))
return nil, &model.ApiError{Typ: model.ErrorExec, Err: err}
}
return dashboard, nil
}
func LockUnlockDashboard(ctx context.Context, orgID, uuid string, lock bool) *model.ApiError {
dashboard, apiErr := GetDashboard(ctx, orgID, uuid)
if apiErr != nil {
return apiErr
}
var lockValue int
if lock {
lockValue = 1
} else {
lockValue = 0
}
_, err := store.BunDB().NewUpdate().Model(dashboard).Set("locked = ?", lockValue).Where("org_id = ?", orgID).Where("uuid = ?", uuid).Exec(ctx)
if err != nil {
zap.L().Error("Error in updating dashboard", zap.String("uuid", uuid), zap.Error(err))
return &model.ApiError{Typ: model.ErrorExec, Err: err}
}
return nil
}
func IsPostDataSane(data *map[string]interface{}) error {
val, ok := (*data)["title"]
if !ok || val == nil {
return fmt.Errorf("title not found in post data")
}
return nil
}
func getWidgetIds(data map[string]interface{}) []string {
widgetIds := []string{}
if data != nil && data["widgets"] != nil {
widgets, ok := data["widgets"]
if ok {
data, ok := widgets.([]interface{})
if ok {
for _, widget := range data {
sData, ok := widget.(map[string]interface{})
if ok && sData["query"] != nil && sData["id"] != nil {
id, ok := sData["id"].(string)
if ok {
widgetIds = append(widgetIds, id)
}
}
}
}
}
}
return widgetIds
}
func getIdDifference(existingIds []string, newIds []string) []string {
// Convert newIds array to a map for faster lookups
newIdsMap := make(map[string]bool)
for _, id := range newIds {
newIdsMap[id] = true
}
// Initialize a map to keep track of elements in the difference array
differenceMap := make(map[string]bool)
// Initialize the difference array
difference := []string{}
// Iterate through existingIds
for _, id := range existingIds {
// If the id is not found in newIds, and it's not already in the difference array
if _, found := newIdsMap[id]; !found && !differenceMap[id] {
difference = append(difference, id)
differenceMap[id] = true // Mark the id as seen in the difference array
}
}
return difference
}
// GetDashboardsInfo returns analytics data for dashboards
func GetDashboardsInfo(ctx context.Context) (*model.DashboardsInfo, error) {
dashboardsInfo := model.DashboardsInfo{}
// fetch dashboards from dashboard db
dashboards := []types.Dashboard{}
err := store.BunDB().NewSelect().Model(&dashboards).Scan(ctx)
if err != nil {
zap.L().Error("Error in processing sql query", zap.Error(err))
return &dashboardsInfo, err
}
totalDashboardsWithPanelAndName := 0
var dashboardNames []string
count := 0
queriesWithTagAttrs := 0
for _, dashboard := range dashboards {
if isDashboardWithPanelAndName(dashboard.Data) {
totalDashboardsWithPanelAndName = totalDashboardsWithPanelAndName + 1
}
dashboardName := extractDashboardName(dashboard.Data)
if dashboardName != "" {
dashboardNames = append(dashboardNames, dashboardName)
}
dashboardInfo := countPanelsInDashboard(dashboard.Data)
dashboardsInfo.LogsBasedPanels += dashboardInfo.LogsBasedPanels
dashboardsInfo.TracesBasedPanels += dashboardInfo.TracesBasedPanels
dashboardsInfo.MetricBasedPanels += dashboardInfo.MetricBasedPanels
dashboardsInfo.LogsPanelsWithAttrContainsOp += dashboardInfo.LogsPanelsWithAttrContainsOp
dashboardsInfo.DashboardsWithLogsChQuery += dashboardInfo.DashboardsWithLogsChQuery
dashboardsInfo.DashboardsWithTraceChQuery += dashboardInfo.DashboardsWithTraceChQuery
if isDashboardWithTSV2(dashboard.Data) {
count = count + 1
}
if isDashboardWithTagAttrs(dashboard.Data) {
queriesWithTagAttrs += 1
}
if dashboardInfo.DashboardsWithTraceChQuery > 0 {
dashboardsInfo.DashboardNamesWithTraceChQuery = append(dashboardsInfo.DashboardNamesWithTraceChQuery, dashboardName)
}
// check if dashboard is a has a log operator with contains
}
dashboardsInfo.DashboardNames = dashboardNames
dashboardsInfo.TotalDashboards = len(dashboards)
dashboardsInfo.TotalDashboardsWithPanelAndName = totalDashboardsWithPanelAndName
dashboardsInfo.QueriesWithTSV2 = count
dashboardsInfo.QueriesWithTagAttrs = queriesWithTagAttrs
return &dashboardsInfo, nil
}
func isDashboardWithTSV2(data map[string]interface{}) bool {
jsonData, err := json.Marshal(data)
if err != nil {
return false
}
return strings.Contains(string(jsonData), "time_series_v2")
}
func isDashboardWithTagAttrs(data map[string]interface{}) bool {
jsonData, err := json.Marshal(data)
if err != nil {
return false
}
return strings.Contains(string(jsonData), "span_attributes") ||
strings.Contains(string(jsonData), "tag_attributes")
}
func isDashboardWithLogsClickhouseQuery(data map[string]interface{}) bool {
jsonData, err := json.Marshal(data)
if err != nil {
return false
}
result := strings.Contains(string(jsonData), "signoz_logs.distributed_logs") ||
strings.Contains(string(jsonData), "signoz_logs.logs")
return result
}
func isDashboardWithTracesClickhouseQuery(data map[string]interface{}) bool {
jsonData, err := json.Marshal(data)
if err != nil {
return false
}
// also check if the query is actually active
str := string(jsonData)
result := strings.Contains(str, "signoz_traces.distributed_signoz_index_v2") ||
strings.Contains(str, "signoz_traces.distributed_signoz_spans") ||
strings.Contains(str, "signoz_traces.distributed_signoz_error_index_v2")
return result
}
func isDashboardWithPanelAndName(data map[string]interface{}) bool {
isDashboardName := false
isDashboardWithPanelAndName := false
if data != nil && data["title"] != nil && data["widgets"] != nil {
title, ok := data["title"].(string)
if ok && title != "Sample Title" {
isDashboardName = true
}
widgets, ok := data["widgets"]
if ok && isDashboardName {
data, ok := widgets.([]interface{})
if ok && len(data) > 0 {
isDashboardWithPanelAndName = true
}
}
}
return isDashboardWithPanelAndName
}
func extractDashboardName(data map[string]interface{}) string {
if data != nil && data["title"] != nil {
title, ok := data["title"].(string)
if ok {
return title
}
}
return ""
}
func checkLogPanelAttrContains(data map[string]interface{}) int {
var logsPanelsWithAttrContains int
filters, ok := data["filters"].(map[string]interface{})
if ok && filters["items"] != nil {
items, ok := filters["items"].([]interface{})
if ok {
for _, item := range items {
itemMap, ok := item.(map[string]interface{})
if ok {
opStr, ok := itemMap["op"].(string)
if ok {
if slices.Contains([]string{"contains", "ncontains", "like", "nlike"}, opStr) {
// check if it's not body
key, ok := itemMap["key"].(map[string]string)
if ok && key["key"] != "body" {
logsPanelsWithAttrContains++
}
}
}
}
}
}
}
return logsPanelsWithAttrContains
}
func countPanelsInDashboard(inputData map[string]interface{}) model.DashboardsInfo {
var logsPanelCount, tracesPanelCount, metricsPanelCount, logsPanelsWithAttrContains int
traceChQueryCount := 0
logChQueryCount := 0
// totalPanels := 0
if inputData != nil && inputData["widgets"] != nil {
widgets, ok := inputData["widgets"]
if ok {
data, ok := widgets.([]interface{})
if ok {
for _, widget := range data {
sData, ok := widget.(map[string]interface{})
if ok && sData["query"] != nil {
// totalPanels++
query, ok := sData["query"].(map[string]interface{})
if ok && query["queryType"] == "builder" && query["builder"] != nil {
builderData, ok := query["builder"].(map[string]interface{})
if ok && builderData["queryData"] != nil {
builderQueryData, ok := builderData["queryData"].([]interface{})
if ok {
for _, queryData := range builderQueryData {
data, ok := queryData.(map[string]interface{})
if ok {
if data["dataSource"] == "traces" {
tracesPanelCount++
} else if data["dataSource"] == "metrics" {
metricsPanelCount++
} else if data["dataSource"] == "logs" {
logsPanelCount++
logsPanelsWithAttrContains += checkLogPanelAttrContains(data)
}
}
}
}
}
} else if ok && query["queryType"] == "clickhouse_sql" && query["clickhouse_sql"] != nil {
if isDashboardWithLogsClickhouseQuery(inputData) {
logChQueryCount = 1
}
if isDashboardWithTracesClickhouseQuery(inputData) {
traceChQueryCount = 1
}
}
}
}
}
}
}
return model.DashboardsInfo{
LogsBasedPanels: logsPanelCount,
TracesBasedPanels: tracesPanelCount,
MetricBasedPanels: metricsPanelCount,
DashboardsWithLogsChQuery: logChQueryCount,
DashboardsWithTraceChQuery: traceChQueryCount,
LogsPanelsWithAttrContainsOp: logsPanelsWithAttrContains,
}
}
func GetDashboardsWithMetricNames(ctx context.Context, orgID string, metricNames []string) (map[string][]map[string]string, *model.ApiError) {
dashboards := []types.Dashboard{}
err := store.BunDB().NewSelect().Model(&dashboards).Where("org_id = ?", orgID).Scan(ctx)
if err != nil {
zap.L().Error("Error in getting dashboards", zap.Error(err))
return nil, &model.ApiError{Typ: model.ErrorExec, Err: err}
}
if err != nil {
zap.L().Error("Error in getting dashboards", zap.Error(err))
return nil, &model.ApiError{Typ: model.ErrorExec, Err: err}
}
// Initialize result map for each metric
result := make(map[string][]map[string]string)
// Process the JSON data in Go
for _, dashboard := range dashboards {
var dashData = dashboard.Data
dashTitle, _ := dashData["title"].(string)
widgets, ok := dashData["widgets"].([]interface{})
if !ok {
continue
}
for _, w := range widgets {
widget, ok := w.(map[string]interface{})
if !ok {
continue
}
widgetTitle, _ := widget["title"].(string)
widgetID, _ := widget["id"].(string)
query, ok := widget["query"].(map[string]interface{})
if !ok {
continue
}
builder, ok := query["builder"].(map[string]interface{})
if !ok {
continue
}
queryData, ok := builder["queryData"].([]interface{})
if !ok {
continue
}
for _, qd := range queryData {
data, ok := qd.(map[string]interface{})
if !ok {
continue
}
if dataSource, ok := data["dataSource"].(string); !ok || dataSource != "metrics" {
continue
}
aggregateAttr, ok := data["aggregateAttribute"].(map[string]interface{})
if !ok {
continue
}
if key, ok := aggregateAttr["key"].(string); ok {
// Check if this metric is in our list of interest
for _, metricName := range metricNames {
if strings.TrimSpace(key) == metricName {
result[metricName] = append(result[metricName], map[string]string{
"dashboard_id": dashboard.UUID,
"widget_name": widgetTitle,
"widget_id": widgetID,
"dashboard_name": dashTitle,
})
}
}
}
}
}
}
return result, nil
}

View File

@ -39,8 +39,6 @@ import (
"github.com/SigNoz/signoz/pkg/cache"
"github.com/SigNoz/signoz/pkg/query-service/agentConf"
"github.com/SigNoz/signoz/pkg/query-service/app/cloudintegrations"
"github.com/SigNoz/signoz/pkg/query-service/app/dashboards"
"github.com/SigNoz/signoz/pkg/query-service/app/explorer"
"github.com/SigNoz/signoz/pkg/query-service/app/inframetrics"
queues2 "github.com/SigNoz/signoz/pkg/query-service/app/integrations/messagingQueues/queues"
"github.com/SigNoz/signoz/pkg/query-service/app/integrations/thirdPartyApi"
@ -67,7 +65,6 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/app/integrations/messagingQueues/kafka"
"github.com/SigNoz/signoz/pkg/query-service/app/logparsingpipeline"
"github.com/SigNoz/signoz/pkg/query-service/dao"
"github.com/SigNoz/signoz/pkg/query-service/interfaces"
"github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/query-service/rules"
@ -91,7 +88,6 @@ func NewRouter() *mux.Router {
// APIHandler implements the query service public API
type APIHandler struct {
reader interfaces.Reader
appDao dao.ModelDao
ruleManager *rules.Manager
featureFlags interfaces.FeatureLookup
querier interfaces.Querier
@ -156,9 +152,6 @@ type APIHandlerOpts struct {
PreferSpanMetrics bool
// dao layer to perform crud on app objects like dashboard, alerts etc
AppDao dao.ModelDao
// rule manager handles rule crud operations
RuleManager *rules.Manager
@ -223,12 +216,11 @@ func NewAPIHandler(opts APIHandlerOpts) (*APIHandler, error) {
statefulsetsRepo := inframetrics.NewStatefulSetsRepo(opts.Reader, querierv2)
jobsRepo := inframetrics.NewJobsRepo(opts.Reader, querierv2)
pvcsRepo := inframetrics.NewPvcsRepo(opts.Reader, querierv2)
summaryService := metricsexplorer.NewSummaryService(opts.Reader, opts.RuleManager)
summaryService := metricsexplorer.NewSummaryService(opts.Reader, opts.RuleManager, opts.Signoz.Modules.Dashboard)
//quickFilterModule := quickfilter.NewAPI(opts.QuickFilterModule)
aH := &APIHandler{
reader: opts.Reader,
appDao: opts.AppDao,
preferSpanMetrics: opts.PreferSpanMetrics,
temporalityMap: make(map[string]map[v3.Temporality]bool),
ruleManager: opts.RuleManager,
@ -531,14 +523,14 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router, am *middleware.AuthZ) {
router.HandleFunc("/api/v1/dashboards", am.EditAccess(aH.createDashboards)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/dashboards/{uuid}", am.ViewAccess(aH.getDashboard)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/dashboards/{uuid}", am.EditAccess(aH.updateDashboard)).Methods(http.MethodPut)
router.HandleFunc("/api/v1/dashboards/{uuid}", am.EditAccess(aH.deleteDashboard)).Methods(http.MethodDelete)
router.HandleFunc("/api/v1/dashboards/{uuid}", am.EditAccess(aH.Signoz.Handlers.Dashboard.Delete)).Methods(http.MethodDelete)
router.HandleFunc("/api/v2/variables/query", am.ViewAccess(aH.queryDashboardVarsV2)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/explorer/views", am.ViewAccess(aH.getSavedViews)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/explorer/views", am.EditAccess(aH.createSavedViews)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/explorer/views/{viewId}", am.ViewAccess(aH.getSavedView)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/explorer/views/{viewId}", am.EditAccess(aH.updateSavedView)).Methods(http.MethodPut)
router.HandleFunc("/api/v1/explorer/views/{viewId}", am.EditAccess(aH.deleteSavedView)).Methods(http.MethodDelete)
router.HandleFunc("/api/v1/explorer/views", am.ViewAccess(aH.Signoz.Handlers.SavedView.List)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/explorer/views", am.EditAccess(aH.Signoz.Handlers.SavedView.Create)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/explorer/views/{viewId}", am.ViewAccess(aH.Signoz.Handlers.SavedView.Get)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/explorer/views/{viewId}", am.EditAccess(aH.Signoz.Handlers.SavedView.Update)).Methods(http.MethodPut)
router.HandleFunc("/api/v1/explorer/views/{viewId}", am.EditAccess(aH.Signoz.Handlers.SavedView.Delete)).Methods(http.MethodDelete)
router.HandleFunc("/api/v1/feedback", am.OpenAccess(aH.submitFeedback)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/event", am.ViewAccess(aH.registerEvent)).Methods(http.MethodPost)
@ -553,8 +545,8 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router, am *middleware.AuthZ) {
router.HandleFunc("/api/v1/dependency_graph", am.ViewAccess(aH.dependencyGraph)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/settings/ttl", am.AdminAccess(aH.setTTL)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/settings/ttl", am.ViewAccess(aH.getTTL)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/settings/apdex", am.AdminAccess(aH.setApdexSettings)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/settings/apdex", am.ViewAccess(aH.getApdexSettings)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/settings/apdex", am.AdminAccess(aH.Signoz.Handlers.Apdex.Set)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/settings/apdex", am.ViewAccess(aH.Signoz.Handlers.Apdex.Get)).Methods(http.MethodGet)
router.HandleFunc("/api/v2/traces/fields", am.ViewAccess(aH.traceFields)).Methods(http.MethodGet)
router.HandleFunc("/api/v2/traces/fields", am.EditAccess(aH.updateTraceField)).Methods(http.MethodPost)
@ -1093,9 +1085,10 @@ func (aH *APIHandler) getDashboards(w http.ResponseWriter, r *http.Request) {
render.Error(w, errv2)
return
}
allDashboards, err := dashboards.GetDashboards(r.Context(), claims.OrgID)
if err != nil {
RespondError(w, err, nil)
allDashboards, errv2 := aH.Signoz.Modules.Dashboard.List(r.Context(), claims.OrgID)
if errv2 != nil {
render.Error(w, errv2)
return
}
@ -1147,7 +1140,7 @@ func (aH *APIHandler) getDashboards(w http.ResponseWriter, r *http.Request) {
inter = Intersection(inter, tags2Dash[tag])
}
filteredDashboards := []types.Dashboard{}
filteredDashboards := []*types.Dashboard{}
for _, val := range inter {
dash := (allDashboards)[val]
filteredDashboards = append(filteredDashboards, dash)
@ -1155,23 +1148,6 @@ func (aH *APIHandler) getDashboards(w http.ResponseWriter, r *http.Request) {
aH.Respond(w, filteredDashboards)
}
func (aH *APIHandler) deleteDashboard(w http.ResponseWriter, r *http.Request) {
uuid := mux.Vars(r)["uuid"]
claims, errv2 := authtypes.ClaimsFromContext(r.Context())
if errv2 != nil {
render.Error(w, errv2)
return
}
err := dashboards.DeleteDashboard(r.Context(), claims.OrgID, uuid)
if err != nil {
RespondError(w, err, nil)
return
}
aH.Respond(w, nil)
}
func prepareQuery(r *http.Request) (string, error) {
@ -1235,6 +1211,12 @@ func (aH *APIHandler) queryDashboardVarsV2(w http.ResponseWriter, r *http.Reques
}
func (aH *APIHandler) updateDashboard(w http.ResponseWriter, r *http.Request) {
claims, errv2 := authtypes.ClaimsFromContext(r.Context())
if errv2 != nil {
render.Error(w, errv2)
return
}
uuid := mux.Vars(r)["uuid"]
var postData map[string]interface{}
@ -1243,20 +1225,16 @@ func (aH *APIHandler) updateDashboard(w http.ResponseWriter, r *http.Request) {
RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, "Error reading request body")
return
}
err = dashboards.IsPostDataSane(&postData)
err = aH.IsDashboardPostDataSane(&postData)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, "Error reading request body")
return
}
claims, errv2 := authtypes.ClaimsFromContext(r.Context())
if errv2 != nil {
render.Error(w, errv2)
return
}
dashboard, apiError := dashboards.UpdateDashboard(r.Context(), claims.OrgID, claims.Email, uuid, postData)
dashboard, apiError := aH.Signoz.Modules.Dashboard.Update(r.Context(), claims.OrgID, claims.Email, uuid, postData)
if apiError != nil {
RespondError(w, apiError, nil)
render.Error(w, apiError)
return
}
@ -1264,6 +1242,15 @@ func (aH *APIHandler) updateDashboard(w http.ResponseWriter, r *http.Request) {
}
func (aH *APIHandler) IsDashboardPostDataSane(data *map[string]interface{}) error {
val, ok := (*data)["title"]
if !ok || val == nil {
return fmt.Errorf("title not found in post data")
}
return nil
}
func (aH *APIHandler) getDashboard(w http.ResponseWriter, r *http.Request) {
uuid := mux.Vars(r)["uuid"]
@ -1273,11 +1260,12 @@ func (aH *APIHandler) getDashboard(w http.ResponseWriter, r *http.Request) {
render.Error(w, errv2)
return
}
dashboard, apiError := dashboards.GetDashboard(r.Context(), claims.OrgID, uuid)
dashboard, errv2 := aH.Signoz.Modules.Dashboard.Get(r.Context(), claims.OrgID, uuid)
if apiError != nil {
if apiError.Type() != model.ErrorNotFound {
RespondError(w, apiError, nil)
var apiError *model.ApiError
if errv2 != nil {
if !errorsV2.Ast(errv2, errorsV2.TypeNotFound) {
render.Error(w, errv2)
return
}
@ -1308,7 +1296,6 @@ func (aH *APIHandler) getDashboard(w http.ResponseWriter, r *http.Request) {
}
func (aH *APIHandler) createDashboards(w http.ResponseWriter, r *http.Request) {
var postData map[string]interface{}
err := json.NewDecoder(r.Body).Decode(&postData)
@ -1317,7 +1304,7 @@ func (aH *APIHandler) createDashboards(w http.ResponseWriter, r *http.Request) {
return
}
err = dashboards.IsPostDataSane(&postData)
err = aH.IsDashboardPostDataSane(&postData)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, "Error reading request body")
return
@ -1327,10 +1314,10 @@ func (aH *APIHandler) createDashboards(w http.ResponseWriter, r *http.Request) {
render.Error(w, errv2)
return
}
dash, apiErr := dashboards.CreateDashboard(r.Context(), claims.OrgID, claims.Email, postData)
if apiErr != nil {
RespondError(w, apiErr, nil)
dash, errv2 := aH.Signoz.Modules.Dashboard.Create(r.Context(), claims.OrgID, claims.Email, postData)
if errv2 != nil {
render.Error(w, errv2)
return
}
@ -4233,129 +4220,6 @@ func (aH *APIHandler) CreateLogsPipeline(w http.ResponseWriter, r *http.Request)
aH.Respond(w, res)
}
func (aH *APIHandler) getSavedViews(w http.ResponseWriter, r *http.Request) {
// get sourcePage, name, and category from the query params
sourcePage := r.URL.Query().Get("sourcePage")
name := r.URL.Query().Get("name")
category := r.URL.Query().Get("category")
claims, errv2 := authtypes.ClaimsFromContext(r.Context())
if errv2 != nil {
render.Error(w, errv2)
return
}
queries, err := explorer.GetViewsForFilters(r.Context(), claims.OrgID, sourcePage, name, category)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil)
return
}
aH.Respond(w, queries)
}
func (aH *APIHandler) createSavedViews(w http.ResponseWriter, r *http.Request) {
var view v3.SavedView
err := json.NewDecoder(r.Body).Decode(&view)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, nil)
return
}
// validate the query
if err := view.Validate(); err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, nil)
return
}
claims, errv2 := authtypes.ClaimsFromContext(r.Context())
if errv2 != nil {
render.Error(w, errv2)
return
}
uuid, err := explorer.CreateView(r.Context(), claims.OrgID, view)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil)
return
}
aH.Respond(w, uuid)
}
func (aH *APIHandler) getSavedView(w http.ResponseWriter, r *http.Request) {
viewID := mux.Vars(r)["viewId"]
viewUUID, err := valuer.NewUUID(viewID)
if err != nil {
render.Error(w, errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, err.Error()))
return
}
claims, errv2 := authtypes.ClaimsFromContext(r.Context())
if errv2 != nil {
render.Error(w, errv2)
return
}
view, err := explorer.GetView(r.Context(), claims.OrgID, viewUUID)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil)
return
}
aH.Respond(w, view)
}
func (aH *APIHandler) updateSavedView(w http.ResponseWriter, r *http.Request) {
viewID := mux.Vars(r)["viewId"]
viewUUID, err := valuer.NewUUID(viewID)
if err != nil {
render.Error(w, errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, err.Error()))
return
}
var view v3.SavedView
err = json.NewDecoder(r.Body).Decode(&view)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, nil)
return
}
// validate the query
if err := view.Validate(); err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, nil)
return
}
claims, errv2 := authtypes.ClaimsFromContext(r.Context())
if errv2 != nil {
render.Error(w, errv2)
return
}
err = explorer.UpdateView(r.Context(), claims.OrgID, viewUUID, view)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil)
return
}
aH.Respond(w, view)
}
func (aH *APIHandler) deleteSavedView(w http.ResponseWriter, r *http.Request) {
viewID := mux.Vars(r)["viewId"]
viewUUID, err := valuer.NewUUID(viewID)
if err != nil {
render.Error(w, errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, err.Error()))
return
}
claims, errv2 := authtypes.ClaimsFromContext(r.Context())
if errv2 != nil {
render.Error(w, errv2)
return
}
err = explorer.DeleteView(r.Context(), claims.OrgID, viewUUID)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil)
return
}
aH.Respond(w, nil)
}
func (aH *APIHandler) autocompleteAggregateAttributes(w http.ResponseWriter, r *http.Request) {
claims, err := authtypes.ClaimsFromContext(r.Context())
if err != nil {

View File

@ -129,7 +129,7 @@ func (c *Controller) GetPipelinesForInstalledIntegrations(
func (c *Controller) GetDashboardsForInstalledIntegrations(
ctx context.Context, orgId string,
) ([]types.Dashboard, *model.ApiError) {
) ([]*types.Dashboard, *model.ApiError) {
return c.mgr.GetDashboardsForInstalledIntegrations(ctx, orgId)
}

View File

@ -355,13 +355,13 @@ func (m *Manager) GetInstalledIntegrationDashboardById(
func (m *Manager) GetDashboardsForInstalledIntegrations(
ctx context.Context,
orgId string,
) ([]types.Dashboard, *model.ApiError) {
) ([]*types.Dashboard, *model.ApiError) {
installedIntegrations, apiErr := m.getInstalledIntegrations(ctx, orgId)
if apiErr != nil {
return nil, apiErr
}
result := []types.Dashboard{}
result := []*types.Dashboard{}
for _, ii := range installedIntegrations {
for _, dd := range ii.Assets.Dashboards {
@ -369,7 +369,7 @@ func (m *Manager) GetDashboardsForInstalledIntegrations(
if dashboardId, ok := dId.(string); ok {
isLocked := 1
author := "integration"
result = append(result, types.Dashboard{
result = append(result, &types.Dashboard{
UUID: m.dashboardUuid(ii.IntegrationSummary.Id, dashboardId),
Locked: &isLocked,
Data: dd,

View File

@ -10,7 +10,7 @@ import (
"go.uber.org/zap"
"github.com/SigNoz/signoz/pkg/query-service/app/dashboards"
"github.com/SigNoz/signoz/pkg/modules/dashboard"
"github.com/SigNoz/signoz/pkg/query-service/interfaces"
"github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/query-service/model/metrics_explorer"
@ -24,10 +24,11 @@ import (
type SummaryService struct {
reader interfaces.Reader
rulesManager *rules.Manager
dashboard dashboard.Module
}
func NewSummaryService(reader interfaces.Reader, alertManager *rules.Manager) *SummaryService {
return &SummaryService{reader: reader, rulesManager: alertManager}
func NewSummaryService(reader interfaces.Reader, alertManager *rules.Manager, dashboard dashboard.Module) *SummaryService {
return &SummaryService{reader: reader, rulesManager: alertManager, dashboard: dashboard}
}
func (receiver *SummaryService) FilterKeys(ctx context.Context, params *metrics_explorer.FilterKeyRequest) (*metrics_explorer.FilterKeyResponse, *model.ApiError) {
@ -164,7 +165,7 @@ func (receiver *SummaryService) GetMetricsSummary(ctx context.Context, orgID val
if errv2 != nil {
return &model.ApiError{Typ: model.ErrorInternal, Err: errv2}
}
data, err := dashboards.GetDashboardsWithMetricNames(ctx, claims.OrgID, metricNames)
data, err := receiver.dashboard.GetByMetricNames(ctx, claims.OrgID, metricNames)
if err != nil {
return err
}
@ -337,9 +338,9 @@ func (receiver *SummaryService) GetRelatedMetrics(ctx context.Context, params *m
if errv2 != nil {
return &model.ApiError{Typ: model.ErrorInternal, Err: errv2}
}
names, apiError := dashboards.GetDashboardsWithMetricNames(ctx, claims.OrgID, metricNames)
if apiError != nil {
return apiError
names, err := receiver.dashboard.GetByMetricNames(ctx, claims.OrgID, metricNames)
if err != nil {
return err
}
if names != nil {
jsonData, err := json.Marshal(names)

View File

@ -32,7 +32,6 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
"github.com/SigNoz/signoz/pkg/query-service/utils"
querytemplate "github.com/SigNoz/signoz/pkg/query-service/utils/queryTemplate"
"github.com/SigNoz/signoz/pkg/types"
chVariables "github.com/SigNoz/signoz/pkg/variables/clickhouse"
)
@ -472,14 +471,6 @@ func parseGetTTL(r *http.Request) (*model.GetTTLParams, error) {
return &model.GetTTLParams{Type: typeTTL}, nil
}
func parseSetApdexScoreRequest(r *http.Request) (*types.ApdexSettings, error) {
var req types.ApdexSettings
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return &req, nil
}
func parseAggregateAttributeRequest(r *http.Request) (*v3.AggregateAttributeRequest, error) {
var req v3.AggregateAttributeRequest

View File

@ -20,7 +20,6 @@ import (
"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/cloudintegrations"
"github.com/SigNoz/signoz/pkg/query-service/app/dashboards"
"github.com/SigNoz/signoz/pkg/query-service/app/integrations"
"github.com/SigNoz/signoz/pkg/query-service/app/logparsingpipeline"
"github.com/SigNoz/signoz/pkg/query-service/app/opamp"
@ -34,9 +33,7 @@ import (
"github.com/soheilhy/cmux"
"github.com/SigNoz/signoz/pkg/cache"
"github.com/SigNoz/signoz/pkg/query-service/app/explorer"
"github.com/SigNoz/signoz/pkg/query-service/constants"
"github.com/SigNoz/signoz/pkg/query-service/dao"
"github.com/SigNoz/signoz/pkg/query-service/featureManager"
"github.com/SigNoz/signoz/pkg/query-service/healthcheck"
"github.com/SigNoz/signoz/pkg/query-service/interfaces"
@ -84,19 +81,6 @@ func (s Server) HealthCheckStatus() chan healthcheck.Status {
// NewServer creates and initializes Server
func NewServer(serverOptions *ServerOptions) (*Server, error) {
var err error
if err := dao.InitDao(serverOptions.SigNoz.SQLStore); err != nil {
return nil, err
}
if err := dashboards.InitDB(serverOptions.SigNoz.SQLStore); err != nil {
return nil, err
}
if err := explorer.InitWithDSN(serverOptions.SigNoz.SQLStore); err != nil {
return nil, err
}
// initiate feature manager
fm := featureManager.StartManager()
@ -150,12 +134,17 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
telemetry.GetInstance().SetReader(reader)
telemetry.GetInstance().SetSqlStore(serverOptions.SigNoz.SQLStore)
telemetry.GetInstance().SetSavedViewsInfoCallback(telemetry.GetSavedViewsInfo)
telemetry.GetInstance().SetAlertsInfoCallback(telemetry.GetAlertsInfo)
telemetry.GetInstance().SetGetUsersCallback(telemetry.GetUsers)
telemetry.GetInstance().SetUserCountCallback(telemetry.GetUserCount)
telemetry.GetInstance().SetDashboardsInfoCallback(telemetry.GetDashboardsInfo)
quickfiltermodule := quickfilterscore.NewQuickFilters(quickfilterscore.NewStore(serverOptions.SigNoz.SQLStore))
quickFilter := quickfilter.NewAPI(quickfiltermodule)
apiHandler, err := NewAPIHandler(APIHandlerOpts{
Reader: reader,
PreferSpanMetrics: serverOptions.PreferSpanMetrics,
AppDao: dao.DB(),
RuleManager: rm,
FeatureFlags: fm,
IntegrationsController: integrationsController,

View File

@ -1,31 +0,0 @@
package dao
import (
"github.com/SigNoz/signoz/pkg/query-service/dao/sqlite"
"github.com/SigNoz/signoz/pkg/sqlstore"
)
var db ModelDao
func InitDao(sqlStore sqlstore.SQLStore) error {
var err error
db, err = sqlite.InitDB(sqlStore)
if err != nil {
return err
}
return nil
}
// SetDB is used by ee for setting modelDAO
func SetDB(m ModelDao) {
db = m
}
func DB() ModelDao {
if db == nil {
// Should never reach here
panic("GetDB called before initialization")
}
return db
}

View File

@ -1,22 +0,0 @@
package dao
import (
"context"
"github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/types"
)
type ModelDao interface {
Queries
Mutations
}
type Queries interface {
GetApdexSettings(ctx context.Context, orgID string, services []string) ([]types.ApdexSettings, *model.ApiError)
}
type Mutations interface {
UpdateUserRole(ctx context.Context, userId string, role types.Role) *model.ApiError
SetApdexSettings(ctx context.Context, orgID string, set *types.ApdexSettings) *model.ApiError
}

View File

@ -1,66 +0,0 @@
package sqlite
import (
"context"
"github.com/SigNoz/signoz/pkg/query-service/constants"
"github.com/SigNoz/signoz/pkg/query-service/telemetry"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types"
"github.com/pkg/errors"
"github.com/uptrace/bun"
)
type ModelDaoSqlite struct {
bundb *bun.DB
}
// InitDB sets up setting up the connection pool global variable.
func InitDB(sqlStore sqlstore.SQLStore) (*ModelDaoSqlite, error) {
mds := &ModelDaoSqlite{bundb: sqlStore.BunDB()}
ctx := context.Background()
if err := mds.initializeOrgPreferences(ctx); err != nil {
return nil, err
}
telemetry.GetInstance().SetGetUsersCallback(telemetry.GetUsers)
telemetry.GetInstance().SetUserCountCallback(telemetry.GetUserCount)
return mds, nil
}
// DB returns database connection
func (mds *ModelDaoSqlite) DB() *bun.DB {
return mds.bundb
}
// initializeOrgPreferences initializes in-memory telemetry settings. It is planned to have
// multiple orgs in the system. In case of multiple orgs, there will be separate instance
// of in-memory telemetry for each of the org, having their own settings. As of now, we only
// have one org so this method relies on the settings of this org to initialize the telemetry etc.
// TODO(Ahsan): Make it multi-tenant when we move to a system with multiple orgs.
func (mds *ModelDaoSqlite) initializeOrgPreferences(ctx context.Context) error {
// set anonymous setting as default in case of any failures to fetch UserPreference in below section
telemetry.GetInstance().SetTelemetryAnonymous(constants.DEFAULT_TELEMETRY_ANONYMOUS)
orgs, apiError := mds.GetOrgs(ctx)
if apiError != nil {
return apiError.Err
}
if len(orgs) > 1 {
return errors.Errorf("Found %d organizations, expected one or none.", len(orgs))
}
var org types.Organization
if len(orgs) == 1 {
org = orgs[0]
}
// set telemetry fields from userPreferences
telemetry.GetInstance().SetDistinctId(org.ID.StringValue())
return nil
}

View File

@ -1,34 +0,0 @@
package sqlite
import (
"context"
"github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/types"
)
func (mds *ModelDaoSqlite) GetOrgs(ctx context.Context) ([]types.Organization, *model.ApiError) {
var orgs []types.Organization
err := mds.bundb.NewSelect().
Model(&orgs).
Scan(ctx)
if err != nil {
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
}
return orgs, nil
}
func (mds *ModelDaoSqlite) UpdateUserRole(ctx context.Context, userId string, role types.Role) *model.ApiError {
_, err := mds.bundb.NewUpdate().
Model(&types.User{}).
Set("role = ?", role).
Where("id = ?", userId).
Exec(ctx)
if err != nil {
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
}
return nil
}

View File

@ -22,7 +22,6 @@ import (
"github.com/SigNoz/signoz/pkg/prometheus"
"github.com/SigNoz/signoz/pkg/query-service/interfaces"
"github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/query-service/telemetry"
"github.com/SigNoz/signoz/pkg/ruler/rulestore/sqlrulestore"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/telemetrystore"
@ -197,7 +196,6 @@ func NewManager(o *ManagerOptions) (*Manager, error) {
ruleStore := sqlrulestore.NewRuleStore(o.DBConn, o.SQLStore)
maintenanceStore := sqlrulestore.NewMaintenanceStore(o.SQLStore)
telemetry.GetInstance().SetAlertsInfoCallback(ruleStore.GetAlertsInfo)
m := &Manager{
tasks: map[string]Task{},
rules: map[string]Rule{},

View File

@ -0,0 +1,225 @@
package telemetry
import (
"context"
"encoding/json"
"slices"
"strings"
"github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types"
"go.uber.org/zap"
)
// GetDashboardsInfo returns analytics data for dashboards
func GetDashboardsInfo(ctx context.Context, sqlstore sqlstore.SQLStore) (*model.DashboardsInfo, error) {
dashboardsInfo := model.DashboardsInfo{}
// fetch dashboards from dashboard db
dashboards := []types.Dashboard{}
err := sqlstore.BunDB().NewSelect().Model(&dashboards).Scan(ctx)
if err != nil {
zap.L().Error("Error in processing sql query", zap.Error(err))
return &dashboardsInfo, err
}
totalDashboardsWithPanelAndName := 0
var dashboardNames []string
count := 0
queriesWithTagAttrs := 0
for _, dashboard := range dashboards {
if isDashboardWithPanelAndName(dashboard.Data) {
totalDashboardsWithPanelAndName = totalDashboardsWithPanelAndName + 1
}
dashboardName := extractDashboardName(dashboard.Data)
if dashboardName != "" {
dashboardNames = append(dashboardNames, dashboardName)
}
dashboardInfo := countPanelsInDashboard(dashboard.Data)
dashboardsInfo.LogsBasedPanels += dashboardInfo.LogsBasedPanels
dashboardsInfo.TracesBasedPanels += dashboardInfo.TracesBasedPanels
dashboardsInfo.MetricBasedPanels += dashboardInfo.MetricBasedPanels
dashboardsInfo.LogsPanelsWithAttrContainsOp += dashboardInfo.LogsPanelsWithAttrContainsOp
dashboardsInfo.DashboardsWithLogsChQuery += dashboardInfo.DashboardsWithLogsChQuery
dashboardsInfo.DashboardsWithTraceChQuery += dashboardInfo.DashboardsWithTraceChQuery
if isDashboardWithTSV2(dashboard.Data) {
count = count + 1
}
if isDashboardWithTagAttrs(dashboard.Data) {
queriesWithTagAttrs += 1
}
if dashboardInfo.DashboardsWithTraceChQuery > 0 {
dashboardsInfo.DashboardNamesWithTraceChQuery = append(dashboardsInfo.DashboardNamesWithTraceChQuery, dashboardName)
}
// check if dashboard is a has a log operator with contains
}
dashboardsInfo.DashboardNames = dashboardNames
dashboardsInfo.TotalDashboards = len(dashboards)
dashboardsInfo.TotalDashboardsWithPanelAndName = totalDashboardsWithPanelAndName
dashboardsInfo.QueriesWithTSV2 = count
dashboardsInfo.QueriesWithTagAttrs = queriesWithTagAttrs
return &dashboardsInfo, nil
}
func isDashboardWithTSV2(data map[string]interface{}) bool {
jsonData, err := json.Marshal(data)
if err != nil {
return false
}
return strings.Contains(string(jsonData), "time_series_v2")
}
func isDashboardWithTagAttrs(data map[string]interface{}) bool {
jsonData, err := json.Marshal(data)
if err != nil {
return false
}
return strings.Contains(string(jsonData), "span_attributes") ||
strings.Contains(string(jsonData), "tag_attributes")
}
func isDashboardWithLogsClickhouseQuery(data map[string]interface{}) bool {
jsonData, err := json.Marshal(data)
if err != nil {
return false
}
result := strings.Contains(string(jsonData), "signoz_logs.distributed_logs") ||
strings.Contains(string(jsonData), "signoz_logs.logs")
return result
}
func isDashboardWithTracesClickhouseQuery(data map[string]interface{}) bool {
jsonData, err := json.Marshal(data)
if err != nil {
return false
}
// also check if the query is actually active
str := string(jsonData)
result := strings.Contains(str, "signoz_traces.distributed_signoz_index_v2") ||
strings.Contains(str, "signoz_traces.distributed_signoz_spans") ||
strings.Contains(str, "signoz_traces.distributed_signoz_error_index_v2")
return result
}
func isDashboardWithPanelAndName(data map[string]interface{}) bool {
isDashboardName := false
isDashboardWithPanelAndName := false
if data != nil && data["title"] != nil && data["widgets"] != nil {
title, ok := data["title"].(string)
if ok && title != "Sample Title" {
isDashboardName = true
}
widgets, ok := data["widgets"]
if ok && isDashboardName {
data, ok := widgets.([]interface{})
if ok && len(data) > 0 {
isDashboardWithPanelAndName = true
}
}
}
return isDashboardWithPanelAndName
}
func extractDashboardName(data map[string]interface{}) string {
if data != nil && data["title"] != nil {
title, ok := data["title"].(string)
if ok {
return title
}
}
return ""
}
func checkLogPanelAttrContains(data map[string]interface{}) int {
var logsPanelsWithAttrContains int
filters, ok := data["filters"].(map[string]interface{})
if ok && filters["items"] != nil {
items, ok := filters["items"].([]interface{})
if ok {
for _, item := range items {
itemMap, ok := item.(map[string]interface{})
if ok {
opStr, ok := itemMap["op"].(string)
if ok {
if slices.Contains([]string{"contains", "ncontains", "like", "nlike"}, opStr) {
// check if it's not body
key, ok := itemMap["key"].(map[string]string)
if ok && key["key"] != "body" {
logsPanelsWithAttrContains++
}
}
}
}
}
}
}
return logsPanelsWithAttrContains
}
func countPanelsInDashboard(inputData map[string]interface{}) model.DashboardsInfo {
var logsPanelCount, tracesPanelCount, metricsPanelCount, logsPanelsWithAttrContains int
traceChQueryCount := 0
logChQueryCount := 0
// totalPanels := 0
if inputData != nil && inputData["widgets"] != nil {
widgets, ok := inputData["widgets"]
if ok {
data, ok := widgets.([]interface{})
if ok {
for _, widget := range data {
sData, ok := widget.(map[string]interface{})
if ok && sData["query"] != nil {
// totalPanels++
query, ok := sData["query"].(map[string]interface{})
if ok && query["queryType"] == "builder" && query["builder"] != nil {
builderData, ok := query["builder"].(map[string]interface{})
if ok && builderData["queryData"] != nil {
builderQueryData, ok := builderData["queryData"].([]interface{})
if ok {
for _, queryData := range builderQueryData {
data, ok := queryData.(map[string]interface{})
if ok {
if data["dataSource"] == "traces" {
tracesPanelCount++
} else if data["dataSource"] == "metrics" {
metricsPanelCount++
} else if data["dataSource"] == "logs" {
logsPanelCount++
logsPanelsWithAttrContains += checkLogPanelAttrContains(data)
}
}
}
}
}
} else if ok && query["queryType"] == "clickhouse_sql" && query["clickhouse_sql"] != nil {
if isDashboardWithLogsClickhouseQuery(inputData) {
logChQueryCount = 1
}
if isDashboardWithTracesClickhouseQuery(inputData) {
traceChQueryCount = 1
}
}
}
}
}
}
}
return model.DashboardsInfo{
LogsBasedPanels: logsPanelCount,
TracesBasedPanels: tracesPanelCount,
MetricBasedPanels: metricsPanelCount,
DashboardsWithLogsChQuery: logChQueryCount,
DashboardsWithTraceChQuery: traceChQueryCount,
LogsPanelsWithAttrContainsOp: logsPanelsWithAttrContains,
}
}

View File

@ -0,0 +1,140 @@
package telemetry
import (
"context"
"encoding/json"
"slices"
"strings"
"github.com/SigNoz/signoz/pkg/query-service/model"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types/alertmanagertypes"
"github.com/SigNoz/signoz/pkg/types/ruletypes"
"go.uber.org/zap"
)
func getChannels(ctx context.Context, sqlstore sqlstore.SQLStore) ([]*alertmanagertypes.Channel, error) {
channels := []*alertmanagertypes.Channel{}
if err := sqlstore.BunDB().NewSelect().Model(&channels).Scan(ctx); err != nil {
return nil, err
}
return channels, nil
}
func GetAlertsInfo(ctx context.Context, sqlstore sqlstore.SQLStore) (*model.AlertsInfo, error) {
alertsInfo := model.AlertsInfo{}
var alertsData []string
var alertNames []string
err := sqlstore.BunDB().NewSelect().Model((*ruletypes.Rule)(nil)).Column("data").Scan(ctx, &alertsData)
if err != nil {
zap.L().Error("Error in processing sql query", zap.Error(err))
return &alertsInfo, err
}
for _, alert := range alertsData {
var rule ruletypes.GettableRule
if strings.Contains(alert, "time_series_v2") {
alertsInfo.AlertsWithTSV2 = alertsInfo.AlertsWithTSV2 + 1
}
err = json.Unmarshal([]byte(alert), &rule)
if err != nil {
zap.L().Error("invalid rule data", zap.Error(err))
continue
}
alertNames = append(alertNames, rule.AlertName)
if rule.AlertType == ruletypes.AlertTypeLogs {
alertsInfo.LogsBasedAlerts = alertsInfo.LogsBasedAlerts + 1
if rule.RuleCondition != nil && rule.RuleCondition.CompositeQuery != nil {
if rule.RuleCondition.CompositeQuery.QueryType == v3.QueryTypeClickHouseSQL {
if strings.Contains(alert, "signoz_logs.distributed_logs") ||
strings.Contains(alert, "signoz_logs.logs") {
alertsInfo.AlertsWithLogsChQuery = alertsInfo.AlertsWithLogsChQuery + 1
}
}
}
for _, query := range rule.RuleCondition.CompositeQuery.BuilderQueries {
if rule.RuleCondition.CompositeQuery.QueryType == v3.QueryTypeBuilder {
if query.Filters != nil {
for _, item := range query.Filters.Items {
if slices.Contains([]string{"contains", "ncontains", "like", "nlike"}, string(item.Operator)) {
if item.Key.Key != "body" {
alertsInfo.AlertsWithLogsContainsOp += 1
}
}
}
}
}
}
} else if rule.AlertType == ruletypes.AlertTypeMetric {
alertsInfo.MetricBasedAlerts = alertsInfo.MetricBasedAlerts + 1
if rule.RuleCondition != nil && rule.RuleCondition.CompositeQuery != nil {
if rule.RuleCondition.CompositeQuery.QueryType == v3.QueryTypeBuilder {
alertsInfo.MetricsBuilderQueries = alertsInfo.MetricsBuilderQueries + 1
} else if rule.RuleCondition.CompositeQuery.QueryType == v3.QueryTypeClickHouseSQL {
alertsInfo.MetricsClickHouseQueries = alertsInfo.MetricsClickHouseQueries + 1
} else if rule.RuleCondition.CompositeQuery.QueryType == v3.QueryTypePromQL {
alertsInfo.MetricsPrometheusQueries = alertsInfo.MetricsPrometheusQueries + 1
for _, query := range rule.RuleCondition.CompositeQuery.PromQueries {
if strings.Contains(query.Query, "signoz_") {
alertsInfo.SpanMetricsPrometheusQueries = alertsInfo.SpanMetricsPrometheusQueries + 1
}
}
}
}
if rule.RuleType == ruletypes.RuleTypeAnomaly {
alertsInfo.AnomalyBasedAlerts = alertsInfo.AnomalyBasedAlerts + 1
}
} else if rule.AlertType == ruletypes.AlertTypeTraces {
alertsInfo.TracesBasedAlerts = alertsInfo.TracesBasedAlerts + 1
if rule.RuleCondition != nil && rule.RuleCondition.CompositeQuery != nil {
if rule.RuleCondition.CompositeQuery.QueryType == v3.QueryTypeClickHouseSQL {
if strings.Contains(alert, "signoz_traces.distributed_signoz_index_v2") ||
strings.Contains(alert, "signoz_traces.distributed_signoz_spans") ||
strings.Contains(alert, "signoz_traces.distributed_signoz_error_index_v2") {
alertsInfo.AlertsWithTraceChQuery = alertsInfo.AlertsWithTraceChQuery + 1
}
}
}
}
alertsInfo.TotalAlerts = alertsInfo.TotalAlerts + 1
if !rule.PostableRule.Disabled {
alertsInfo.TotalActiveAlerts = alertsInfo.TotalActiveAlerts + 1
}
}
alertsInfo.AlertNames = alertNames
channels, err := getChannels(ctx, sqlstore)
if err != nil {
return &alertsInfo, err
}
if channels != nil {
alertsInfo.TotalChannels = len(channels)
for _, channel := range channels {
if channel.Type == "slack" {
alertsInfo.SlackChannels = alertsInfo.SlackChannels + 1
}
if channel.Type == "webhook" {
alertsInfo.WebHookChannels = alertsInfo.WebHookChannels + 1
}
if channel.Type == "email" {
alertsInfo.EmailChannels = alertsInfo.EmailChannels + 1
}
if channel.Type == "pagerduty" {
alertsInfo.PagerDutyChannels = alertsInfo.PagerDutyChannels + 1
}
if channel.Type == "opsgenie" {
alertsInfo.OpsGenieChannels = alertsInfo.OpsGenieChannels + 1
}
if channel.Type == "msteams" {
alertsInfo.MSTeamsChannels = alertsInfo.MSTeamsChannels + 1
}
}
}
return &alertsInfo, nil
}

View File

@ -0,0 +1,87 @@
package telemetry
import (
"context"
"encoding/json"
"fmt"
"slices"
"strings"
"github.com/SigNoz/signoz/pkg/query-service/model"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types"
"go.uber.org/zap"
)
func GetViews(ctx context.Context, sqlstore sqlstore.SQLStore, orgID string) ([]*v3.SavedView, error) {
var views []types.SavedView
err := sqlstore.BunDB().NewSelect().Model(&views).Where("org_id = ?", orgID).Scan(ctx)
if err != nil {
return nil, fmt.Errorf("error in getting saved views: %s", err.Error())
}
var savedViews []*v3.SavedView
for _, view := range views {
var compositeQuery v3.CompositeQuery
err = json.Unmarshal([]byte(view.Data), &compositeQuery)
if err != nil {
return nil, fmt.Errorf("error in unmarshalling explorer query data: %s", err.Error())
}
savedViews = append(savedViews, &v3.SavedView{
ID: view.ID,
Name: view.Name,
Category: view.Category,
CreatedAt: view.CreatedAt,
CreatedBy: view.CreatedBy,
UpdatedAt: view.UpdatedAt,
UpdatedBy: view.UpdatedBy,
Tags: strings.Split(view.Tags, ","),
SourcePage: view.SourcePage,
CompositeQuery: &compositeQuery,
ExtraData: view.ExtraData,
})
}
return savedViews, nil
}
func GetSavedViewsInfo(ctx context.Context, sqlstore sqlstore.SQLStore) (*model.SavedViewsInfo, error) {
savedViewsInfo := model.SavedViewsInfo{}
// get single org ID from db
var orgIDs []string
err := sqlstore.BunDB().NewSelect().Model((*types.Organization)(nil)).Column("id").Scan(ctx, &orgIDs)
if err != nil {
return nil, fmt.Errorf("error in getting org IDs: %s", err.Error())
}
if len(orgIDs) != 1 {
zap.S().Warn("GetSavedViewsInfo: Zero or multiple org IDs found in the database", zap.Int("orgIDs", len(orgIDs)))
return &savedViewsInfo, nil
}
savedViews, err := GetViews(ctx, sqlstore, orgIDs[0])
if err != nil {
zap.S().Debug("Error in fetching saved views info: ", err)
return &savedViewsInfo, err
}
savedViewsInfo.TotalSavedViews = len(savedViews)
for _, view := range savedViews {
if view.SourcePage == "traces" {
savedViewsInfo.TracesSavedViews += 1
} else if view.SourcePage == "logs" {
savedViewsInfo.LogsSavedViews += 1
for _, query := range view.CompositeQuery.BuilderQueries {
if query.Filters != nil {
for _, item := range query.Filters.Items {
if slices.Contains([]string{"contains", "ncontains", "like", "nlike"}, string(item.Operator)) {
if item.Key.Key != "body" {
savedViewsInfo.LogsSavedViewWithContainsOp += 1
}
}
}
}
}
}
}
return &savedViewsInfo, nil
}

View File

@ -195,7 +195,6 @@ type Telemetry struct {
userEmail string
isEnabled bool
isAnonymous bool
distinctId string
reader interfaces.Reader
sqlStore sqlstore.SQLStore
companyDomain string
@ -206,14 +205,14 @@ type Telemetry struct {
patTokenUser bool
mutex sync.RWMutex
alertsInfoCallback func(ctx context.Context) (*model.AlertsInfo, error)
alertsInfoCallback func(ctx context.Context, store sqlstore.SQLStore) (*model.AlertsInfo, error)
userCountCallback func(ctx context.Context, store sqlstore.SQLStore) (int, error)
getUsersCallback func(ctx context.Context, store sqlstore.SQLStore) ([]TelemetryUser, error)
dashboardsInfoCallback func(ctx context.Context) (*model.DashboardsInfo, error)
savedViewsInfoCallback func(ctx context.Context) (*model.SavedViewsInfo, error)
dashboardsInfoCallback func(ctx context.Context, store sqlstore.SQLStore) (*model.DashboardsInfo, error)
savedViewsInfoCallback func(ctx context.Context, store sqlstore.SQLStore) (*model.SavedViewsInfo, error)
}
func (a *Telemetry) SetAlertsInfoCallback(callback func(ctx context.Context) (*model.AlertsInfo, error)) {
func (a *Telemetry) SetAlertsInfoCallback(callback func(ctx context.Context, store sqlstore.SQLStore) (*model.AlertsInfo, error)) {
a.alertsInfoCallback = callback
}
@ -225,11 +224,11 @@ func (a *Telemetry) SetGetUsersCallback(callback func(ctx context.Context, store
a.getUsersCallback = callback
}
func (a *Telemetry) SetSavedViewsInfoCallback(callback func(ctx context.Context) (*model.SavedViewsInfo, error)) {
func (a *Telemetry) SetSavedViewsInfoCallback(callback func(ctx context.Context, store sqlstore.SQLStore) (*model.SavedViewsInfo, error)) {
a.savedViewsInfoCallback = callback
}
func (a *Telemetry) SetDashboardsInfoCallback(callback func(ctx context.Context) (*model.DashboardsInfo, error)) {
func (a *Telemetry) SetDashboardsInfoCallback(callback func(ctx context.Context, store sqlstore.SQLStore) (*model.DashboardsInfo, error)) {
a.dashboardsInfoCallback = callback
}
@ -352,14 +351,14 @@ func createTelemetry() {
}
}
alertsInfo, err := telemetry.alertsInfoCallback(ctx)
alertsInfo, err := telemetry.alertsInfoCallback(ctx, telemetry.sqlStore)
if err != nil {
telemetry.SendEvent(TELEMETRY_EVENT_DASHBOARDS_ALERTS, map[string]interface{}{"error": err.Error()}, "", true, false)
}
if err == nil {
dashboardsInfo, err := telemetry.dashboardsInfoCallback(ctx)
dashboardsInfo, err := telemetry.dashboardsInfoCallback(ctx, telemetry.sqlStore)
if err == nil {
savedViewsInfo, err := telemetry.savedViewsInfoCallback(ctx)
savedViewsInfo, err := telemetry.savedViewsInfoCallback(ctx, telemetry.sqlStore)
if err == nil {
dashboardsAlertsData := map[string]interface{}{
"totalDashboards": dashboardsInfo.TotalDashboards,
@ -739,7 +738,7 @@ func (a *Telemetry) SendEvent(event string, data map[string]interface{}, userEma
userId := a.ipAddress
if a.isTelemetryAnonymous() || userId == IP_NOT_FOUND_PLACEHOLDER {
userId = a.GetDistinctId()
userId = "anonymous"
}
// check if event is part of SAAS_EVENTS_LIST
@ -774,13 +773,6 @@ func (a *Telemetry) SendEvent(event string, data map[string]interface{}, userEma
}
}
func (a *Telemetry) GetDistinctId() string {
return a.distinctId
}
func (a *Telemetry) SetDistinctId(distinctId string) {
a.distinctId = distinctId
}
func (a *Telemetry) isTelemetryAnonymous() bool {
return a.isAnonymous
}

View File

@ -19,7 +19,6 @@ import (
"github.com/SigNoz/signoz/pkg/modules/user/impluser"
"github.com/SigNoz/signoz/pkg/query-service/app"
"github.com/SigNoz/signoz/pkg/query-service/constants"
"github.com/SigNoz/signoz/pkg/query-service/dao"
"github.com/SigNoz/signoz/pkg/query-service/featureManager"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/query-service/utils"
@ -311,7 +310,6 @@ func NewFilterSuggestionsTestBed(t *testing.T) *FilterSuggestionsTestBed {
apiHandler, err := app.NewAPIHandler(app.APIHandlerOpts{
Reader: reader,
AppDao: dao.DB(),
FeatureFlags: fm,
JWT: jwt,
Signoz: &signoz.SigNoz{

View File

@ -21,7 +21,6 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/app/opamp"
opampModel "github.com/SigNoz/signoz/pkg/query-service/app/opamp/model"
"github.com/SigNoz/signoz/pkg/query-service/constants"
"github.com/SigNoz/signoz/pkg/query-service/dao"
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/utils"
@ -484,7 +483,6 @@ func NewTestbedWithoutOpamp(t *testing.T, sqlStore sqlstore.SQLStore) *LogPipeli
quickFilterModule := quickfilter.NewAPI(quickfilterscore.NewQuickFilters(quickfilterscore.NewStore(sqlStore)))
apiHandler, err := app.NewAPIHandler(app.APIHandlerOpts{
AppDao: dao.DB(),
LogsParsingPipelineController: controller,
JWT: jwt,
Signoz: &signoz.SigNoz{

View File

@ -20,7 +20,6 @@ import (
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
"github.com/SigNoz/signoz/pkg/query-service/app"
"github.com/SigNoz/signoz/pkg/query-service/app/cloudintegrations"
"github.com/SigNoz/signoz/pkg/query-service/dao"
"github.com/SigNoz/signoz/pkg/query-service/featureManager"
"github.com/SigNoz/signoz/pkg/query-service/utils"
"github.com/SigNoz/signoz/pkg/sqlstore"
@ -375,7 +374,6 @@ func NewCloudIntegrationsTestBed(t *testing.T, testDB sqlstore.SQLStore) *CloudI
apiHandler, err := app.NewAPIHandler(app.APIHandlerOpts{
Reader: reader,
AppDao: dao.DB(),
CloudIntegrationsController: controller,
FeatureFlags: fm,
JWT: jwt,

View File

@ -19,7 +19,6 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/app"
"github.com/SigNoz/signoz/pkg/query-service/app/cloudintegrations"
"github.com/SigNoz/signoz/pkg/query-service/app/integrations"
"github.com/SigNoz/signoz/pkg/query-service/dao"
"github.com/SigNoz/signoz/pkg/query-service/featureManager"
"github.com/SigNoz/signoz/pkg/query-service/model"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
@ -582,7 +581,6 @@ func NewIntegrationsTestBed(t *testing.T, testDB sqlstore.SQLStore) *Integration
apiHandler, err := app.NewAPIHandler(app.APIHandlerOpts{
Reader: reader,
AppDao: dao.DB(),
IntegrationsController: controller,
FeatureFlags: fm,
JWT: jwt,

View File

@ -7,8 +7,6 @@ import (
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/factory/factorytest"
"github.com/SigNoz/signoz/pkg/query-service/app/dashboards"
"github.com/SigNoz/signoz/pkg/query-service/dao"
"github.com/SigNoz/signoz/pkg/sqlmigration"
"github.com/SigNoz/signoz/pkg/sqlmigrator"
"github.com/SigNoz/signoz/pkg/sqlstore"
@ -83,12 +81,5 @@ func NewTestSqliteDB(t *testing.T) (sqlStore sqlstore.SQLStore, testDBFilePath s
func NewQueryServiceDBForTests(t *testing.T) sqlstore.SQLStore {
sqlStore, _ := NewTestSqliteDB(t)
err := dao.InitDao(sqlStore)
if err != nil {
t.Fatalf("could not initialize dao: %v", err)
}
_ = dashboards.InitDB(sqlStore)
return sqlStore
}

View File

@ -2,12 +2,7 @@ package sqlrulestore
import (
"context"
"encoding/json"
"slices"
"strings"
"github.com/SigNoz/signoz/pkg/query-service/model"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types"
ruletypes "github.com/SigNoz/signoz/pkg/types/ruletypes"
@ -151,134 +146,3 @@ func (r *rule) ListOrgs(ctx context.Context) ([]valuer.UUID, error) {
return orgIDs, nil
}
func (r *rule) getChannels() (*[]model.ChannelItem, *model.ApiError) {
channels := []model.ChannelItem{}
query := "SELECT id, created_at, updated_at, name, type, data FROM notification_channel"
err := r.Select(&channels, query)
zap.L().Info(query)
if err != nil {
zap.L().Error("Error in processing sql query", zap.Error(err))
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
}
return &channels, nil
}
func (r *rule) GetAlertsInfo(ctx context.Context) (*model.AlertsInfo, error) {
alertsInfo := model.AlertsInfo{}
// fetch alerts from rules db
query := "SELECT data FROM rule"
var alertsData []string
var alertNames []string
err := r.Select(&alertsData, query)
if err != nil {
zap.L().Error("Error in processing sql query", zap.Error(err))
return &alertsInfo, err
}
for _, alert := range alertsData {
var rule ruletypes.GettableRule
if strings.Contains(alert, "time_series_v2") {
alertsInfo.AlertsWithTSV2 = alertsInfo.AlertsWithTSV2 + 1
}
err = json.Unmarshal([]byte(alert), &rule)
if err != nil {
zap.L().Error("invalid rule data", zap.Error(err))
continue
}
alertNames = append(alertNames, rule.AlertName)
if rule.AlertType == ruletypes.AlertTypeLogs {
alertsInfo.LogsBasedAlerts = alertsInfo.LogsBasedAlerts + 1
if rule.RuleCondition != nil && rule.RuleCondition.CompositeQuery != nil {
if rule.RuleCondition.CompositeQuery.QueryType == v3.QueryTypeClickHouseSQL {
if strings.Contains(alert, "signoz_logs.distributed_logs") ||
strings.Contains(alert, "signoz_logs.logs") {
alertsInfo.AlertsWithLogsChQuery = alertsInfo.AlertsWithLogsChQuery + 1
}
}
}
for _, query := range rule.RuleCondition.CompositeQuery.BuilderQueries {
if rule.RuleCondition.CompositeQuery.QueryType == v3.QueryTypeBuilder {
if query.Filters != nil {
for _, item := range query.Filters.Items {
if slices.Contains([]string{"contains", "ncontains", "like", "nlike"}, string(item.Operator)) {
if item.Key.Key != "body" {
alertsInfo.AlertsWithLogsContainsOp += 1
}
}
}
}
}
}
} else if rule.AlertType == ruletypes.AlertTypeMetric {
alertsInfo.MetricBasedAlerts = alertsInfo.MetricBasedAlerts + 1
if rule.RuleCondition != nil && rule.RuleCondition.CompositeQuery != nil {
if rule.RuleCondition.CompositeQuery.QueryType == v3.QueryTypeBuilder {
alertsInfo.MetricsBuilderQueries = alertsInfo.MetricsBuilderQueries + 1
} else if rule.RuleCondition.CompositeQuery.QueryType == v3.QueryTypeClickHouseSQL {
alertsInfo.MetricsClickHouseQueries = alertsInfo.MetricsClickHouseQueries + 1
} else if rule.RuleCondition.CompositeQuery.QueryType == v3.QueryTypePromQL {
alertsInfo.MetricsPrometheusQueries = alertsInfo.MetricsPrometheusQueries + 1
for _, query := range rule.RuleCondition.CompositeQuery.PromQueries {
if strings.Contains(query.Query, "signoz_") {
alertsInfo.SpanMetricsPrometheusQueries = alertsInfo.SpanMetricsPrometheusQueries + 1
}
}
}
}
if rule.RuleType == ruletypes.RuleTypeAnomaly {
alertsInfo.AnomalyBasedAlerts = alertsInfo.AnomalyBasedAlerts + 1
}
} else if rule.AlertType == ruletypes.AlertTypeTraces {
alertsInfo.TracesBasedAlerts = alertsInfo.TracesBasedAlerts + 1
if rule.RuleCondition != nil && rule.RuleCondition.CompositeQuery != nil {
if rule.RuleCondition.CompositeQuery.QueryType == v3.QueryTypeClickHouseSQL {
if strings.Contains(alert, "signoz_traces.distributed_signoz_index_v2") ||
strings.Contains(alert, "signoz_traces.distributed_signoz_spans") ||
strings.Contains(alert, "signoz_traces.distributed_signoz_error_index_v2") {
alertsInfo.AlertsWithTraceChQuery = alertsInfo.AlertsWithTraceChQuery + 1
}
}
}
}
alertsInfo.TotalAlerts = alertsInfo.TotalAlerts + 1
if !rule.PostableRule.Disabled {
alertsInfo.TotalActiveAlerts = alertsInfo.TotalActiveAlerts + 1
}
}
alertsInfo.AlertNames = alertNames
channels, _ := r.getChannels()
if channels != nil {
alertsInfo.TotalChannels = len(*channels)
for _, channel := range *channels {
if channel.Type == "slack" {
alertsInfo.SlackChannels = alertsInfo.SlackChannels + 1
}
if channel.Type == "webhook" {
alertsInfo.WebHookChannels = alertsInfo.WebHookChannels + 1
}
if channel.Type == "email" {
alertsInfo.EmailChannels = alertsInfo.EmailChannels + 1
}
if channel.Type == "pagerduty" {
alertsInfo.PagerDutyChannels = alertsInfo.PagerDutyChannels + 1
}
if channel.Type == "opsgenie" {
alertsInfo.OpsGenieChannels = alertsInfo.OpsGenieChannels + 1
}
if channel.Type == "msteams" {
alertsInfo.MSTeamsChannels = alertsInfo.MSTeamsChannels + 1
}
}
}
return &alertsInfo, nil
}

View File

@ -1,10 +1,16 @@
package signoz
import (
"github.com/SigNoz/signoz/pkg/modules/apdex"
"github.com/SigNoz/signoz/pkg/modules/apdex/implapdex"
"github.com/SigNoz/signoz/pkg/modules/dashboard"
"github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
"github.com/SigNoz/signoz/pkg/modules/preference"
"github.com/SigNoz/signoz/pkg/modules/preference/implpreference"
"github.com/SigNoz/signoz/pkg/modules/savedview"
"github.com/SigNoz/signoz/pkg/modules/savedview/implsavedview"
"github.com/SigNoz/signoz/pkg/modules/user"
)
@ -12,6 +18,9 @@ type Handlers struct {
Organization organization.Handler
Preference preference.Handler
User user.Handler
SavedView savedview.Handler
Apdex apdex.Handler
Dashboard dashboard.Handler
}
func NewHandlers(modules Modules, user user.Handler) Handlers {
@ -19,5 +28,8 @@ func NewHandlers(modules Modules, user user.Handler) Handlers {
Organization: implorganization.NewHandler(modules.Organization),
Preference: implpreference.NewHandler(modules.Preference),
User: user,
SavedView: implsavedview.NewHandler(modules.SavedView),
Apdex: implapdex.NewHandler(modules.Apdex),
Dashboard: impldashboard.NewHandler(modules.Dashboard),
}
}

View File

@ -1,10 +1,16 @@
package signoz
import (
"github.com/SigNoz/signoz/pkg/modules/apdex"
"github.com/SigNoz/signoz/pkg/modules/apdex/implapdex"
"github.com/SigNoz/signoz/pkg/modules/dashboard"
"github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
"github.com/SigNoz/signoz/pkg/modules/preference"
"github.com/SigNoz/signoz/pkg/modules/preference/implpreference"
"github.com/SigNoz/signoz/pkg/modules/savedview"
"github.com/SigNoz/signoz/pkg/modules/savedview/implsavedview"
"github.com/SigNoz/signoz/pkg/modules/user"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types/preferencetypes"
@ -14,6 +20,9 @@ type Modules struct {
Organization organization.Module
Preference preference.Module
User user.Module
SavedView savedview.Module
Apdex apdex.Module
Dashboard dashboard.Module
}
func NewModules(sqlstore sqlstore.SQLStore, user user.Module) Modules {
@ -21,5 +30,8 @@ func NewModules(sqlstore sqlstore.SQLStore, user user.Module) Modules {
Organization: implorganization.NewModule(implorganization.NewStore(sqlstore)),
Preference: implpreference.NewModule(implpreference.NewStore(sqlstore), preferencetypes.NewDefaultPreferenceMap()),
User: user,
SavedView: implsavedview.NewModule(sqlstore),
Apdex: implapdex.NewModule(sqlstore),
Dashboard: impldashboard.NewModule(sqlstore),
}
}

View File

@ -3,7 +3,6 @@ package ruletypes
import (
"context"
"github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/uptrace/bun"
@ -32,6 +31,5 @@ type RuleStore interface {
GetStoredRules(context.Context, string) ([]*Rule, error)
GetStoredRule(context.Context, valuer.UUID) (*Rule, error)
GetRuleUUID(context.Context, int) (*RuleHistory, error)
GetAlertsInfo(context.Context) (*model.AlertsInfo, error)
ListOrgs(context.Context) ([]valuer.UUID, error)
}