Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: send proper error code in case of bad request #38

Merged
merged 2 commits into from
May 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions api/handler/v1beta1/appeal.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package v1beta1
import (
"context"
"errors"

guardianv1beta1 "github.com/goto/guardian/api/proto/gotocompany/guardian/v1beta1"
"github.com/goto/guardian/core/appeal"
"github.com/goto/guardian/domain"
Expand Down Expand Up @@ -106,15 +105,20 @@ func (s *GRPCServer) CreateAppeal(ctx context.Context, req *guardianv1beta1.Crea

func (s *GRPCServer) GetAppeal(ctx context.Context, req *guardianv1beta1.GetAppealRequest) (*guardianv1beta1.GetAppealResponse, error) {
id := req.GetId()
appeal, err := s.appealService.GetByID(ctx, id)

a, err := s.appealService.GetByID(ctx, id)
if err != nil {
if errors.As(err, new(appeal.InvalidError)) || errors.Is(err, appeal.ErrAppealIDEmptyParam) {
return nil, status.Errorf(codes.InvalidArgument, err.Error())
}
return nil, status.Errorf(codes.Internal, "failed to retrieve appeal: %v", err)
}
if appeal == nil {

if a == nil {
return nil, status.Errorf(codes.NotFound, "appeal not found: %v", id)
}

appealProto, err := s.adapter.ToAppealProto(appeal)
appealProto, err := s.adapter.ToAppealProto(a)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to parse appeal: %v", err)
}
Expand All @@ -126,8 +130,13 @@ func (s *GRPCServer) GetAppeal(ctx context.Context, req *guardianv1beta1.GetAppe

func (s *GRPCServer) CancelAppeal(ctx context.Context, req *guardianv1beta1.CancelAppealRequest) (*guardianv1beta1.CancelAppealResponse, error) {
id := req.GetId()

a, err := s.appealService.Cancel(ctx, id)
if err != nil {
if errors.As(err, new(appeal.InvalidError)) || errors.Is(err, appeal.ErrAppealIDEmptyParam) {
return nil, status.Errorf(codes.InvalidArgument, err.Error())
}

switch err {
case appeal.ErrAppealNotFound:
return nil, status.Errorf(codes.NotFound, "appeal not found: %v", id)
Expand Down
26 changes: 19 additions & 7 deletions api/handler/v1beta1/appeal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package v1beta1_test
import (
"context"
"errors"
"github.com/google/uuid"
"time"

guardianv1beta1 "github.com/goto/guardian/api/proto/gotocompany/guardian/v1beta1"
Expand Down Expand Up @@ -557,7 +558,7 @@ func (s *GrpcHandlersSuite) TestGetAppeal() {
s.setup()
timeNow := time.Now()

expectedID := "test-appeal-id"
expectedID := uuid.New().String()
expectedAppeal := &domain.Appeal{
ID: expectedID,
ResourceID: "test-resource-id",
Expand Down Expand Up @@ -655,7 +656,9 @@ func (s *GrpcHandlersSuite) TestGetAppeal() {
s.appealService.EXPECT().GetByID(mock.AnythingOfType("*context.emptyCtx"), mock.Anything).
Return(nil, expectedError).Once()

req := &guardianv1beta1.GetAppealRequest{}
req := &guardianv1beta1.GetAppealRequest{
Id: uuid.New().String(),
}
res, err := s.grpcServer.GetAppeal(context.Background(), req)

s.Equal(codes.Internal, status.Code(err))
Expand All @@ -669,7 +672,9 @@ func (s *GrpcHandlersSuite) TestGetAppeal() {
s.appealService.EXPECT().GetByID(mock.AnythingOfType("*context.emptyCtx"), mock.Anything).
Return(nil, nil).Once()

req := &guardianv1beta1.GetAppealRequest{}
req := &guardianv1beta1.GetAppealRequest{
Id: uuid.New().String(),
}
res, err := s.grpcServer.GetAppeal(context.Background(), req)

s.Equal(codes.NotFound, status.Code(err))
Expand All @@ -688,21 +693,24 @@ func (s *GrpcHandlersSuite) TestGetAppeal() {
s.appealService.EXPECT().GetByID(mock.AnythingOfType("*context.emptyCtx"), mock.Anything).
Return(invalidAppeal, nil).Once()

req := &guardianv1beta1.GetAppealRequest{}
req := &guardianv1beta1.GetAppealRequest{
Id: uuid.New().String(),
}
res, err := s.grpcServer.GetAppeal(context.Background(), req)

s.Equal(codes.Internal, status.Code(err))
s.Nil(res)
s.appealService.AssertExpectations(s.T())
})

}

func (s *GrpcHandlersSuite) TestCancelAppeal() {
s.Run("should return appeal details on success", func() {
s.setup()
timeNow := time.Now()

expectedID := "test-appeal-id"
expectedID := uuid.New().String()
expectedAppeal := &domain.Appeal{
ID: expectedID,
ResourceID: "test-resource-id",
Expand Down Expand Up @@ -838,7 +846,9 @@ func (s *GrpcHandlersSuite) TestCancelAppeal() {
s.appealService.EXPECT().Cancel(mock.AnythingOfType("*context.emptyCtx"), mock.Anything).
Return(nil, tc.expectedError).Once()

req := &guardianv1beta1.CancelAppealRequest{}
req := &guardianv1beta1.CancelAppealRequest{
Id: uuid.New().String(),
}
res, err := s.grpcServer.CancelAppeal(context.Background(), req)

s.Equal(tc.expectedStatusCode, status.Code(err))
Expand All @@ -859,7 +869,9 @@ func (s *GrpcHandlersSuite) TestCancelAppeal() {
s.appealService.EXPECT().Cancel(mock.AnythingOfType("*context.emptyCtx"), mock.Anything).
Return(invalidAppeal, nil).Once()

req := &guardianv1beta1.CancelAppealRequest{}
req := &guardianv1beta1.CancelAppealRequest{
Id: uuid.New().String(),
}
res, err := s.grpcServer.CancelAppeal(context.Background(), req)

s.Equal(codes.Internal, status.Code(err))
Expand Down
13 changes: 12 additions & 1 deletion core/appeal/errors.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package appeal

import "errors"
import (
"errors"
"fmt"
)

var (
ErrAppealIDEmptyParam = errors.New("appeal id is required")
Expand Down Expand Up @@ -50,3 +53,11 @@ var (
ErrApproverNotFound = errors.New("approver not found")
ErrGrantNotFound = errors.New("grant not found")
)

type InvalidError struct {
AppealID string
}

func (ie InvalidError) Error() string {
return fmt.Sprintf("invalid appeal id: %s", ie.AppealID)
}
12 changes: 12 additions & 0 deletions core/appeal/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ func (s *Service) GetByID(ctx context.Context, id string) (*domain.Appeal, error
return nil, ErrAppealIDEmptyParam
}

if !utils.IsValidUUID(id) {
return nil, InvalidError{AppealID: id}
}

return s.repo.GetByID(ctx, id)
}

Expand Down Expand Up @@ -590,6 +594,14 @@ func (s *Service) Update(ctx context.Context, appeal *domain.Appeal) error {
}

func (s *Service) Cancel(ctx context.Context, id string) (*domain.Appeal, error) {
if id == "" {
return nil, ErrAppealIDEmptyParam
}

if !utils.IsValidUUID(id) {
return nil, InvalidError{AppealID: id}
}

appeal, err := s.GetByID(ctx, id)
if err != nil {
return nil, err
Expand Down
47 changes: 33 additions & 14 deletions core/appeal/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,15 @@ func (s *ServiceTestSuite) TestGetByID() {
expectedError := errors.New("repository error")
s.mockRepository.EXPECT().GetByID(mock.AnythingOfType("*context.emptyCtx"), mock.Anything).Return(nil, expectedError).Once()

actualResult, actualError := s.service.GetByID(context.Background(), "1")
id := uuid.New().String()
actualResult, actualError := s.service.GetByID(context.Background(), id)

s.Nil(actualResult)
s.EqualError(actualError, expectedError.Error())
})

s.Run("should return record on success", func() {
expectedID := "1"
expectedID := uuid.New().String()
expectedResult := &domain.Appeal{
ID: expectedID,
}
Expand Down Expand Up @@ -1430,6 +1431,7 @@ func (s *ServiceTestSuite) TestCreateAppeal__WithAdditionalAppeals() {

func (s *ServiceTestSuite) TestUpdateApproval() {
timeNow := time.Now()
appealID := uuid.New().String()
appeal.TimeNow = func() time.Time {
return timeNow
}
Expand All @@ -1441,23 +1443,23 @@ func (s *ServiceTestSuite) TestUpdateApproval() {
Action: "name",
},
{
AppealID: "1",
AppealID: appealID,
Actor: "user@email.com",
Action: "name",
},
{
AppealID: "1",
AppealID: appealID,
ApprovalName: "approval_1",
Actor: "invalidemail",
Action: "name",
},
{
AppealID: "1",
AppealID: appealID,
ApprovalName: "approval_1",
Action: "name",
},
{
AppealID: "1",
AppealID: appealID,
ApprovalName: "approval_1",
Actor: "user@email.com",
},
Expand All @@ -1472,7 +1474,7 @@ func (s *ServiceTestSuite) TestUpdateApproval() {
})

validApprovalActionParam := domain.ApprovalAction{
AppealID: "1",
AppealID: appealID,
ApprovalName: "approval_1",
Actor: "user@email.com",
Action: "approve",
Expand Down Expand Up @@ -1718,13 +1720,13 @@ func (s *ServiceTestSuite) TestUpdateApproval() {

s.Run("should terminate existing active grant if present", func() {
action := domain.ApprovalAction{
AppealID: "1",
AppealID: appealID,
ApprovalName: "test-approval-step",
Action: "approve",
Actor: "approver@example.com",
}
appealDetails := &domain.Appeal{
ID: "1",
ID: appealID,
AccountID: "user@example.com",
ResourceID: "1",
Role: "test-role",
Expand Down Expand Up @@ -1887,7 +1889,7 @@ func (s *ServiceTestSuite) TestUpdateApproval() {
{
name: "reject",
expectedApprovalAction: domain.ApprovalAction{
AppealID: "1",
AppealID: appealID,
ApprovalName: "approval_1",
Actor: "user@email.com",
Action: domain.AppealActionNameReject,
Expand Down Expand Up @@ -1962,7 +1964,7 @@ func (s *ServiceTestSuite) TestUpdateApproval() {
{
name: "reject in the middle step",
expectedApprovalAction: domain.ApprovalAction{
AppealID: "1",
AppealID: appealID,
ApprovalName: "approval_1",
Actor: user,
Action: domain.AppealActionNameReject,
Expand Down Expand Up @@ -2289,9 +2291,26 @@ func (s *ServiceTestSuite) TestGrantAccessToProvider() {
})
}

// func (s *ServiceTestSuite) TestCancel() {
// s.Run("should return error from")
// }
func (s *ServiceTestSuite) TestCancel() {
s.Run("should return error if appeal id is empty", func() {
id := ""
expectedErr := appeal.ErrAppealIDEmptyParam

actualResult, actualErr := s.service.Cancel(context.Background(), id)
s.Nil(actualResult)
s.EqualError(actualErr, expectedErr.Error())
})

s.Run("should return error if appeal id is invalid", func() {
id := "abc"
expectedErr := appeal.InvalidError{AppealID: id}

actualResult, actualErr := s.service.Cancel(context.Background(), id)
s.Nil(actualResult)
s.EqualError(actualErr, expectedErr.Error())
})

}

func (s *ServiceTestSuite) TestAddApprover() {
s.Run("should return appeal on success", func() {
Expand Down
8 changes: 8 additions & 0 deletions utils/utils.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package utils

import "github.com/google/uuid"

func IsInteger(val float64) bool {
return val == float64(int(val))
}
Expand All @@ -19,3 +21,9 @@ func MapToSlice(m map[string]string) []string {
}
return s
}

// IsValidUUID returns true if uuid is valid
func IsValidUUID(u string) bool {
_, err := uuid.Parse(u)
return err == nil
}
Loading
Loading