Skip to content

Commit

Permalink
add connection type and get connections
Browse files Browse the repository at this point in the history
  • Loading branch information
hvn2k1 committed Jan 15, 2025
1 parent 66a4481 commit abb97cc
Show file tree
Hide file tree
Showing 19 changed files with 645 additions and 553 deletions.
2 changes: 2 additions & 0 deletions api-description/web-api.swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5954,6 +5954,8 @@ definitions:
type: string
description:
type: string
connectionType:
$ref: '#/definitions/GoalConnectionType'
required:
- environmentId
- id
Expand Down
2 changes: 1 addition & 1 deletion manifests/bucketeer/charts/web/values.yaml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions migration/mysql/20250115085728_update_goal_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE `goal` ADD COLUMN `connection_type` INT NOT NULL DEFAULT 0 AFTER `description`;
3 changes: 2 additions & 1 deletion migration/mysql/atlas.sum
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
h1:gXfe2Jmu/IeG0QCdw6NY4V+AyeTd6/yun75fvTO7BW8=
h1:HgHcXqQAS/1LlZOfTQHNMcKCKOQR/nXd1eCtdwWz+l0=
20240626022133_initialization.sql h1:u9qmPkwWX7PN92qEcDDfR92lrMpwadQSMxUJgv6LjQ0=
20240708065726_update_audit_log_table.sql h1:k7gK8Njv1yHMsYXAQtSgMaSbXy4lxyZ9MPzbJyMyyoM=
20240815043128_update_auto_ops_rule_table.sql h1:6ib+XfS1uu9AUO3qESvkpUfOu3qUsLwHm9KHcrGEz0E=
Expand All @@ -21,3 +21,4 @@ h1:gXfe2Jmu/IeG0QCdw6NY4V+AyeTd6/yun75fvTO7BW8=
20250108145651_update_tag_table.sql h1:HGaQB+/bTGL8vqZPJl5nlRS/NlUS1ksFsaI2AbBkPmc=
20250109061411_update_tag_table.sql h1:3QsxRDVjdv45SkOKTUFswaSFaXV2ViHoqjlr6XR5fmY=
20250115040347_update_account_v2_table.sql h1:nEzBj41mgpXbQGgbNaIZ9thMqvtfamLO9NHISpEwMm8=
20250115085728_update_goal_table.sql h1:tUjNvXo6ExU+WxfRq/DhcuFlxmjG6HK9kpC6FNOsbMQ=
44 changes: 25 additions & 19 deletions pkg/experiment/api/goal.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ func (s *experimentService) GetGoal(ctx context.Context, req *proto.GetGoalReque
}
return nil, dt.Err()
}
err = s.mapConnectedOperations(ctx, []*proto.Goal{goal.Goal}, req.EnvironmentId)
if err != nil {
s.logger.Error("Failed to map connected operations", zap.Error(err))
dt, err := statusInternal.WithDetails(&errdetails.LocalizedMessage{
Locale: localizer.GetLocale(),
Message: localizer.MustLocalize(locale.InternalServerError),
})
if err != nil {
return nil, statusInternal.Err()
}
return nil, dt.Err()
}
return &proto.GetGoalResponse{Goal: goal.Goal}, nil
}

Expand Down Expand Up @@ -175,18 +187,9 @@ func (s *experimentService) ListGoals(
}
return nil, dt.Err()
}

