feat: trace funnel state management

Signed-off-by: Shivanshu Raj Shrivastava <shivanshu1333@gmail.com>
This commit is contained in:
Shivanshu Raj Shrivastava 2025-05-22 13:38:46 +05:30
parent 14ce7f80e2
commit 5e0d6110b5
No known key found for this signature in database
GPG Key ID: D34D26C62AC3E9AE
11 changed files with 138 additions and 18 deletions

View File

@ -38,7 +38,9 @@ func (module *module) Create(ctx context.Context, timestamp int64, name string,
// Set up the user relationship
funnel.CreatedByUser = &types.User{
ID: userID,
Identifiable: types.Identifiable{
ID: valuer.MustNewUUID(userID),
},
}
if err := module.store.Create(ctx, funnel); err != nil {

View File

@ -32,7 +32,7 @@ func (store *store) Create(ctx context.Context, funnel *traceFunnels.Funnel) err
// Set created_by if CreatedByUser is present
if funnel.CreatedByUser != nil {
funnel.CreatedBy = funnel.CreatedByUser.ID
funnel.CreatedBy = funnel.CreatedByUser.Identifiable.ID.String()
}
_, err := store.

View File

@ -8,7 +8,7 @@ import (
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/types/authtypes"
tracefunnel "github.com/SigNoz/signoz/pkg/types/tracefunnel"
"github.com/SigNoz/signoz/pkg/types/tracefunnel"
"github.com/SigNoz/signoz/pkg/valuer"
)

View File

@ -8,7 +8,7 @@ import (
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/authtypes"
tracefunnel "github.com/SigNoz/signoz/pkg/types/tracefunnel"
"github.com/SigNoz/signoz/pkg/types/tracefunnel"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/stretchr/testify/assert"
)
@ -452,7 +452,7 @@ func TestConstructFunnelResponse(t *testing.T) {
},
},
CreatedByUser: &types.User{
ID: "user-123",
Identifiable: types.Identifiable{ID: valuer.MustNewUUID("user-123")},
Email: "funnel@example.com",
},
Steps: []tracefunnel.FunnelStep{

View File

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

View File

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

View File

@ -13,6 +13,8 @@ import (
"github.com/SigNoz/signoz/pkg/modules/quickfilter/implquickfilter"
"github.com/SigNoz/signoz/pkg/modules/savedview"
"github.com/SigNoz/signoz/pkg/modules/savedview/implsavedview"
"github.com/SigNoz/signoz/pkg/modules/tracefunnel"
"github.com/SigNoz/signoz/pkg/modules/tracefunnel/impltracefunnel"
"github.com/SigNoz/signoz/pkg/modules/user"
"github.com/SigNoz/signoz/pkg/modules/user/impluser"
"github.com/SigNoz/signoz/pkg/modules/tracefunnel"
@ -23,6 +25,7 @@ type Handlers struct {
Organization organization.Handler
Preference preference.Handler
User user.Handler
TraceFunnel tracefunnel.Handler
SavedView savedview.Handler
Apdex apdex.Handler
Dashboard dashboard.Handler

View File

@ -16,10 +16,10 @@ import (
"github.com/SigNoz/signoz/pkg/modules/quickfilter/implquickfilter"
"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/modules/user/impluser"
"github.com/SigNoz/signoz/pkg/modules/tracefunnel"
"github.com/SigNoz/signoz/pkg/modules/tracefunnel/impltracefunnel"
"github.com/SigNoz/signoz/pkg/modules/user"
"github.com/SigNoz/signoz/pkg/modules/user/impluser"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/types/preferencetypes"

View File

@ -83,6 +83,7 @@ func NewSQLMigrationProviderFactories(sqlstore sqlstore.SQLStore) factory.NamedM
sqlmigration.NewDropGroupsFactory(sqlstore),
sqlmigration.NewCreateQuickFiltersFactory(sqlstore),
sqlmigration.NewUpdateQuickFiltersFactory(sqlstore),
sqlmigration.NewAddTraceFunnelsFactory(sqlstore),
sqlmigration.NewAuthRefactorFactory(sqlstore),
sqlmigration.NewUpdateLicenseFactory(sqlstore),
sqlmigration.NewMigratePATToFactorAPIKey(sqlstore),

View File

@ -0,0 +1,114 @@
package sqlmigration
import (
"context"
"fmt"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/sqlstore"
traceFunnels "github.com/SigNoz/signoz/pkg/types/tracefunnel"
"github.com/uptrace/bun"
"github.com/uptrace/bun/migrate"
)
type addTraceFunnels struct {
sqlstore sqlstore.SQLStore
}
func NewAddTraceFunnelsFactory(sqlstore sqlstore.SQLStore) factory.ProviderFactory[SQLMigration, Config] {
return factory.
NewProviderFactory(factory.
MustNewName("add_trace_funnels"),
func(ctx context.Context, providerSettings factory.ProviderSettings, config Config) (SQLMigration, error) {
return newAddTraceFunnels(ctx, providerSettings, config, sqlstore)
})
}
func newAddTraceFunnels(_ context.Context, _ factory.ProviderSettings, _ Config, sqlstore sqlstore.SQLStore) (SQLMigration, error) {
return &addTraceFunnels{sqlstore: sqlstore}, nil
}
func (migration *addTraceFunnels) Register(migrations *migrate.Migrations) error {
if err := migrations.
Register(migration.Up, migration.Down); err != nil {
return err
}
return nil
}
func (migration *addTraceFunnels) Up(ctx context.Context, db *bun.DB) error {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()
// Create trace_funnel table with foreign key constraint inline
_, err = tx.NewCreateTable().
Model((*traceFunnels.Funnel)(nil)).
ForeignKey(`("org_id") REFERENCES "organizations" ("id") ON DELETE CASCADE`).
IfNotExists().
Exec(ctx)
if err != nil {
return fmt.Errorf("failed to create trace_funnel table: %v", err)
}
// Add unique constraint for org_id and name
//_, err = tx.NewRaw(`
// CREATE UNIQUE INDEX IF NOT EXISTS idx_trace_funnel_org_id_name
// ON trace_funnel (org_id, name)
//`).Exec(ctx)
//if err != nil {
// return fmt.Errorf("failed to create unique constraint: %v", err)
//}
// Create indexes
_, err = tx.NewCreateIndex().
Model((*traceFunnels.Funnel)(nil)).
Index("idx_trace_funnel_org_id").
Column("org_id").
IfNotExists().
Exec(ctx)
if err != nil {
return fmt.Errorf("failed to create org_id index: %v", err)
}
_, err = tx.NewCreateIndex().
Model((*traceFunnels.Funnel)(nil)).
Index("idx_trace_funnel_created_at").
Column("created_at").
IfNotExists().
Exec(ctx)
if err != nil {
return fmt.Errorf("failed to create created_at index: %v", err)
}
if err := tx.Commit(); err != nil {
return err
}
return nil
}
func (migration *addTraceFunnels) Down(ctx context.Context, db *bun.DB) error {
//tx, err := db.BeginTx(ctx, nil)
//if err != nil {
// return err
//}
//defer tx.Rollback()
//
//// Drop trace_funnel table
//_, err = tx.NewDropTable().
// Model((*traceFunnels.Funnel)(nil)).
// IfExists().
// Exec(ctx)
//if err != nil {
// return fmt.Errorf("failed to drop trace_funnel table: %v", err)
//}
//
//if err := tx.Commit(); err != nil {
// return err
//}
return nil
}

View File

@ -84,8 +84,8 @@ type TimeRange struct {
// StepTransitionRequest represents a request for step transition analytics
type StepTransitionRequest struct {
TimeRange
StepAOrder int64 `json:"step_a_order"`
StepBOrder int64 `json:"step_b_order"`
StepAOrder int64 `json:"step_start,omitempty"`
StepBOrder int64 `json:"step_end,omitempty"`
}
// UserInfo represents basic user information