diff --git a/pkg/modules/tracefunnel/impltracefunnel/handler.go b/pkg/modules/tracefunnel/impltracefunnel/handler.go index 4b7d636870..7de7147fca 100644 --- a/pkg/modules/tracefunnel/impltracefunnel/handler.go +++ b/pkg/modules/tracefunnel/impltracefunnel/handler.go @@ -21,13 +21,13 @@ func NewHandler(module tracefunnel.Module) tracefunnel.Handler { } func (handler *handler) New(rw http.ResponseWriter, r *http.Request) { - var req tf.FunnelRequest + var req tf.PostableFunnel if err := json.NewDecoder(r.Body).Decode(&req); err != nil { render.Error(rw, err) return } - claims, err := tracefunnel.GetClaims(r) + claims, err := tf.GetClaims(r) if err != nil { render.Error(rw, err) return @@ -41,24 +41,24 @@ func (handler *handler) New(rw http.ResponseWriter, r *http.Request) { return } - response := tracefunnel.ConstructFunnelResponse(funnel, claims) + response := tf.ConstructFunnelResponse(funnel, claims) render.Success(rw, http.StatusOK, response) } func (handler *handler) UpdateSteps(rw http.ResponseWriter, r *http.Request) { - var req tf.FunnelRequest + var req tf.PostableFunnel if err := json.NewDecoder(r.Body).Decode(&req); err != nil { render.Error(rw, err) return } - claims, err := tracefunnel.GetClaims(r) + claims, err := tf.GetClaims(r) if err != nil { render.Error(rw, err) return } - updatedAt, err := tracefunnel.ValidateAndConvertTimestamp(req.Timestamp) + updatedAt, err := tf.ValidateAndConvertTimestamp(req.Timestamp) if err != nil { render.Error(rw, err) return @@ -72,7 +72,7 @@ func (handler *handler) UpdateSteps(rw http.ResponseWriter, r *http.Request) { return } - steps, err := tracefunnel.ProcessFunnelSteps(req.Steps) + steps, err := tf.ProcessFunnelSteps(req.Steps) if err != nil { render.Error(rw, err) return @@ -104,24 +104,24 @@ func (handler *handler) UpdateSteps(rw http.ResponseWriter, r *http.Request) { return } - response := tracefunnel.ConstructFunnelResponse(updatedFunnel, claims) + response := tf.ConstructFunnelResponse(updatedFunnel, claims) render.Success(rw, http.StatusOK, response) } func (handler *handler) UpdateFunnel(rw http.ResponseWriter, r *http.Request) { - var req tf.FunnelRequest + var req tf.PostableFunnel if err := json.NewDecoder(r.Body).Decode(&req); err != nil { render.Error(rw, err) return } - claims, err := tracefunnel.GetClaims(r) + claims, err := tf.GetClaims(r) if err != nil { render.Error(rw, err) return } - updatedAt, err := tracefunnel.ValidateAndConvertTimestamp(req.Timestamp) + updatedAt, err := tf.ValidateAndConvertTimestamp(req.Timestamp) if err != nil { render.Error(rw, err) return @@ -163,12 +163,12 @@ func (handler *handler) UpdateFunnel(rw http.ResponseWriter, r *http.Request) { return } - response := tracefunnel.ConstructFunnelResponse(updatedFunnel, claims) + response := tf.ConstructFunnelResponse(updatedFunnel, claims) render.Success(rw, http.StatusOK, response) } func (handler *handler) List(rw http.ResponseWriter, r *http.Request) { - claims, err := tracefunnel.GetClaims(r) + claims, err := tf.GetClaims(r) if err != nil { render.Error(rw, err) return @@ -182,9 +182,9 @@ func (handler *handler) List(rw http.ResponseWriter, r *http.Request) { return } - var response []tf.FunnelResponse + var response []tf.GettableFunnel for _, f := range funnels { - response = append(response, tracefunnel.ConstructFunnelResponse(f, claims)) + response = append(response, tf.ConstructFunnelResponse(f, claims)) } render.Success(rw, http.StatusOK, response) @@ -202,8 +202,8 @@ func (handler *handler) Get(rw http.ResponseWriter, r *http.Request) { return } - claims, _ := tracefunnel.GetClaims(r) // Ignore error as email is optional - response := tracefunnel.ConstructFunnelResponse(funnel, claims) + claims, _ := tf.GetClaims(r) // Ignore error as email is optional + response := tf.ConstructFunnelResponse(funnel, claims) render.Success(rw, http.StatusOK, response) } @@ -222,7 +222,7 @@ func (handler *handler) Delete(rw http.ResponseWriter, r *http.Request) { } func (handler *handler) Save(rw http.ResponseWriter, r *http.Request) { - var req tf.FunnelRequest + var req tf.PostableFunnel if err := json.NewDecoder(r.Body).Decode(&req); err != nil { render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, @@ -230,7 +230,7 @@ func (handler *handler) Save(rw http.ResponseWriter, r *http.Request) { return } - claims, err := tracefunnel.GetClaims(r) + claims, err := tf.GetClaims(r) if err != nil { render.Error(rw, err) return @@ -247,14 +247,14 @@ func (handler *handler) Save(rw http.ResponseWriter, r *http.Request) { updateTimestamp := req.Timestamp if updateTimestamp == 0 { updateTimestamp = time.Now().UnixMilli() - } else if !tracefunnel.ValidateTimestampIsMilliseconds(updateTimestamp) { + } else if !tf.ValidateTimestampIsMilliseconds(updateTimestamp) { render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "timestamp must be in milliseconds format (13 digits)")) return } - updatedAt, err := tracefunnel.ValidateAndConvertTimestamp(updateTimestamp) + updatedAt, err := tf.ValidateAndConvertTimestamp(updateTimestamp) if err != nil { render.Error(rw, err) return @@ -279,7 +279,7 @@ func (handler *handler) Save(rw http.ResponseWriter, r *http.Request) { return } - resp := tf.FunnelResponse{ + resp := tf.GettableFunnel{ FunnelName: funnel.Name, CreatedAt: createdAtMillis, UpdatedAt: updatedAtMillis, diff --git a/pkg/modules/tracefunnel/impltracefunnel/handler_test.go b/pkg/modules/tracefunnel/impltracefunnel/handler_test.go index 513c6aaaea..f10615e45e 100644 --- a/pkg/modules/tracefunnel/impltracefunnel/handler_test.go +++ b/pkg/modules/tracefunnel/impltracefunnel/handler_test.go @@ -22,24 +22,24 @@ type MockModule struct { mock.Mock } -func (m *MockModule) Create(ctx context.Context, timestamp int64, name string, userID string, orgID string) (*traceFunnels.Funnel, error) { +func (m *MockModule) Create(ctx context.Context, timestamp int64, name string, userID string, orgID string) (*traceFunnels.StorableFunnel, error) { args := m.Called(ctx, timestamp, name, userID, orgID) - return args.Get(0).(*traceFunnels.Funnel), args.Error(1) + return args.Get(0).(*traceFunnels.StorableFunnel), args.Error(1) } -func (m *MockModule) Get(ctx context.Context, funnelID string) (*traceFunnels.Funnel, error) { +func (m *MockModule) Get(ctx context.Context, funnelID string) (*traceFunnels.StorableFunnel, error) { args := m.Called(ctx, funnelID) - return args.Get(0).(*traceFunnels.Funnel), args.Error(1) + return args.Get(0).(*traceFunnels.StorableFunnel), args.Error(1) } -func (m *MockModule) Update(ctx context.Context, funnel *traceFunnels.Funnel, userID string) error { +func (m *MockModule) Update(ctx context.Context, funnel *traceFunnels.StorableFunnel, userID string) error { args := m.Called(ctx, funnel, userID) return args.Error(0) } -func (m *MockModule) List(ctx context.Context, orgID string) ([]*traceFunnels.Funnel, error) { +func (m *MockModule) List(ctx context.Context, orgID string) ([]*traceFunnels.StorableFunnel, error) { args := m.Called(ctx, orgID) - return args.Get(0).([]*traceFunnels.Funnel), args.Error(1) + return args.Get(0).([]*traceFunnels.StorableFunnel), args.Error(1) } func (m *MockModule) Delete(ctx context.Context, funnelID string) error { @@ -47,7 +47,7 @@ func (m *MockModule) Delete(ctx context.Context, funnelID string) error { return args.Error(0) } -func (m *MockModule) Save(ctx context.Context, funnel *traceFunnels.Funnel, userID string, orgID string) error { +func (m *MockModule) Save(ctx context.Context, funnel *traceFunnels.StorableFunnel, userID string, orgID string) error { args := m.Called(ctx, funnel, userID, orgID) return args.Error(0) } @@ -61,7 +61,7 @@ func TestHandler_New(t *testing.T) { mockModule := new(MockModule) handler := NewHandler(mockModule) - reqBody := traceFunnels.FunnelRequest{ + reqBody := traceFunnels.PostableFunnel{ Name: "test-funnel", Timestamp: time.Now().UnixMilli(), } @@ -81,7 +81,7 @@ func TestHandler_New(t *testing.T) { rr := httptest.NewRecorder() funnelID := valuer.GenerateUUID() - expectedFunnel := &traceFunnels.Funnel{ + expectedFunnel := &traceFunnels.StorableFunnel{ BaseMetadata: traceFunnels.BaseMetadata{ Identifiable: types.Identifiable{ ID: funnelID, @@ -91,7 +91,7 @@ func TestHandler_New(t *testing.T) { }, } - mockModule.On("List", req.Context(), orgID).Return([]*traceFunnels.Funnel{}, nil) + mockModule.On("List", req.Context(), orgID).Return([]*traceFunnels.StorableFunnel{}, nil) mockModule.On("Create", req.Context(), reqBody.Timestamp, reqBody.Name, "user-123", orgID).Return(expectedFunnel, nil) handler.New(rr, req) @@ -100,7 +100,7 @@ func TestHandler_New(t *testing.T) { var response struct { Status string `json:"status"` - Data traceFunnels.FunnelResponse `json:"data"` + Data traceFunnels.GettableFunnel `json:"data"` } err := json.Unmarshal(rr.Body.Bytes(), &response) assert.NoError(t, err) @@ -120,10 +120,10 @@ func TestHandler_Update(t *testing.T) { funnelID := valuer.GenerateUUID() orgID := valuer.GenerateUUID().String() - reqBody := traceFunnels.FunnelRequest{ + reqBody := traceFunnels.PostableFunnel{ FunnelID: funnelID, Name: "updated-funnel", - Steps: []traceFunnels.FunnelStep{ + Steps: []*traceFunnels.FunnelStep{ { ID: valuer.GenerateUUID(), Name: "Step 1", @@ -161,7 +161,7 @@ func TestHandler_Update(t *testing.T) { rr := httptest.NewRecorder() // Set up mock expectations - existingFunnel := &traceFunnels.Funnel{ + existingFunnel := &traceFunnels.StorableFunnel{ BaseMetadata: traceFunnels.BaseMetadata{ Identifiable: types.Identifiable{ ID: funnelID, @@ -183,7 +183,7 @@ func TestHandler_Update(t *testing.T) { }, } - updatedFunnel := &traceFunnels.Funnel{ + updatedFunnel := &traceFunnels.StorableFunnel{ BaseMetadata: traceFunnels.BaseMetadata{ Identifiable: types.Identifiable{ ID: funnelID, @@ -209,9 +209,9 @@ func TestHandler_Update(t *testing.T) { // First Get call to validate the funnel exists mockModule.On("Get", req.Context(), funnelID.String()).Return(existingFunnel, nil).Once() // List call to check for name conflicts - mockModule.On("List", req.Context(), orgID).Return([]*traceFunnels.Funnel{}, nil).Once() + mockModule.On("List", req.Context(), orgID).Return([]*traceFunnels.StorableFunnel{}, nil).Once() // Update call to save the changes - mockModule.On("Update", req.Context(), mock.MatchedBy(func(f *traceFunnels.Funnel) bool { + mockModule.On("Update", req.Context(), mock.MatchedBy(func(f *traceFunnels.StorableFunnel) bool { return f.Name == reqBody.Name && f.ID.String() == funnelID.String() && len(f.Steps) == len(reqBody.Steps) && @@ -233,7 +233,7 @@ func TestHandler_Update(t *testing.T) { var response struct { Status string `json:"status"` - Data traceFunnels.FunnelResponse `json:"data"` + Data traceFunnels.GettableFunnel `json:"data"` } err = json.Unmarshal(rr.Body.Bytes(), &response) assert.NoError(t, err) @@ -261,7 +261,7 @@ func TestHandler_List(t *testing.T) { funnel1ID := valuer.GenerateUUID() funnel2ID := valuer.GenerateUUID() - expectedFunnels := []*traceFunnels.Funnel{ + expectedFunnels := []*traceFunnels.StorableFunnel{ { BaseMetadata: traceFunnels.BaseMetadata{ Identifiable: types.Identifiable{ @@ -290,7 +290,7 @@ func TestHandler_List(t *testing.T) { var response struct { Status string `json:"status"` - Data []traceFunnels.FunnelResponse `json:"data"` + Data []traceFunnels.GettableFunnel `json:"data"` } err := json.Unmarshal(rr.Body.Bytes(), &response) assert.NoError(t, err) @@ -312,7 +312,7 @@ func TestHandler_Get(t *testing.T) { rr := httptest.NewRecorder() - expectedFunnel := &traceFunnels.Funnel{ + expectedFunnel := &traceFunnels.StorableFunnel{ BaseMetadata: traceFunnels.BaseMetadata{ Identifiable: types.Identifiable{ ID: funnelID, @@ -330,7 +330,7 @@ func TestHandler_Get(t *testing.T) { var response struct { Status string `json:"status"` - Data traceFunnels.FunnelResponse `json:"data"` + Data traceFunnels.GettableFunnel `json:"data"` } err := json.Unmarshal(rr.Body.Bytes(), &response) assert.NoError(t, err) @@ -364,7 +364,7 @@ func TestHandler_Save(t *testing.T) { mockModule := new(MockModule) handler := NewHandler(mockModule) - reqBody := traceFunnels.FunnelRequest{ + reqBody := traceFunnels.PostableFunnel{ FunnelID: valuer.GenerateUUID(), Description: "updated description", Timestamp: time.Now().UnixMilli(), @@ -384,7 +384,7 @@ func TestHandler_Save(t *testing.T) { rr := httptest.NewRecorder() - existingFunnel := &traceFunnels.Funnel{ + existingFunnel := &traceFunnels.StorableFunnel{ BaseMetadata: traceFunnels.BaseMetadata{ Identifiable: types.Identifiable{ ID: reqBody.FunnelID, @@ -395,7 +395,7 @@ func TestHandler_Save(t *testing.T) { } mockModule.On("Get", req.Context(), reqBody.FunnelID.String()).Return(existingFunnel, nil) - mockModule.On("Save", req.Context(), mock.MatchedBy(func(f *traceFunnels.Funnel) bool { + mockModule.On("Save", req.Context(), mock.MatchedBy(func(f *traceFunnels.StorableFunnel) bool { return f.ID.String() == reqBody.FunnelID.String() && f.Name == existingFunnel.Name && f.Description == reqBody.Description && @@ -410,7 +410,7 @@ func TestHandler_Save(t *testing.T) { var response struct { Status string `json:"status"` - Data traceFunnels.FunnelResponse `json:"data"` + Data traceFunnels.GettableFunnel `json:"data"` } err := json.Unmarshal(rr.Body.Bytes(), &response) assert.NoError(t, err) diff --git a/pkg/modules/tracefunnel/impltracefunnel/module.go b/pkg/modules/tracefunnel/impltracefunnel/module.go index 346c6d569d..9deacf2c80 100644 --- a/pkg/modules/tracefunnel/impltracefunnel/module.go +++ b/pkg/modules/tracefunnel/impltracefunnel/module.go @@ -21,13 +21,13 @@ func NewModule(store traceFunnels.FunnelStore) tracefunnel.Module { } } -func (module *module) Create(ctx context.Context, timestamp int64, name string, userID string, orgID string) (*traceFunnels.Funnel, error) { +func (module *module) Create(ctx context.Context, timestamp int64, name string, userID string, orgID string) (*traceFunnels.StorableFunnel, error) { orgUUID, err := valuer.NewUUID(orgID) if err != nil { return nil, fmt.Errorf("invalid org ID: %v", err) } - funnel := &traceFunnels.Funnel{ + funnel := &traceFunnels.StorableFunnel{ BaseMetadata: traceFunnels.BaseMetadata{ Name: name, OrgID: orgUUID, @@ -43,6 +43,22 @@ func (module *module) Create(ctx context.Context, timestamp int64, name string, }, } + if funnel.ID.IsZero() { + funnel.ID = valuer.GenerateUUID() + } + + if funnel.CreatedAt.IsZero() { + funnel.CreatedAt = time.Now() + } + if funnel.UpdatedAt.IsZero() { + funnel.UpdatedAt = time.Now() + } + + // Set created_by if CreatedByUser is present + if funnel.CreatedByUser != nil { + funnel.CreatedBy = funnel.CreatedByUser.Identifiable.ID.String() + } + if err := module.store.Create(ctx, funnel); err != nil { return nil, fmt.Errorf("failed to create funnel: %v", err) } @@ -51,7 +67,7 @@ func (module *module) Create(ctx context.Context, timestamp int64, name string, } // Get gets a funnel by ID -func (module *module) Get(ctx context.Context, funnelID string) (*traceFunnels.Funnel, error) { +func (module *module) Get(ctx context.Context, funnelID string) (*traceFunnels.StorableFunnel, error) { uuid, err := valuer.NewUUID(funnelID) if err != nil { return nil, fmt.Errorf("invalid funnel ID: %v", err) @@ -60,13 +76,13 @@ func (module *module) Get(ctx context.Context, funnelID string) (*traceFunnels.F } // Update updates a funnel -func (module *module) Update(ctx context.Context, funnel *traceFunnels.Funnel, userID string) error { +func (module *module) Update(ctx context.Context, funnel *traceFunnels.StorableFunnel, userID string) error { funnel.UpdatedBy = userID return module.store.Update(ctx, funnel) } // List lists all funnels for an organization -func (module *module) List(ctx context.Context, orgID string) ([]*traceFunnels.Funnel, error) { +func (module *module) List(ctx context.Context, orgID string) ([]*traceFunnels.StorableFunnel, error) { orgUUID, err := valuer.NewUUID(orgID) if err != nil { return nil, fmt.Errorf("invalid org ID: %v", err) @@ -90,7 +106,7 @@ func (module *module) Delete(ctx context.Context, funnelID string) error { } // Save saves a funnel -func (module *module) Save(ctx context.Context, funnel *traceFunnels.Funnel, userID string, orgID string) error { +func (module *module) Save(ctx context.Context, funnel *traceFunnels.StorableFunnel, userID string, orgID string) error { orgUUID, err := valuer.NewUUID(orgID) if err != nil { return fmt.Errorf("invalid org ID: %v", err) diff --git a/pkg/modules/tracefunnel/impltracefunnel/store.go b/pkg/modules/tracefunnel/impltracefunnel/store.go index 01692c49ca..50e6ee1913 100644 --- a/pkg/modules/tracefunnel/impltracefunnel/store.go +++ b/pkg/modules/tracefunnel/impltracefunnel/store.go @@ -18,23 +18,7 @@ func NewStore(sqlstore sqlstore.SQLStore) traceFunnels.FunnelStore { return &store{sqlstore: sqlstore} } -func (store *store) Create(ctx context.Context, funnel *traceFunnels.Funnel) error { - if funnel.ID.IsZero() { - funnel.ID = valuer.GenerateUUID() - } - - if funnel.CreatedAt.IsZero() { - funnel.CreatedAt = time.Now() - } - if funnel.UpdatedAt.IsZero() { - funnel.UpdatedAt = time.Now() - } - - // Set created_by if CreatedByUser is present - if funnel.CreatedByUser != nil { - funnel.CreatedBy = funnel.CreatedByUser.Identifiable.ID.String() - } - +func (store *store) Create(ctx context.Context, funnel *traceFunnels.StorableFunnel) error { _, err := store. sqlstore. BunDB(). @@ -49,8 +33,8 @@ func (store *store) Create(ctx context.Context, funnel *traceFunnels.Funnel) err } // Get retrieves a funnel by ID -func (store *store) Get(ctx context.Context, uuid valuer.UUID) (*traceFunnels.Funnel, error) { - funnel := &traceFunnels.Funnel{} +func (store *store) Get(ctx context.Context, uuid valuer.UUID) (*traceFunnels.StorableFunnel, error) { + funnel := &traceFunnels.StorableFunnel{} err := store. sqlstore. BunDB(). @@ -66,7 +50,7 @@ func (store *store) Get(ctx context.Context, uuid valuer.UUID) (*traceFunnels.Fu } // Update updates an existing funnel -func (store *store) Update(ctx context.Context, funnel *traceFunnels.Funnel) error { +func (store *store) Update(ctx context.Context, funnel *traceFunnels.StorableFunnel) error { funnel.UpdatedAt = time.Now() _, err := store. @@ -83,8 +67,8 @@ func (store *store) Update(ctx context.Context, funnel *traceFunnels.Funnel) err } // List retrieves all funnels for a given organization -func (store *store) List(ctx context.Context, orgID valuer.UUID) ([]*traceFunnels.Funnel, error) { - var funnels []*traceFunnels.Funnel +func (store *store) List(ctx context.Context, orgID valuer.UUID) ([]*traceFunnels.StorableFunnel, error) { + var funnels []*traceFunnels.StorableFunnel err := store. sqlstore. BunDB(). @@ -105,7 +89,7 @@ func (store *store) Delete(ctx context.Context, uuid valuer.UUID) error { sqlstore. BunDB(). NewDelete(). - Model((*traceFunnels.Funnel)(nil)). + Model((*traceFunnels.StorableFunnel)(nil)). Where("id = ?", uuid).Exec(ctx) if err != nil { return fmt.Errorf("failed to delete funnel: %v", err) diff --git a/pkg/modules/tracefunnel/tracefunnel.go b/pkg/modules/tracefunnel/tracefunnel.go index 4d609544f7..4e115b782b 100644 --- a/pkg/modules/tracefunnel/tracefunnel.go +++ b/pkg/modules/tracefunnel/tracefunnel.go @@ -9,17 +9,17 @@ import ( // Module defines the interface for trace funnel operations type Module interface { - Create(ctx context.Context, timestamp int64, name string, userID string, orgID string) (*traceFunnels.Funnel, error) + Create(ctx context.Context, timestamp int64, name string, userID string, orgID string) (*traceFunnels.StorableFunnel, error) - Get(ctx context.Context, funnelID string) (*traceFunnels.Funnel, error) + Get(ctx context.Context, funnelID string) (*traceFunnels.StorableFunnel, error) - Update(ctx context.Context, funnel *traceFunnels.Funnel, userID string) error + Update(ctx context.Context, funnel *traceFunnels.StorableFunnel, userID string) error - List(ctx context.Context, orgID string) ([]*traceFunnels.Funnel, error) + List(ctx context.Context, orgID string) ([]*traceFunnels.StorableFunnel, error) Delete(ctx context.Context, funnelID string) error - Save(ctx context.Context, funnel *traceFunnels.Funnel, userID string, orgID string) error + Save(ctx context.Context, funnel *traceFunnels.StorableFunnel, userID string, orgID string) error GetFunnelMetadata(ctx context.Context, funnelID string) (int64, int64, string, error) } diff --git a/pkg/modules/tracefunnel/tracefunneltest/module_test.go b/pkg/modules/tracefunnel/tracefunneltest/module_test.go index 0f56d6dc5d..e88772541e 100644 --- a/pkg/modules/tracefunnel/tracefunneltest/module_test.go +++ b/pkg/modules/tracefunnel/tracefunneltest/module_test.go @@ -17,22 +17,22 @@ type MockStore struct { mock.Mock } -func (m *MockStore) Create(ctx context.Context, funnel *traceFunnels.Funnel) error { +func (m *MockStore) Create(ctx context.Context, funnel *traceFunnels.StorableFunnel) error { args := m.Called(ctx, funnel) return args.Error(0) } -func (m *MockStore) Get(ctx context.Context, uuid valuer.UUID) (*traceFunnels.Funnel, error) { +func (m *MockStore) Get(ctx context.Context, uuid valuer.UUID) (*traceFunnels.StorableFunnel, error) { args := m.Called(ctx, uuid) - return args.Get(0).(*traceFunnels.Funnel), args.Error(1) + return args.Get(0).(*traceFunnels.StorableFunnel), args.Error(1) } -func (m *MockStore) List(ctx context.Context, orgID valuer.UUID) ([]*traceFunnels.Funnel, error) { +func (m *MockStore) List(ctx context.Context, orgID valuer.UUID) ([]*traceFunnels.StorableFunnel, error) { args := m.Called(ctx, orgID) - return args.Get(0).([]*traceFunnels.Funnel), args.Error(1) + return args.Get(0).([]*traceFunnels.StorableFunnel), args.Error(1) } -func (m *MockStore) Update(ctx context.Context, funnel *traceFunnels.Funnel) error { +func (m *MockStore) Update(ctx context.Context, funnel *traceFunnels.StorableFunnel) error { args := m.Called(ctx, funnel) return args.Error(0) } @@ -52,7 +52,7 @@ func TestModule_Create(t *testing.T) { userID := valuer.GenerateUUID() orgID := valuer.GenerateUUID().String() - mockStore.On("Create", ctx, mock.MatchedBy(func(f *traceFunnels.Funnel) bool { + mockStore.On("Create", ctx, mock.MatchedBy(func(f *traceFunnels.StorableFunnel) bool { return f.Name == name && f.CreatedBy == userID.String() && f.OrgID.String() == orgID && @@ -79,7 +79,7 @@ func TestModule_Get(t *testing.T) { ctx := context.Background() funnelID := valuer.GenerateUUID().String() - expectedFunnel := &traceFunnels.Funnel{ + expectedFunnel := &traceFunnels.StorableFunnel{ BaseMetadata: traceFunnels.BaseMetadata{ Name: "test-funnel", }, @@ -100,7 +100,7 @@ func TestModule_Update(t *testing.T) { ctx := context.Background() userID := "user-123" - funnel := &traceFunnels.Funnel{ + funnel := &traceFunnels.StorableFunnel{ BaseMetadata: traceFunnels.BaseMetadata{ Name: "test-funnel", }, @@ -122,7 +122,7 @@ func TestModule_List(t *testing.T) { ctx := context.Background() orgID := valuer.GenerateUUID().String() orgUUID := valuer.MustNewUUID(orgID) - expectedFunnels := []*traceFunnels.Funnel{ + expectedFunnels := []*traceFunnels.StorableFunnel{ { BaseMetadata: traceFunnels.BaseMetadata{ Name: "funnel-1", @@ -169,7 +169,7 @@ func TestModule_Save(t *testing.T) { ctx := context.Background() userID := "user-123" orgID := valuer.GenerateUUID().String() - funnel := &traceFunnels.Funnel{ + funnel := &traceFunnels.StorableFunnel{ BaseMetadata: traceFunnels.BaseMetadata{ Name: "test-funnel", }, @@ -192,7 +192,7 @@ func TestModule_GetFunnelMetadata(t *testing.T) { ctx := context.Background() funnelID := valuer.GenerateUUID().String() now := time.Now() - expectedFunnel := &traceFunnels.Funnel{ + expectedFunnel := &traceFunnels.StorableFunnel{ BaseMetadata: traceFunnels.BaseMetadata{ Description: "test description", TimeAuditable: types.TimeAuditable{ diff --git a/pkg/sqlmigration/030_add_trace_funnels.go b/pkg/sqlmigration/030_add_trace_funnels.go deleted file mode 100644 index 12759bc0b6..0000000000 --- a/pkg/sqlmigration/030_add_trace_funnels.go +++ /dev/null @@ -1,111 +0,0 @@ -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"). - 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").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 -} diff --git a/pkg/sqlmigration/050_add_trace_funnels.go b/pkg/sqlmigration/050_add_trace_funnels.go index 1d8be2c34f..cf12de6f95 100644 --- a/pkg/sqlmigration/050_add_trace_funnels.go +++ b/pkg/sqlmigration/050_add_trace_funnels.go @@ -3,25 +3,54 @@ package sqlmigration import ( "context" "fmt" - "github.com/SigNoz/signoz/pkg/factory" + v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3" "github.com/SigNoz/signoz/pkg/sqlstore" - traceFunnels "github.com/SigNoz/signoz/pkg/types/tracefunnel" + "github.com/SigNoz/signoz/pkg/types" + "github.com/SigNoz/signoz/pkg/valuer" "github.com/uptrace/bun" "github.com/uptrace/bun/migrate" ) +type BaseMetadata struct { + types.Identifiable // funnel id + types.TimeAuditable + types.UserAuditable + Name string `json:"funnel_name" bun:"name,type:text,notnull"` // funnel name + Description string `json:"description" bun:"description,type:text"` // funnel description + OrgID valuer.UUID `json:"org_id" bun:"org_id,type:varchar,notnull"` +} + +// Funnel Core Data Structure (Funnel and FunnelStep) +type Funnel struct { + bun.BaseModel `bun:"table:trace_funnel"` + BaseMetadata + Steps []FunnelStep `json:"steps" bun:"steps,type:text,notnull"` + Tags string `json:"tags" bun:"tags,type:text"` + CreatedByUser *types.User `json:"user" bun:"rel:belongs-to,join:created_by=id"` +} + +type FunnelStep struct { + ID valuer.UUID `json:"id,omitempty"` + Name string `json:"name,omitempty"` // step name + Description string `json:"description,omitempty"` // step description + Order int64 `json:"step_order"` + ServiceName string `json:"service_name"` + SpanName string `json:"span_name"` + Filters *v3.FilterSet `json:"filters,omitempty"` + LatencyPointer string `json:"latency_pointer,omitempty"` + LatencyType string `json:"latency_type,omitempty"` + HasErrors bool `json:"has_errors"` +} + type addTraceFunnels struct { sqlstore sqlstore.SQLStore } func NewAddTraceFunnelsFactory(sqlstore sqlstore.SQLStore) factory.ProviderFactory[SQLMigration, Config] { - return factory. - NewProviderFactory(factory. - MustNewName("add_trace_funnels"), - func(ctx context.Context, providerSettings factory.ProviderSettings, config Config) (SQLMigration, error) { - return newAddTraceFunnels(ctx, providerSettings, config, sqlstore) - }) + 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) { @@ -29,8 +58,7 @@ func newAddTraceFunnels(_ context.Context, _ factory.ProviderSettings, _ Config, } func (migration *addTraceFunnels) Register(migrations *migrate.Migrations) error { - if err := migrations. - Register(migration.Up, migration.Down); err != nil { + if err := migrations.Register(migration.Up, migration.Down); err != nil { return err } return nil @@ -43,9 +71,8 @@ func (migration *addTraceFunnels) Up(ctx context.Context, db *bun.DB) error { } defer tx.Rollback() - // Create trace_funnel table with foreign key constraint inline _, err = tx.NewCreateTable(). - Model((*traceFunnels.Funnel)(nil)). + Model((*Funnel)(nil)). ForeignKey(`("org_id") REFERENCES "organizations" ("id") ON DELETE CASCADE`). IfNotExists(). Exec(ctx) @@ -53,62 +80,9 @@ func (migration *addTraceFunnels) Up(ctx context.Context, db *bun.DB) error { 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 } diff --git a/pkg/types/tracefunnel/store.go b/pkg/types/tracefunnel/store.go index f0936e80ec..44dbf7da4e 100644 --- a/pkg/types/tracefunnel/store.go +++ b/pkg/types/tracefunnel/store.go @@ -7,9 +7,9 @@ import ( ) type FunnelStore interface { - Create(context.Context, *Funnel) error - Get(context.Context, valuer.UUID) (*Funnel, error) - List(context.Context, valuer.UUID) ([]*Funnel, error) - Update(context.Context, *Funnel) error + Create(context.Context, *StorableFunnel) error + Get(context.Context, valuer.UUID) (*StorableFunnel, error) + List(context.Context, valuer.UUID) ([]*StorableFunnel, error) + Update(context.Context, *StorableFunnel) error Delete(context.Context, valuer.UUID) error } diff --git a/pkg/types/tracefunnel/tracefunnel.go b/pkg/types/tracefunnel/tracefunnel.go index 91731c2b9b..07c863f3e2 100644 --- a/pkg/types/tracefunnel/tracefunnel.go +++ b/pkg/types/tracefunnel/tracefunnel.go @@ -22,13 +22,13 @@ type BaseMetadata struct { OrgID valuer.UUID `json:"org_id" bun:"org_id,type:varchar,notnull"` } -// Funnel Core Data Structure (Funnel and FunnelStep) -type Funnel struct { +// StorableFunnel Core Data Structure (StorableFunnel and FunnelStep) +type StorableFunnel struct { bun.BaseModel `bun:"table:trace_funnel"` BaseMetadata - Steps []FunnelStep `json:"steps" bun:"steps,type:text,notnull"` - Tags string `json:"tags" bun:"tags,type:text"` - CreatedByUser *types.User `json:"user" bun:"rel:belongs-to,join:created_by=id"` + Steps []*FunnelStep `json:"steps" bun:"steps,type:text,notnull"` + Tags string `json:"tags" bun:"tags,type:text"` + CreatedByUser *types.User `json:"user" bun:"rel:belongs-to,join:created_by=id"` } type FunnelStep struct { @@ -44,14 +44,14 @@ type FunnelStep struct { HasErrors bool `json:"has_errors"` } -// FunnelRequest represents all possible funnel-related requests -type FunnelRequest struct { - FunnelID valuer.UUID `json:"funnel_id,omitempty"` - Name string `json:"funnel_name,omitempty"` - Timestamp int64 `json:"timestamp,omitempty"` - Description string `json:"description,omitempty"` - Steps []FunnelStep `json:"steps,omitempty"` - UserID string `json:"user_id,omitempty"` +// PostableFunnel represents all possible funnel-related requests +type PostableFunnel struct { + FunnelID valuer.UUID `json:"funnel_id,omitempty"` + Name string `json:"funnel_name,omitempty"` + Timestamp int64 `json:"timestamp,omitempty"` + Description string `json:"description,omitempty"` + Steps []*FunnelStep `json:"steps,omitempty"` + UserID string `json:"user_id,omitempty"` // Analytics specific fields StartTime int64 `json:"start_time,omitempty"` @@ -60,19 +60,19 @@ type FunnelRequest struct { StepBOrder int64 `json:"step_b_order,omitempty"` } -// FunnelResponse represents all possible funnel-related responses -type FunnelResponse struct { - FunnelID string `json:"funnel_id,omitempty"` - FunnelName string `json:"funnel_name,omitempty"` - Description string `json:"description,omitempty"` - CreatedAt int64 `json:"created_at,omitempty"` - CreatedBy string `json:"created_by,omitempty"` - UpdatedAt int64 `json:"updated_at,omitempty"` - UpdatedBy string `json:"updated_by,omitempty"` - OrgID string `json:"org_id,omitempty"` - UserEmail string `json:"user_email,omitempty"` - Funnel *Funnel `json:"funnel,omitempty"` - Steps []FunnelStep `json:"steps,omitempty"` +// GettableFunnel represents all possible funnel-related responses +type GettableFunnel struct { + FunnelID string `json:"funnel_id,omitempty"` + FunnelName string `json:"funnel_name,omitempty"` + Description string `json:"description,omitempty"` + CreatedAt int64 `json:"created_at,omitempty"` + CreatedBy string `json:"created_by,omitempty"` + UpdatedAt int64 `json:"updated_at,omitempty"` + UpdatedBy string `json:"updated_by,omitempty"` + OrgID string `json:"org_id,omitempty"` + UserEmail string `json:"user_email,omitempty"` + Funnel *StorableFunnel `json:"funnel,omitempty"` + Steps []*FunnelStep `json:"steps,omitempty"` } // TimeRange represents a time range for analytics diff --git a/pkg/modules/tracefunnel/utils.go b/pkg/types/tracefunnel/utils.go similarity index 85% rename from pkg/modules/tracefunnel/utils.go rename to pkg/types/tracefunnel/utils.go index e2ab16cd79..aa985fa6d5 100644 --- a/pkg/modules/tracefunnel/utils.go +++ b/pkg/types/tracefunnel/utils.go @@ -8,7 +8,6 @@ import ( "github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/types/authtypes" - "github.com/SigNoz/signoz/pkg/types/tracefunnel" "github.com/SigNoz/signoz/pkg/valuer" ) @@ -28,7 +27,7 @@ func ValidateTimestampIsMilliseconds(timestamp int64) bool { return timestamp >= 1000000000000 && timestamp <= 9999999999999 } -func ValidateFunnelSteps(steps []tracefunnel.FunnelStep) error { +func ValidateFunnelSteps(steps []FunnelStep) error { if len(steps) < 2 { return fmt.Errorf("funnel must have at least 2 steps") } @@ -50,12 +49,12 @@ func ValidateFunnelSteps(steps []tracefunnel.FunnelStep) error { // NormalizeFunnelSteps normalizes step orders to be sequential starting from 1. // Returns a new slice with normalized step orders, leaving the input slice unchanged. -func NormalizeFunnelSteps(steps []tracefunnel.FunnelStep) []tracefunnel.FunnelStep { +func NormalizeFunnelSteps(steps []FunnelStep) []FunnelStep { if len(steps) == 0 { - return []tracefunnel.FunnelStep{} + return []FunnelStep{} } - newSteps := make([]tracefunnel.FunnelStep, len(steps)) + newSteps := make([]FunnelStep, len(steps)) copy(newSteps, steps) sort.Slice(newSteps, func(i, j int) bool { @@ -88,8 +87,8 @@ func ValidateAndConvertTimestamp(timestamp int64) (time.Time, error) { return time.Unix(0, timestamp*1000000), nil // Convert to nanoseconds } -func ConstructFunnelResponse(funnel *tracefunnel.Funnel, claims *authtypes.Claims) tracefunnel.FunnelResponse { - resp := tracefunnel.FunnelResponse{ +func ConstructFunnelResponse(funnel *StorableFunnel, claims *authtypes.Claims) GettableFunnel { + resp := GettableFunnel{ FunnelName: funnel.Name, FunnelID: funnel.ID.String(), Steps: funnel.Steps, @@ -110,7 +109,7 @@ func ConstructFunnelResponse(funnel *tracefunnel.Funnel, claims *authtypes.Claim return resp } -func ProcessFunnelSteps(steps []tracefunnel.FunnelStep) ([]tracefunnel.FunnelStep, error) { +func ProcessFunnelSteps(steps []FunnelStep) ([]FunnelStep, error) { // First validate the steps if err := ValidateFunnelSteps(steps); err != nil { return nil, errors.Newf(errors.TypeInvalidInput, diff --git a/pkg/modules/tracefunnel/utils_test.go b/pkg/types/tracefunnel/utils_test.go similarity index 91% rename from pkg/modules/tracefunnel/utils_test.go rename to pkg/types/tracefunnel/utils_test.go index faff8a647e..3ce4e691db 100644 --- a/pkg/modules/tracefunnel/utils_test.go +++ b/pkg/types/tracefunnel/utils_test.go @@ -8,7 +8,6 @@ import ( "github.com/SigNoz/signoz/pkg/types" "github.com/SigNoz/signoz/pkg/types/authtypes" - "github.com/SigNoz/signoz/pkg/types/tracefunnel" "github.com/SigNoz/signoz/pkg/valuer" "github.com/stretchr/testify/assert" ) @@ -91,12 +90,12 @@ func TestValidateTimestampIsMilliseconds(t *testing.T) { func TestValidateFunnelSteps(t *testing.T) { tests := []struct { name string - steps []tracefunnel.FunnelStep + steps []FunnelStep expectError bool }{ { name: "valid steps", - steps: []tracefunnel.FunnelStep{ + steps: []FunnelStep{ { ID: valuer.GenerateUUID(), Name: "Step 1", @@ -116,7 +115,7 @@ func TestValidateFunnelSteps(t *testing.T) { }, { name: "too few steps", - steps: []tracefunnel.FunnelStep{ + steps: []FunnelStep{ { ID: valuer.GenerateUUID(), Name: "Step 1", @@ -129,7 +128,7 @@ func TestValidateFunnelSteps(t *testing.T) { }, { name: "missing service name", - steps: []tracefunnel.FunnelStep{ + steps: []FunnelStep{ { ID: valuer.GenerateUUID(), Name: "Step 1", @@ -148,7 +147,7 @@ func TestValidateFunnelSteps(t *testing.T) { }, { name: "missing span name", - steps: []tracefunnel.FunnelStep{ + steps: []FunnelStep{ { ID: valuer.GenerateUUID(), Name: "Step 1", @@ -167,7 +166,7 @@ func TestValidateFunnelSteps(t *testing.T) { }, { name: "negative order", - steps: []tracefunnel.FunnelStep{ + steps: []FunnelStep{ { ID: valuer.GenerateUUID(), Name: "Step 1", @@ -202,12 +201,12 @@ func TestValidateFunnelSteps(t *testing.T) { func TestNormalizeFunnelSteps(t *testing.T) { tests := []struct { name string - steps []tracefunnel.FunnelStep - expected []tracefunnel.FunnelStep + steps []FunnelStep + expected []FunnelStep }{ { name: "already normalized steps", - steps: []tracefunnel.FunnelStep{ + steps: []FunnelStep{ { ID: valuer.GenerateUUID(), Name: "Step 1", @@ -223,7 +222,7 @@ func TestNormalizeFunnelSteps(t *testing.T) { Order: 2, }, }, - expected: []tracefunnel.FunnelStep{ + expected: []FunnelStep{ { Name: "Step 1", ServiceName: "test-service", @@ -240,7 +239,7 @@ func TestNormalizeFunnelSteps(t *testing.T) { }, { name: "unordered steps", - steps: []tracefunnel.FunnelStep{ + steps: []FunnelStep{ { ID: valuer.GenerateUUID(), Name: "Step 2", @@ -256,7 +255,7 @@ func TestNormalizeFunnelSteps(t *testing.T) { Order: 1, }, }, - expected: []tracefunnel.FunnelStep{ + expected: []FunnelStep{ { Name: "Step 1", ServiceName: "test-service", @@ -273,7 +272,7 @@ func TestNormalizeFunnelSteps(t *testing.T) { }, { name: "steps with gaps in order", - steps: []tracefunnel.FunnelStep{ + steps: []FunnelStep{ { ID: valuer.GenerateUUID(), Name: "Step 1", @@ -296,7 +295,7 @@ func TestNormalizeFunnelSteps(t *testing.T) { Order: 2, }, }, - expected: []tracefunnel.FunnelStep{ + expected: []FunnelStep{ { Name: "Step 1", ServiceName: "test-service", @@ -322,7 +321,7 @@ func TestNormalizeFunnelSteps(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Make a copy of the steps to avoid modifying the original - steps := make([]tracefunnel.FunnelStep, len(tt.steps)) + steps := make([]FunnelStep, len(tt.steps)) copy(steps, tt.steps) result := NormalizeFunnelSteps(steps) @@ -429,14 +428,14 @@ func TestConstructFunnelResponse(t *testing.T) { tests := []struct { name string - funnel *tracefunnel.Funnel + funnel *StorableFunnel claims *authtypes.Claims - expected tracefunnel.FunnelResponse + expected GettableFunnel }{ { name: "with user email from funnel", - funnel: &tracefunnel.Funnel{ - BaseMetadata: tracefunnel.BaseMetadata{ + funnel: &StorableFunnel{ + BaseMetadata: BaseMetadata{ Identifiable: types.Identifiable{ ID: funnelID, }, @@ -455,7 +454,7 @@ func TestConstructFunnelResponse(t *testing.T) { Identifiable: types.Identifiable{ID: valuer.MustNewUUID("user-123")}, Email: "funnel@example.com", }, - Steps: []tracefunnel.FunnelStep{ + Steps: []*FunnelStep{ { ID: valuer.GenerateUUID(), Name: "Step 1", @@ -470,10 +469,10 @@ func TestConstructFunnelResponse(t *testing.T) { OrgID: orgID.String(), Email: "claims@example.com", }, - expected: tracefunnel.FunnelResponse{ + expected: GettableFunnel{ FunnelName: "test-funnel", FunnelID: funnelID.String(), - Steps: []tracefunnel.FunnelStep{ + Steps: []*FunnelStep{ { Name: "Step 1", ServiceName: "test-service", @@ -491,8 +490,8 @@ func TestConstructFunnelResponse(t *testing.T) { }, { name: "with user email from claims", - funnel: &tracefunnel.Funnel{ - BaseMetadata: tracefunnel.BaseMetadata{ + funnel: &StorableFunnel{ + BaseMetadata: BaseMetadata{ Identifiable: types.Identifiable{ ID: funnelID, }, @@ -507,7 +506,7 @@ func TestConstructFunnelResponse(t *testing.T) { UpdatedBy: "user-123", }, }, - Steps: []tracefunnel.FunnelStep{ + Steps: []*FunnelStep{ { ID: valuer.GenerateUUID(), Name: "Step 1", @@ -522,10 +521,10 @@ func TestConstructFunnelResponse(t *testing.T) { OrgID: orgID.String(), Email: "claims@example.com", }, - expected: tracefunnel.FunnelResponse{ + expected: GettableFunnel{ FunnelName: "test-funnel", FunnelID: funnelID.String(), - Steps: []tracefunnel.FunnelStep{ + Steps: []*FunnelStep{ { Name: "Step 1", ServiceName: "test-service", @@ -573,12 +572,12 @@ func TestConstructFunnelResponse(t *testing.T) { func TestProcessFunnelSteps(t *testing.T) { tests := []struct { name string - steps []tracefunnel.FunnelStep + steps []FunnelStep expectError bool }{ { name: "valid steps with missing IDs", - steps: []tracefunnel.FunnelStep{ + steps: []FunnelStep{ { Name: "Step 1", ServiceName: "test-service", @@ -596,7 +595,7 @@ func TestProcessFunnelSteps(t *testing.T) { }, { name: "invalid steps - missing service name", - steps: []tracefunnel.FunnelStep{ + steps: []FunnelStep{ { Name: "Step 1", SpanName: "test-span", @@ -613,7 +612,7 @@ func TestProcessFunnelSteps(t *testing.T) { }, { name: "invalid steps - negative order", - steps: []tracefunnel.FunnelStep{ + steps: []FunnelStep{ { Name: "Step 1", ServiceName: "test-service",