Skip to content

Commit

Permalink
Merge branch 'main' into feat/get-experiment-rest
Browse files Browse the repository at this point in the history
  • Loading branch information
hvn2k1 committed Jan 21, 2025
2 parents da2ebae + aeabf7e commit c916823
Show file tree
Hide file tree
Showing 21 changed files with 4,227 additions and 2,690 deletions.
74 changes: 74 additions & 0 deletions api-description/web-api.swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3373,6 +3373,54 @@ paths:
$ref: '#/definitions/experimentCreateGoalRequest'
tags:
- goal
patch:
summary: Update
description: Update a goal.
operationId: web.v1.goal.update
responses:
"200":
description: A successful response.
schema:
$ref: '#/definitions/experimentUpdateGoalResponse'
"400":
description: Returned for bad requests that may have failed validation.
schema:
$ref: '#/definitions/googlerpcStatus'
examples:
application/json:
code: 3
details: []
message: invalid arguments error
"401":
description: Request could not be authenticated (authentication required).
schema:
$ref: '#/definitions/googlerpcStatus'
examples:
application/json:
code: 16
details: []
message: not authenticated
"503":
description: Returned for internal errors.
schema:
$ref: '#/definitions/googlerpcStatus'
examples:
application/json:
code: 13
details: []
message: internal
default:
description: An unexpected error response.
schema:
$ref: '#/definitions/googlerpcStatus'
parameters:
- name: body
in: body
required: true
schema:
$ref: '#/definitions/experimentUpdateGoalRequest'
tags:
- goal
/v1/goals:
get:
summary: List
Expand Down Expand Up @@ -6449,8 +6497,34 @@ definitions:
type: object
experimentUpdateExperimentResponse:
type: object
experimentUpdateGoalRequest:
type: object
properties:
id:
type: string
renameCommand:
$ref: '#/definitions/experimentRenameGoalCommand'
description: deprecated
changeDescriptionCommand:
$ref: '#/definitions/experimentChangeDescriptionGoalCommand'
description: deprecated
environmentId:
type: string
name:
type: string
description:
type: string
archived:
type: boolean
description: if true, the goal will be archived
required:
- id
- environmentId
experimentUpdateGoalResponse:
type: object
properties:
goal:
$ref: '#/definitions/experimentGoal'
featureAddSegmentUserCommand:
type: object
properties:
Expand Down
4 changes: 2 additions & 2 deletions manifests/bucketeer/charts/web/values.yaml

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions pkg/domainevent/domain/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,14 @@ func LocalizedMessage(eventType proto.Event_Type, localizer locale.Localizer) *p
localizer.MustLocalizeWithTemplate(locale.Goal),
),
}
case proto.Event_GOAL_UPDATED:
return &proto.LocalizedMessage{
Locale: localizer.GetLocale(),
Message: localizer.MustLocalizeWithTemplate(
locale.UpdatedTemplate,
localizer.MustLocalizeWithTemplate(locale.Goal),
),
}
case proto.Event_EXPERIMENT_CREATED:
return &proto.LocalizedMessage{
Locale: localizer.GetLocale(),
Expand Down
131 changes: 130 additions & 1 deletion pkg/experiment/api/goal.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"regexp"
"strconv"

pb "github.com/golang/protobuf/proto"
"go.uber.org/zap"
"google.golang.org/genproto/googleapis/rpc/errdetails"

Expand Down Expand Up @@ -554,6 +555,9 @@ func (s *experimentService) UpdateGoal(
if err != nil {
return nil, err
}
if req.ChangeDescriptionCommand == nil && req.RenameCommand == nil {
return s.updateGoalNoCommand(ctx, req, editor, localizer)
}
if req.Id == "" {
dt, err := statusGoalIDRequired.WithDetails(&errdetails.LocalizedMessage{
Locale: localizer.GetLocale(),
Expand Down Expand Up @@ -602,6 +606,131 @@ func (s *experimentService) UpdateGoal(
return &proto.UpdateGoalResponse{}, nil
}

func (s *experimentService) updateGoalNoCommand(
ctx context.Context,
req *proto.UpdateGoalRequest,
editor *eventproto.Editor,
localizer locale.Localizer,
) (*proto.UpdateGoalResponse, error) {
err := s.validateUpdateGoalNoCommandRequest(req, localizer)
if err != nil {
return nil, err
}

tx, err := s.mysqlClient.BeginTx(ctx)
if err != nil {
s.logger.Error(
"Failed to begin transaction",
log.FieldsFromImcomingContext(ctx).AddFields(
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()
}
var updatedGoal *proto.Goal
err = s.mysqlClient.RunInTransaction(ctx, tx, func() error {
goalStorage := v2es.NewGoalStorage(tx)
goal, err := goalStorage.GetGoal(ctx, req.Id, req.EnvironmentId)
if err != nil {
return err
}
updated, err := goal.Update(
req.Name,
req.Description,
req.Archived,
)
if err != nil {
return err
}
updatedGoal = updated.Goal

var event pb.Message
if req.Archived != nil && req.Archived.Value {
event = &eventproto.GoalArchivedEvent{Id: goal.Id}
} else {
event = &eventproto.GoalUpdatedEvent{
Id: goal.Id,
Name: req.Name,
Description: req.Description,
}
}
e, err := domainevent.NewEvent(
editor,
eventproto.Event_GOAL,
goal.Id,
eventproto.Event_GOAL_UPDATED,
event,
req.EnvironmentId,
updated.Goal,
goal.Goal,
)
if err != nil {
return err
}
if err = s.publisher.Publish(ctx, e); err != nil {
return err
}
return goalStorage.UpdateGoal(ctx, updated, req.EnvironmentId)
})
if err != nil {
if errors.Is(err, v2es.ErrGoalNotFound) || errors.Is(err, v2es.ErrGoalUnexpectedAffectedRows) {
dt, err := statusNotFound.WithDetails(&errdetails.LocalizedMessage{
Locale: localizer.GetLocale(),
Message: localizer.MustLocalize(locale.NotFoundError),
})
if err != nil {
return nil, statusInternal.Err()
}
return nil, dt.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.UpdateGoalResponse{
Goal: updatedGoal,
}, nil
}

func (s *experimentService) validateUpdateGoalNoCommandRequest(
req *proto.UpdateGoalRequest,
localizer locale.Localizer,
) error {
if req.Id == "" {
dt, err := statusGoalIDRequired.WithDetails(&errdetails.LocalizedMessage{
Locale: localizer.GetLocale(),
Message: localizer.MustLocalizeWithTemplate(locale.RequiredFieldTemplate, "goal_id"),
})
if err != nil {
return statusInternal.Err()
}
return dt.Err()
}
if req.Name != nil && req.Name.Value == "" {
dt, err := statusGoalNameRequired.WithDetails(&errdetails.LocalizedMessage{
Locale: localizer.GetLocale(),
Message: localizer.MustLocalizeWithTemplate(locale.RequiredFieldTemplate, "name"),
})
if err != nil {
return statusInternal.Err()
}
return dt.Err()
}
return nil
}

func (s *experimentService) ArchiveGoal(
ctx context.Context,
req *proto.ArchiveGoalRequest,
Expand Down Expand Up @@ -748,7 +877,7 @@ func (s *experimentService) updateGoal(
return goalStorage.UpdateGoal(ctx, goal, environmentId)
})
if err != nil {
if err == v2es.ErrGoalNotFound || err == v2es.ErrGoalUnexpectedAffectedRows {
if errors.Is(err, v2es.ErrGoalNotFound) || errors.Is(err, v2es.ErrGoalUnexpectedAffectedRows) {
dt, err := statusNotFound.WithDetails(&errdetails.LocalizedMessage{
Locale: localizer.GetLocale(),
Message: localizer.MustLocalize(locale.NotFoundError),
Expand Down
Loading

0 comments on commit c916823

Please sign in to comment.