Skip to content

Commit

Permalink
feat(approval): search and filter by resource and account type (#391)
Browse files Browse the repository at this point in the history
* feat(approval): support search query and filter by resource type and account_type

* fix: approval/grant postgres repository filters

---------

Signed-off-by: Kush Sharma <thekushsharma@gmail.com>
Co-authored-by: Chief-Rishab <rishab.jain7209@gmail.com>
Co-authored-by: Kush Sharma <thekushsharma@gmail.com>
  • Loading branch information
3 people authored Oct 7, 2023
1 parent 5c8a46d commit 683a285
Show file tree
Hide file tree
Showing 113 changed files with 6,227 additions and 2,592 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ COMMIT := $(shell git rev-parse --short HEAD)
TAG := "$(shell git rev-list --tags --max-count=1)"
VERSION := "$(shell git describe --tags ${TAG})-next"
BUILD_DIR=dist
PROTON_COMMIT := "95140abe54e3c27f0bf4f06bc780a289f41aadf1"
PROTON_COMMIT := "17305c2660e06ff9c82c1b6130799a703e4e543b"

.PHONY: all build clean test tidy vet proto setup format generate

Expand Down
2 changes: 1 addition & 1 deletion api/handler/v1beta1/activity.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func (s *GRPCServer) ListActivities(ctx context.Context, req *guardianv1beta1.Li
}

func (s *GRPCServer) ImportActivities(ctx context.Context, req *guardianv1beta1.ImportActivitiesRequest) (*guardianv1beta1.ImportActivitiesResponse, error) {
filter := domain.ImportActivitiesFilter{
filter := domain.ListActivitiesFilter{
ProviderID: req.GetProviderId(),
ResourceIDs: req.GetResourceIds(),
AccountIDs: req.GetAccountIds(),
Expand Down
56 changes: 48 additions & 8 deletions api/handler/v1beta1/appeal.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
guardianv1beta1 "github.com/raystack/guardian/api/proto/raystack/guardian/v1beta1"
"github.com/raystack/guardian/core/appeal"
"github.com/raystack/guardian/domain"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
Expand Down Expand Up @@ -35,40 +36,55 @@ func (s *GRPCServer) ListUserAppeals(ctx context.Context, req *guardianv1beta1.L
if req.GetResourceTypes() != nil {
filters.ResourceTypes = req.GetResourceTypes()
}
if req.GetAccountTypes() != nil {
filters.AccountTypes = req.GetAccountTypes()
}
if req.GetResourceUrns() != nil {
filters.ResourceURNs = req.GetResourceUrns()
}
if req.GetOrderBy() != nil {
filters.OrderBy = req.GetOrderBy()
}
appeals, err := s.listAppeals(ctx, filters)
if req.GetQ() != "" {
filters.Q = req.GetQ()
}
filters.Offset = int(req.GetOffset())
filters.Size = int(req.GetSize())

appeals, total, err := s.listAppeals(ctx, filters)
if err != nil {
return nil, err
}

return &guardianv1beta1.ListUserAppealsResponse{
Appeals: appeals,
Total: int32(total),
}, nil
}

func (s *GRPCServer) ListAppeals(ctx context.Context, req *guardianv1beta1.ListAppealsRequest) (*guardianv1beta1.ListAppealsResponse, error) {
filters := &domain.ListAppealsFilter{
Q: req.GetQ(),
AccountTypes: req.GetAccountTypes(),
AccountID: req.GetAccountId(),
Statuses: req.GetStatuses(),
Role: req.GetRole(),
ProviderTypes: req.GetProviderTypes(),
ProviderURNs: req.GetProviderUrns(),
ResourceTypes: req.GetResourceTypes(),
ResourceURNs: req.GetResourceUrns(),
Size: int(req.GetSize()),
Offset: int(req.GetOffset()),
OrderBy: req.GetOrderBy(),
}
appeals, err := s.listAppeals(ctx, filters)
appeals, total, err := s.listAppeals(ctx, filters)
if err != nil {
return nil, err
}

return &guardianv1beta1.ListAppealsResponse{
Appeals: appeals,
Total: int32(total),
}, nil
}

Expand Down Expand Up @@ -106,13 +122,15 @@ 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()

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 a == nil {
return nil, status.Errorf(codes.NotFound, "appeal not found: %v", id)
}
Expand All @@ -129,11 +147,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 All @@ -157,20 +177,40 @@ func (s *GRPCServer) CancelAppeal(ctx context.Context, req *guardianv1beta1.Canc
}, nil
}

func (s *GRPCServer) listAppeals(ctx context.Context, filters *domain.ListAppealsFilter) ([]*guardianv1beta1.Appeal, error) {
appeals, err := s.appealService.Find(ctx, filters)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get appeal list: %s", err)
func (s *GRPCServer) listAppeals(ctx context.Context, filters *domain.ListAppealsFilter) ([]*guardianv1beta1.Appeal, int64, error) {
eg, ctx := errgroup.WithContext(ctx)
var appeals []*domain.Appeal
var total int64

eg.Go(func() error {
appealRecords, err := s.appealService.Find(ctx, filters)
if err != nil {
return status.Errorf(codes.Internal, "failed to get appeal list: %s", err)
}
appeals = appealRecords
return nil
})
eg.Go(func() error {
totalRecord, err := s.appealService.GetAppealsTotalCount(ctx, filters)
if err != nil {
return status.Errorf(codes.Internal, "failed to get appeal total count: %s", err)
}
total = totalRecord
return nil
})

if err := eg.Wait(); err != nil {
return nil, 0, err
}

appealProtos := []*guardianv1beta1.Appeal{}
for _, a := range appeals {
appealProto, err := s.adapter.ToAppealProto(a)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to parse appeal: %s", err)
return nil, 0, status.Errorf(codes.Internal, "failed to parse appeal: %s", err)
}
appealProtos = append(appealProtos, appealProto)
}

return appealProtos, nil
return appealProtos, total, nil
}
32 changes: 26 additions & 6 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"
"fmt"
"time"

"github.com/google/uuid"
Expand Down Expand Up @@ -32,6 +33,8 @@ func (s *GrpcHandlersSuite) TestListUserAppeals() {
ResourceTypes: []string{"test-resource-type"},
ResourceURNs: []string{"test-resource-urn"},
OrderBy: []string{"test-order"},
AccountTypes: []string{"test-account-type"},
Q: "test",
}
expectedAppeals := []*domain.Appeal{
{
Expand Down Expand Up @@ -116,9 +119,12 @@ func (s *GrpcHandlersSuite) TestListUserAppeals() {
UpdatedAt: timestamppb.New(timeNow),
},
},
Total: 1,
}
s.appealService.EXPECT().Find(mock.AnythingOfType("*context.valueCtx"), expectedFilters).
s.appealService.EXPECT().Find(mock.AnythingOfType("*context.cancelCtx"), expectedFilters).
Return(expectedAppeals, nil).Once()
s.appealService.EXPECT().GetAppealsTotalCount(mock.AnythingOfType("*context.cancelCtx"), expectedFilters).
Return(int64(1), nil).Once()

req := &guardianv1beta1.ListUserAppealsRequest{
Statuses: []string{"active", "pending"},
Expand All @@ -128,6 +134,8 @@ func (s *GrpcHandlersSuite) TestListUserAppeals() {
ResourceTypes: []string{"test-resource-type"},
ResourceUrns: []string{"test-resource-urn"},
OrderBy: []string{"test-order"},
AccountTypes: []string{"test-account-type"},
Q: "test",
}
ctx := context.WithValue(context.Background(), authEmailTestContextKey{}, expectedUser)
res, err := s.grpcServer.ListUserAppeals(ctx, req)
Expand Down Expand Up @@ -155,13 +163,16 @@ func (s *GrpcHandlersSuite) TestListUserAppeals() {
s.setup()

expectedError := errors.New("random error")
s.appealService.EXPECT().Find(mock.AnythingOfType("*context.valueCtx"), mock.Anything).
s.appealService.EXPECT().Find(mock.AnythingOfType("*context.cancelCtx"), mock.Anything).
Return(nil, expectedError).Once()
s.appealService.EXPECT().GetAppealsTotalCount(mock.AnythingOfType("*context.cancelCtx"), mock.Anything).
Return(int64(0), nil).Once()

req := &guardianv1beta1.ListUserAppealsRequest{}
ctx := context.WithValue(context.Background(), authEmailTestContextKey{}, "test-user")
res, err := s.grpcServer.ListUserAppeals(ctx, req)

fmt.Println(status.Code(err))
s.Equal(codes.Internal, status.Code(err))
s.Nil(res)
s.appealService.AssertExpectations(s.T())
Expand All @@ -177,8 +188,10 @@ func (s *GrpcHandlersSuite) TestListUserAppeals() {
},
},
}
s.appealService.EXPECT().Find(mock.AnythingOfType("*context.valueCtx"), mock.Anything).
s.appealService.EXPECT().Find(mock.AnythingOfType("*context.cancelCtx"), mock.Anything).
Return(invalidAppeals, nil).Once()
s.appealService.EXPECT().GetAppealsTotalCount(mock.AnythingOfType("*context.cancelCtx"), mock.Anything).
Return(int64(1), nil).Once()

req := &guardianv1beta1.ListUserAppealsRequest{}
ctx := context.WithValue(context.Background(), authEmailTestContextKey{}, "test-user")
Expand Down Expand Up @@ -287,9 +300,12 @@ func (s *GrpcHandlersSuite) TestListAppeals() {
UpdatedAt: timestamppb.New(timeNow),
},
},
Total: 1,
}
s.appealService.EXPECT().Find(mock.AnythingOfType("*context.emptyCtx"), expectedFilters).
s.appealService.EXPECT().Find(mock.AnythingOfType("*context.cancelCtx"), expectedFilters).
Return(expectedAppeals, nil).Once()
s.appealService.EXPECT().GetAppealsTotalCount(mock.AnythingOfType("*context.cancelCtx"), expectedFilters).
Return(int64(1), nil).Once()

req := &guardianv1beta1.ListAppealsRequest{
AccountId: expectedUser,
Expand All @@ -312,8 +328,10 @@ func (s *GrpcHandlersSuite) TestListAppeals() {
s.setup()

expectedError := errors.New("random error")
s.appealService.EXPECT().Find(mock.AnythingOfType("*context.emptyCtx"), mock.Anything).
s.appealService.EXPECT().Find(mock.AnythingOfType("*context.cancelCtx"), mock.Anything).
Return(nil, expectedError).Once()
s.appealService.EXPECT().GetAppealsTotalCount(mock.AnythingOfType("*context.cancelCtx"), mock.Anything).
Return(int64(0), nil).Once()

req := &guardianv1beta1.ListAppealsRequest{}
res, err := s.grpcServer.ListAppeals(context.Background(), req)
Expand All @@ -333,8 +351,10 @@ func (s *GrpcHandlersSuite) TestListAppeals() {
},
},
}
s.appealService.EXPECT().Find(mock.AnythingOfType("*context.emptyCtx"), mock.Anything).
s.appealService.EXPECT().Find(mock.AnythingOfType("*context.cancelCtx"), mock.Anything).
Return(invalidAppeals, nil).Once()
s.appealService.EXPECT().GetAppealsTotalCount(mock.AnythingOfType("*context.cancelCtx"), mock.Anything).
Return(int64(1), nil).Once()

req := &guardianv1beta1.ListAppealsRequest{}
res, err := s.grpcServer.ListAppeals(context.Background(), req)
Expand Down
46 changes: 38 additions & 8 deletions api/handler/v1beta1/approval.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
guardianv1beta1 "github.com/raystack/guardian/api/proto/raystack/guardian/v1beta1"
"github.com/raystack/guardian/core/appeal"
"github.com/raystack/guardian/domain"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
Expand All @@ -17,8 +18,11 @@ func (s *GRPCServer) ListUserApprovals(ctx context.Context, req *guardianv1beta1
return nil, status.Error(codes.Unauthenticated, err.Error())
}

approvals, err := s.listApprovals(ctx, &domain.ListApprovalsFilter{
approvals, total, err := s.listApprovals(ctx, &domain.ListApprovalsFilter{
Q: req.GetQ(),
AccountID: req.GetAccountId(),
AccountTypes: req.GetAccountTypes(),
ResourceTypes: req.GetResourceTypes(),
CreatedBy: user,
Statuses: req.GetStatuses(),
OrderBy: req.GetOrderBy(),
Expand All @@ -32,12 +36,16 @@ func (s *GRPCServer) ListUserApprovals(ctx context.Context, req *guardianv1beta1

return &guardianv1beta1.ListUserApprovalsResponse{
Approvals: approvals,
Total: int32(total),
}, nil
}

func (s *GRPCServer) ListApprovals(ctx context.Context, req *guardianv1beta1.ListApprovalsRequest) (*guardianv1beta1.ListApprovalsResponse, error) {
approvals, err := s.listApprovals(ctx, &domain.ListApprovalsFilter{
approvals, total, err := s.listApprovals(ctx, &domain.ListApprovalsFilter{
Q: req.GetQ(),
AccountID: req.GetAccountId(),
AccountTypes: req.GetAccountTypes(),
ResourceTypes: req.GetResourceTypes(),
CreatedBy: req.GetCreatedBy(),
Statuses: req.GetStatuses(),
OrderBy: req.GetOrderBy(),
Expand All @@ -51,6 +59,7 @@ func (s *GRPCServer) ListApprovals(ctx context.Context, req *guardianv1beta1.Lis

return &guardianv1beta1.ListApprovalsResponse{
Approvals: approvals,
Total: int32(total),
}, nil
}

Expand Down Expand Up @@ -150,20 +159,41 @@ func (s *GRPCServer) DeleteApprover(ctx context.Context, req *guardianv1beta1.De
}, nil
}

func (s *GRPCServer) listApprovals(ctx context.Context, filters *domain.ListApprovalsFilter) ([]*guardianv1beta1.Approval, error) {
approvals, err := s.approvalService.ListApprovals(ctx, filters)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get approval list: %s", err)
func (s *GRPCServer) listApprovals(ctx context.Context, filters *domain.ListApprovalsFilter) ([]*guardianv1beta1.Approval, int64, error) {
eg, ctx := errgroup.WithContext(ctx)
var approvals []*domain.Approval
var total int64

eg.Go(func() error {
approvalRecords, err := s.approvalService.ListApprovals(ctx, filters)
if err != nil {
return status.Errorf(codes.Internal, "failed to get approval list: %s", err)
}
approvals = approvalRecords
return nil
})

eg.Go(func() error {
totalRecord, err := s.approvalService.GetApprovalsTotalCount(ctx, filters)
if err != nil {
return status.Errorf(codes.Internal, "failed to get approval list: %v", err)
}
total = totalRecord
return nil
})

if err := eg.Wait(); err != nil {
return nil, 0, err
}

approvalProtos := []*guardianv1beta1.Approval{}
for _, a := range approvals {
approvalProto, err := s.adapter.ToApprovalProto(a)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to parse approval: %v: %s", a.ID, err)
return nil, 0, status.Errorf(codes.Internal, "failed to parse approval: %v: %s", a.ID, err)
}
approvalProtos = append(approvalProtos, approvalProto)
}

return approvalProtos, nil
return approvalProtos, total, nil
}
Loading

0 comments on commit 683a285

Please sign in to comment.