listAutoOpRulesResp, err := s.autoOpsClient.ListAutoOpsRules(ctx, &autoopsproto.ListAutoOpsRulesRequest{
EnvironmentId: req.EnvironmentId,
})
err = s.mapConnectedOperations(ctx, goals, req.EnvironmentId)
if err != nil {
s.logger.Error(
"Failed to list auto ops rules",
log.FieldsFromImcomingContext(ctx).AddFields(
zap.Error(err),
zap.String("environmentId", req.EnvironmentId),
)...,
)
s.logger.Error("Failed to map connected operations", zap.Error(err))
dt, err := statusInternal.WithDetails(&errdetails.LocalizedMessage{
Locale: localizer.GetLocale(),
Message: localizer.MustLocalize(locale.InternalServerError),
Expand All @@ -196,11 +199,6 @@ func (s *experimentService) ListGoals(
}
return nil, dt.Err()
}
err = s.mapConnectedOperations(goals, listAutoOpRulesResp.AutoOpsRules)
if err != nil {
s.logger.Error("Failed to unmarshal OpsEventRateClause", zap.Error(err))
return nil, err
}

return &proto.ListGoalsResponse{
Goals: goals,
Expand Down Expand Up @@ -241,9 +239,17 @@ func (s *experimentService) newGoalListOrders(
}

func (s *experimentService) mapConnectedOperations(
ctx context.Context,
goals []*proto.Goal,
autoOpsRules []*autoopsproto.AutoOpsRule,
environmentID string,
) error {
listAutoOpRulesResp, err := s.autoOpsClient.ListAutoOpsRules(ctx, &autoopsproto.ListAutoOpsRulesRequest{
EnvironmentId: environmentID,
})
if err != nil {
return err
}
autoOpsRules := listAutoOpRulesResp.AutoOpsRules
goalOpsMap := make(map[string][]*autoopsproto.AutoOpsRule)
for _, rule := range autoOpsRules {
for _, clause := range rule.Clauses {
Expand Down Expand Up @@ -284,7 +290,7 @@ func (s *experimentService) CreateGoal(
if err := validateCreateGoalRequest(req, localizer); err != nil {
return nil, err
}
goal, err := domain.NewGoal(req.Command.Id, req.Command.Name, req.Command.Description)
goal, err := domain.NewGoal(req.Command.Id, req.Command.Name, req.Command.Description, proto.Goal_UNKNOWN)
if err != nil {
s.logger.Error(
"Failed to create a new goal",
Expand Down Expand Up @@ -371,7 +377,7 @@ func (s *experimentService) createGoalNoCommand(
if err := validateCreateGoalNoCommandRequest(req, localizer); err != nil {
return nil, err
}
goal, err := domain.NewGoal(req.Id, req.Name, req.Description)
goal, err := domain.NewGoal(req.Id, req.Name, req.Description, req.ConnectionType)
if err != nil {
s.logger.Error(
"Failed to create a new goal",
Expand Down
5 changes: 5 additions & 0 deletions pkg/experiment/api/goal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ func TestGetGoalMySQL(t *testing.T) {
s.mysqlClient.(*mysqlmock.MockClient).EXPECT().QueryRowContext(
gomock.Any(), gomock.Any(), gomock.Any(),
).Return(row)
s.autoOpsClient.(*autoopsclientmock.MockClient).EXPECT().ListAutoOpsRules(
gomock.Any(), gomock.Any(),
).Return(&autoopsproto.ListAutoOpsRulesResponse{
AutoOpsRules: []*autoopsproto.AutoOpsRule{},
}, nil)
},
id: "id-1",
environmentId: "ns0",
Expand Down
8 changes: 4 additions & 4 deletions pkg/experiment/command/goal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func TestHandleRenameGoalCommand(t *testing.T) {
mockController := gomock.NewController(t)
defer mockController.Finish()
publisher := publishermock.NewMockPublisher(mockController)
g, err := domain.NewGoal("gId", "gName", "gDesc")
g, err := domain.NewGoal("gId", "gName", "gDesc", experimentproto.Goal_UNKNOWN)
assert.NoError(t, err)

h := newGoalCommandHandler(t, publisher, g)
Expand All @@ -50,7 +50,7 @@ func TestHandleChangeDescriptionGoalCommand(t *testing.T) {
mockController := gomock.NewController(t)
defer mockController.Finish()
publisher := publishermock.NewMockPublisher(mockController)
g, err := domain.NewGoal("gId", "gName", "gDesc")
g, err := domain.NewGoal("gId", "gName", "gDesc", experimentproto.Goal_UNKNOWN)
assert.NoError(t, err)

h := newGoalCommandHandler(t, publisher, g)
Expand All @@ -67,7 +67,7 @@ func TestHandleArchiveGoalCommand(t *testing.T) {
mockController := gomock.NewController(t)
defer mockController.Finish()
publisher := publishermock.NewMockPublisher(mockController)
g, err := domain.NewGoal("gId", "gName", "gDesc")
g, err := domain.NewGoal("gId", "gName", "gDesc", experimentproto.Goal_UNKNOWN)
assert.NoError(t, err)

h := newGoalCommandHandler(t, publisher, g)
Expand All @@ -83,7 +83,7 @@ func TestHandleDeleteGoalCommand(t *testing.T) {
mockController := gomock.NewController(t)
defer mockController.Finish()
publisher := publishermock.NewMockPublisher(mockController)
g, err := domain.NewGoal("gId", "gName", "gDesc")
g, err := domain.NewGoal("gId", "gName", "gDesc", experimentproto.Goal_UNKNOWN)
assert.NoError(t, err)

h := newGoalCommandHandler(t, publisher, g)
Expand Down
16 changes: 10 additions & 6 deletions pkg/experiment/domain/goal.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,18 @@ type Goal struct {
*proto.Goal
}

func NewGoal(id, name, description string) (*Goal, error) {
func NewGoal(
id, name, description string,
connectionType proto.Goal_ConnectionType,
) (*Goal, error) {
now := time.Now().Unix()
return &Goal{&proto.Goal{
Id: id,
Name: name,
Description: description,
CreatedAt: now,
UpdatedAt: now,
Id: id,
Name: name,
Description: description,
ConnectionType: connectionType,
CreatedAt: now,
UpdatedAt: now,
}}, nil
}

Expand Down
5 changes: 3 additions & 2 deletions pkg/experiment/domain/goal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ package domain
import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/stretchr/testify/assert"
proto "github.com/bucketeer-io/bucketeer/proto/experiment"
)

func TestRenameGoal(t *testing.T) {
Expand Down Expand Up @@ -58,7 +59,7 @@ func TestSetDeletedGoal(t *testing.T) {

func newGoal(t *testing.T) *Goal {
t.Helper()
g, err := NewGoal("gID", "gName", "gDesc")
g, err := NewGoal("gID", "gName", "gDesc", proto.Goal_OPERATION)
require.NoError(t, err)
return g
}
63 changes: 15 additions & 48 deletions pkg/experiment/storage/v2/goal.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@ var (
ErrGoalNotFound = errors.New("goal: not found")
ErrGoalUnexpectedAffectedRows = errors.New("goal: unexpected affected rows")

//go:embed sql/goal/list_goals.sql
listGoalSQL string
//go:embed sql/goal/select_goals.sql
selectGoalsSQL string
//go:embed sql/goal/select_goal.sql
selectGoalSQL string
//go:embed sql/goal/count_goals.sql
countGoalSQL string
//go:embed sql/goal/insert_goal.sql
insertGoalSQL string
)

type GoalStorage interface {
Expand All @@ -60,34 +64,21 @@ func NewGoalStorage(qe mysql.QueryExecer) GoalStorage {
}

func (s *goalStorage) CreateGoal(ctx context.Context, g *domain.Goal, environmentId string) error {
query := `
INSERT INTO goal (
id,
name,
description,
archived,
deleted,
created_at,
updated_at,
environment_id
) VALUES (
?, ?, ?, ?, ?, ?, ?, ?
)
`
_, err := s.qe.ExecContext(
ctx,
query,
insertGoalSQL,
g.Id,
g.Name,
g.Description,
g.ConnectionType,
g.Archived,
g.Deleted,
g.CreatedAt,
g.UpdatedAt,
environmentId,
)
if err != nil {
if err == mysql.ErrDuplicateEntry {
if errors.Is(err, mysql.ErrDuplicateEntry) {
return ErrGoalAlreadyExists
}
return err
Expand Down Expand Up @@ -137,51 +128,26 @@ func (s *goalStorage) UpdateGoal(ctx context.Context, g *domain.Goal, environmen

func (s *goalStorage) GetGoal(ctx context.Context, id, environmentId string) (*domain.Goal, error) {
goal := proto.Goal{}
query := `
SELECT
id,
name,
description,
archived,
deleted,
created_at,
updated_at,
CASE
WHEN (
SELECT
COUNT(1)
FROM
experiment
WHERE
environment_id = ? AND
goal_ids LIKE concat("%", goal.id, "%")
) > 0 THEN TRUE
ELSE FALSE
END AS is_in_use_status
FROM
goal
WHERE
id = ? AND
environment_id = ?
`
err := s.qe.QueryRowContext(
ctx,
query,
selectGoalSQL,
environmentId,
id,
environmentId,
).Scan(
&goal.Id,
&goal.Name,
&goal.Description,
&goal.ConnectionType,
&goal.Archived,
&goal.Deleted,
&goal.CreatedAt,
&goal.UpdatedAt,
&goal.IsInUseStatus,
&mysql.JSONObject{Val: &goal.Experiments},
)
if err != nil {
if err == mysql.ErrNoRows {
if errors.Is(err, mysql.ErrNoRows) {
return nil, ErrGoalNotFound
}
return nil, err
Expand Down Expand Up @@ -211,7 +177,7 @@ func (s *goalStorage) ListGoals(
isInUseStatusSQL = "HAVING is_in_use_status = FALSE"
}
}
query := fmt.Sprintf(listGoalSQL, whereSQL, isInUseStatusSQL, orderBySQL, limitOffsetSQL)
query := fmt.Sprintf(selectGoalsSQL, whereSQL, isInUseStatusSQL, orderBySQL, limitOffsetSQL)
rows, err := s.qe.QueryContext(ctx, query, prepareArgs...)
if err != nil {
return nil, 0, 0, err
Expand All @@ -224,6 +190,7 @@ func (s *goalStorage) ListGoals(
&goal.Id,
&goal.Name,
&goal.Description,
&goal.ConnectionType,
&goal.Archived,
&goal.Deleted,
&goal.CreatedAt,
Expand Down
13 changes: 13 additions & 0 deletions pkg/experiment/storage/v2/sql/goal/insert_goal.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
INSERT INTO goal (
id,
name,
description,
connection_type,
archived,
deleted,
created_at,
updated_at,
environment_id
) VALUES (
?, ?, ?, ?, ?, ?, ?, ?, ?
)
32 changes: 32 additions & 0 deletions pkg/experiment/storage/v2/sql/goal/select_goal.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
SELECT
id,
name,
description,
connection_type,
archived,
deleted,
created_at,
updated_at,
CASE
WHEN (
SELECT
COUNT(1)
FROM
experiment ex1
WHERE
ex1.environment_id = ? AND
ex1.goal_ids LIKE concat("%", goal.id, "%")
) > 0 THEN TRUE
ELSE FALSE
END AS is_in_use_status,
(
select
CONCAT('[', GROUP_CONCAT(JSON_OBJECT('id', ex2.id, 'name', ex2.name)), ']')
from experiment ex2
where json_contains(ex2.goal_ids, concat('"', goal.id, '"'), '$')
) as experiments
FROM
goal
WHERE
id = ? AND
environment_id = ?
Loading

0 comments on commit abb97cc

Please sign in to comment.