Skip to content

Commit 7c13f96

Browse files
JAORMXclaude
andcommitted
Address PR review feedback for RegisterEntity
- Use errors.As pattern for UserVisibleError passthrough in handlers - Convert validator errors to util.UserVisibleError for proper gRPC responses - Reduce cyclomatic complexity in CreateEntity by extracting helpers: - runValidators for validator loop - cleanupProviderRegistration for cleanup logic - Remove unused functions: - upsertLegacyEntity in add_originating_entity.go - pushReconcilerEvent and persistRepository in service.go - Unused test helpers in handler_test.go and service_test.go - Fix unused parameters in test files - Update tests to mock EntityCreator instead of internal services - Update expected error messages to match new UserVisibleError format 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent bced660 commit 7c13f96

File tree

12 files changed

+236
-447
lines changed

12 files changed

+236
-447
lines changed

internal/controlplane/handlers_entity_instances.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414

1515
"github.com/mindersec/minder/internal/engine/engcontext"
1616
"github.com/mindersec/minder/internal/entities/models"
17-
"github.com/mindersec/minder/internal/entities/service/validators"
1817
"github.com/mindersec/minder/internal/logger"
1918
"github.com/mindersec/minder/internal/util"
2019
pb "github.com/mindersec/minder/pkg/api/protobuf/go/minder/v1"
@@ -224,9 +223,12 @@ func (s *Server) RegisterEntity(
224223
ewp, err := s.entityCreator.CreateEntity(ctx, provider, projectID,
225224
in.GetEntityType(), identifyingProps, nil) // Use default options
226225
if err != nil {
227-
if errors.Is(err, validators.ErrPrivateRepoForbidden) ||
228-
errors.Is(err, validators.ErrArchivedRepoForbidden) {
229-
return nil, util.UserVisibleError(codes.InvalidArgument, "%s", err.Error())
226+
// If the error is already a UserVisibleError, pass it through directly.
227+
// This allows providers and EntityCreator to add user-visible errors
228+
// without needing to update this allow-list.
229+
var userErr *util.NiceStatus
230+
if errors.As(err, &userErr) {
231+
return nil, err
230232
}
231233
return nil, util.UserVisibleError(codes.Internal,
232234
"unable to register entity: %v", err)

internal/controlplane/handlers_entity_instances_test.go

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,14 @@ func TestServer_RegisterEntity(t *testing.T) {
3636
providerName := "github"
3737

3838
tests := []struct {
39-
name string
40-
request *pb.RegisterEntityRequest
41-
setupContext func(context.Context) context.Context
42-
setupMocks func(*mockproviders.MockProviderStore, *mockentitysvc.MockEntityCreator)
43-
wantErr bool
44-
wantCode codes.Code
45-
errContains string
46-
validateResp func(*testing.T, *pb.RegisterEntityResponse)
39+
name string
40+
request *pb.RegisterEntityRequest
41+
setupContext func(context.Context) context.Context
42+
setupMocks func(*mockproviders.MockProviderStore, *mockentitysvc.MockEntityCreator)
43+
wantErr bool
44+
wantCode codes.Code
45+
errContains string
46+
validateResp func(*testing.T, *pb.RegisterEntityResponse)
4747
}{
4848
{
4949
name: "successfully registers repository",
@@ -95,6 +95,7 @@ func TestServer_RegisterEntity(t *testing.T) {
9595
},
9696
wantErr: false,
9797
validateResp: func(t *testing.T, resp *pb.RegisterEntityResponse) {
98+
t.Helper()
9899
assert.NotNil(t, resp)
99100
assert.NotNil(t, resp.GetEntity())
100101
assert.Equal(t, entityID.String(), resp.GetEntity().GetId())
@@ -118,9 +119,7 @@ func TestServer_RegisterEntity(t *testing.T) {
118119
Provider: engcontext.Provider{Name: providerName},
119120
})
120121
},
121-
setupMocks: func(provStore *mockproviders.MockProviderStore, creator *mockentitysvc.MockEntityCreator) {
122-
// No mocks needed - should fail early
123-
},
122+
// No mocks needed - should fail early
124123
wantErr: true,
125124
wantCode: codes.InvalidArgument,
126125
errContains: "entity_type must be specified",
@@ -138,7 +137,7 @@ func TestServer_RegisterEntity(t *testing.T) {
138137
Provider: engcontext.Provider{Name: providerName},
139138
})
140139
},
141-
setupMocks: func(provStore *mockproviders.MockProviderStore, creator *mockentitysvc.MockEntityCreator) {},
140+
// No mocks needed - should fail early
142141
wantErr: true,
143142
wantCode: codes.InvalidArgument,
144143
errContains: "identifying_properties is required",
@@ -159,7 +158,7 @@ func TestServer_RegisterEntity(t *testing.T) {
159158
Provider: engcontext.Provider{Name: providerName},
160159
})
161160
},
162-
setupMocks: func(provStore *mockproviders.MockProviderStore, creator *mockentitysvc.MockEntityCreator) {
161+
setupMocks: func(provStore *mockproviders.MockProviderStore, _ *mockentitysvc.MockEntityCreator) {
163162
provStore.EXPECT().
164163
GetByName(gomock.Any(), projectID, providerName).
165164
Return(nil, sql.ErrNoRows)
@@ -286,7 +285,9 @@ func TestServer_RegisterEntity(t *testing.T) {
286285
mockProvStore := mockproviders.NewMockProviderStore(ctrl)
287286
mockEntityCreator := mockentitysvc.NewMockEntityCreator(ctrl)
288287

289-
tt.setupMocks(mockProvStore, mockEntityCreator)
288+
if tt.setupMocks != nil {
289+
tt.setupMocks(mockProvStore, mockEntityCreator)
290+
}
290291

291292
server := &Server{
292293
providerStore: mockProvStore,
@@ -342,6 +343,7 @@ func TestParseIdentifyingProperties(t *testing.T) {
342343
},
343344
wantErr: false,
344345
validate: func(t *testing.T, props *properties.Properties) {
346+
t.Helper()
345347
owner := props.GetProperty("github/repo_owner").GetString()
346348
assert.Equal(t, "stacklok", owner)
347349

@@ -402,6 +404,7 @@ func TestParseIdentifyingProperties(t *testing.T) {
402404
},
403405
wantErr: false,
404406
validate: func(t *testing.T, props *properties.Properties) {
407+
t.Helper()
405408
// Empty properties is valid (provider will validate)
406409
assert.NotNil(t, props)
407410
},

internal/controlplane/handlers_repositories_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func TestServer_RegisterRepository(t *testing.T) {
8080
reposvc.ErrPrivateRepoForbidden,
8181
projectID,
8282
)),
83-
ExpectedError: "private repos cannot be registered in this project",
83+
ExpectedError: "private repositories are not allowed in this project",
8484
},
8585
{
8686
Name: "Repo creation fails repo is archived, and archived repos are not allowed",
@@ -90,7 +90,7 @@ func TestServer_RegisterRepository(t *testing.T) {
9090
reposvc.ErrArchivedRepoForbidden,
9191
projectID,
9292
)),
93-
ExpectedError: "archived repos cannot be registered in this project",
93+
ExpectedError: "archived repositories cannot be registered",
9494
},
9595
{
9696
Name: "Repo creation on unexpected error",

internal/entities/handlers/handler_test.go

Lines changed: 70 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
"github.com/mindersec/minder/internal/entities/properties/service"
2525
"github.com/mindersec/minder/internal/entities/properties/service/mock/fixtures"
2626
stubeventer "github.com/mindersec/minder/internal/events/stubs"
27-
pbinternal "github.com/mindersec/minder/internal/proto"
2827
mockgithub "github.com/mindersec/minder/internal/providers/github/mock"
2928
ghprops "github.com/mindersec/minder/internal/providers/github/properties"
3029
"github.com/mindersec/minder/internal/providers/manager"
@@ -96,10 +95,6 @@ type (
9695
providerMockBuilder = func(controller *gomock.Controller) providerMock
9796
)
9897

99-
func getPullRequestProperties() *properties.Properties {
100-
return properties.NewProperties(pullRequestPropMap)
101-
}
102-
10398
func newProviderMock(opts ...func(providerMock)) providerMockBuilder {
10499
return func(ctrl *gomock.Controller) providerMock {
105100
mock := mockgithub.NewMockGitHub(ctrl)
@@ -110,22 +105,6 @@ func newProviderMock(opts ...func(providerMock)) providerMockBuilder {
110105
}
111106
}
112107

113-
func withSuccessfulGetEntityName(name string) func(providerMock) {
114-
return func(mock providerMock) {
115-
mock.EXPECT().
116-
GetEntityName(gomock.Any(), gomock.Any()).
117-
Return(name, nil)
118-
}
119-
}
120-
121-
func withSuccessfulFetchAllProperties(props *properties.Properties) func(mock providerMock) {
122-
return func(mock providerMock) {
123-
mock.EXPECT().
124-
FetchAllProperties(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
125-
Return(props, nil)
126-
}
127-
}
128-
129108
func WithSuccessfulPropertiesToProtoMessage(proto protoreflect.ProtoMessage) func(mock providerMock) {
130109
return func(mock providerMock) {
131110
mock.EXPECT().
@@ -168,18 +147,6 @@ func checkRepoMessage(t *testing.T, msg *watermill.Message) {
168147
assert.Equal(t, repoPropMap[properties.RepoPropertyIsFork].(bool), pbrepo.IsFork)
169148
}
170149

171-
func checkPullRequestMessage(t *testing.T, msg *watermill.Message) {
172-
t.Helper()
173-
174-
eiw, err := entities.ParseEntityEvent(msg)
175-
require.NoError(t, err)
176-
require.NotNil(t, eiw)
177-
178-
pbpr, ok := eiw.Entity.(*pbinternal.PullRequest)
179-
require.True(t, ok)
180-
assert.Equal(t, pullRequestPropMap[ghprops.PullPropertyNumber].(int64), pbpr.Number)
181-
}
182-
183150
type handlerBuilder func(
184151
evt interfaces.Publisher,
185152
store db.Store,
@@ -205,17 +172,6 @@ func refreshByIDHandlerBuilder(
205172
return NewRefreshByIDAndEvaluateHandler(evt, store, propSvc, provMgr)
206173
}
207174

208-
func addOriginatingEntityHandlerBuilder(
209-
evt interfaces.Publisher,
210-
store db.Store,
211-
propSvc service.PropertiesService,
212-
provMgr manager.ProviderManager,
213-
) interfaces.Consumer {
214-
// Create a nil entityCreator for testing - the tests focus on other handlers
215-
// and addOriginatingEntityHandler tests would need separate setup
216-
return NewAddOriginatingEntityHandler(evt, store, propSvc, provMgr, nil)
217-
}
218-
219175
func removeOriginatingEntityHandlerBuilder(
220176
evt interfaces.Publisher,
221177
store db.Store,
@@ -548,80 +504,80 @@ func TestRefreshEntityAndDoHandler_HandleRefreshEntityAndEval(t *testing.T) {
548504
// The test was testing internal implementation details that have been refactored
549505
// New tests for addOriginatingEntityHandler should be written that properly mock EntityCreator
550506
/*
551-
{
552-
name: "NewAddOriginatingEntityHandler: Adding a pull request originating entity publishes",
553-
handlerBuilderFn: addOriginatingEntityHandlerBuilder,
554-
messageBuilder: func() *message.HandleEntityAndDoMessage {
555-
prProps := properties.NewProperties(map[string]any{
556-
properties.PropertyUpstreamID: "789",
557-
ghprops.PullPropertyNumber: int64(789),
558-
})
559-
originatorProps := properties.NewProperties(map[string]any{
560-
properties.PropertyUpstreamID: "123",
561-
})
562-
563-
return message.NewEntityRefreshAndDoMessage().
564-
WithEntity(minderv1.Entity_ENTITY_PULL_REQUESTS, prProps).
565-
WithOriginator(minderv1.Entity_ENTITY_REPOSITORIES, originatorProps).
566-
WithProviderImplementsHint("github")
567-
},
568-
setupPropSvcMocks: func() fixtures.MockPropertyServiceBuilder {
569-
pullEwp := buildEwp(t, pullRequestEwp, pullRequestPropMap)
570-
pullProtoEnt, err := ghprops.PullRequestV1FromProperties(pullEwp.Properties)
571-
require.NoError(t, err)
572-
573-
repoPropsEwp := buildEwp(t, repoEwp, pullRequestPropMap)
574-
575-
return fixtures.NewMockPropertiesService(
576-
fixtures.WithSuccessfulEntityByUpstreamHint(repoPropsEwp, githubHint),
577-
fixtures.WithSuccessfulEntityWithPropertiesAsProto(pullProtoEnt),
578-
fixtures.WithSuccessfulSaveAllProperties(),
579-
)
580-
},
581-
mockStoreFunc: df.NewMockStore(
582-
df.WithTransaction(),
583-
df.WithSuccessfulUpsertPullRequestWithParams(
584-
585-
db.EntityInstance{
586-
ID: pullRequestID,
587-
EntityType: db.EntitiesPullRequest,
588-
Name: "",
589-
ProjectID: projectID,
590-
ProviderID: providerID,
591-
OriginatedFrom: uuid.NullUUID{
592-
UUID: repoID,
593-
Valid: true,
507+
{
508+
name: "NewAddOriginatingEntityHandler: Adding a pull request originating entity publishes",
509+
handlerBuilderFn: addOriginatingEntityHandlerBuilder,
510+
messageBuilder: func() *message.HandleEntityAndDoMessage {
511+
prProps := properties.NewProperties(map[string]any{
512+
properties.PropertyUpstreamID: "789",
513+
ghprops.PullPropertyNumber: int64(789),
514+
})
515+
originatorProps := properties.NewProperties(map[string]any{
516+
properties.PropertyUpstreamID: "123",
517+
})
518+
519+
return message.NewEntityRefreshAndDoMessage().
520+
WithEntity(minderv1.Entity_ENTITY_PULL_REQUESTS, prProps).
521+
WithOriginator(minderv1.Entity_ENTITY_REPOSITORIES, originatorProps).
522+
WithProviderImplementsHint("github")
523+
},
524+
setupPropSvcMocks: func() fixtures.MockPropertyServiceBuilder {
525+
pullEwp := buildEwp(t, pullRequestEwp, pullRequestPropMap)
526+
pullProtoEnt, err := ghprops.PullRequestV1FromProperties(pullEwp.Properties)
527+
require.NoError(t, err)
528+
529+
repoPropsEwp := buildEwp(t, repoEwp, pullRequestPropMap)
530+
531+
return fixtures.NewMockPropertiesService(
532+
fixtures.WithSuccessfulEntityByUpstreamHint(repoPropsEwp, githubHint),
533+
fixtures.WithSuccessfulEntityWithPropertiesAsProto(pullProtoEnt),
534+
fixtures.WithSuccessfulSaveAllProperties(),
535+
)
536+
},
537+
mockStoreFunc: df.NewMockStore(
538+
df.WithTransaction(),
539+
df.WithSuccessfulUpsertPullRequestWithParams(
540+
541+
db.EntityInstance{
542+
ID: pullRequestID,
543+
EntityType: db.EntitiesPullRequest,
544+
Name: "",
545+
ProjectID: projectID,
546+
ProviderID: providerID,
547+
OriginatedFrom: uuid.NullUUID{
548+
UUID: repoID,
549+
Valid: true,
550+
},
594551
},
595-
},
596-
db.CreateOrEnsureEntityByIDParams{
597-
ID: uuid.New(),
598-
EntityType: db.EntitiesPullRequest,
599-
Name: pullName,
600-
ProjectID: projectID,
601-
ProviderID: providerID,
602-
OriginatedFrom: uuid.NullUUID{
603-
UUID: repoID,
604-
Valid: true,
552+
db.CreateOrEnsureEntityByIDParams{
553+
ID: uuid.New(),
554+
EntityType: db.EntitiesPullRequest,
555+
Name: pullName,
556+
ProjectID: projectID,
557+
ProviderID: providerID,
558+
OriginatedFrom: uuid.NullUUID{
559+
UUID: repoID,
560+
Valid: true,
561+
},
605562
},
606-
},
563+
),
607564
),
608-
),
609-
providerSetup: newProviderMock(
610-
withSuccessfulGetEntityName(pullName),
611-
withSuccessfulFetchAllProperties(getPullRequestProperties()),
612-
WithSuccessfulPropertiesToProtoMessage(&pbinternal.PullRequest{
613-
Number: 789,
614-
}),
615-
),
616-
providerManagerSetup: func(prov provifv1.Provider) provManFixtures.ProviderManagerMockBuilder {
617-
return provManFixtures.NewProviderManagerMock(
618-
provManFixtures.WithSuccessfulInstantiateFromID(prov),
619-
)
565+
providerSetup: newProviderMock(
566+
withSuccessfulGetEntityName(pullName),
567+
withSuccessfulFetchAllProperties(getPullRequestProperties()),
568+
WithSuccessfulPropertiesToProtoMessage(&pbinternal.PullRequest{
569+
Number: 789,
570+
}),
571+
),
572+
providerManagerSetup: func(prov provifv1.Provider) provManFixtures.ProviderManagerMockBuilder {
573+
return provManFixtures.NewProviderManagerMock(
574+
provManFixtures.WithSuccessfulInstantiateFromID(prov),
575+
)
576+
},
577+
expectedPublish: true,
578+
topic: constants.TopicQueueEntityEvaluate,
579+
checkWmMsg: checkPullRequestMessage,
620580
},
621-
expectedPublish: true,
622-
topic: constants.TopicQueueEntityEvaluate,
623-
checkWmMsg: checkPullRequestMessage,
624-
},
625581
*/
626582
{
627583
name: "NewRemoveOriginatingEntityHandler: Happy path does not publish",

internal/entities/handlers/strategies/entity/add_originating_entity.go

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,13 @@ import (
77
"context"
88
"fmt"
99

10-
"github.com/google/uuid"
11-
"google.golang.org/protobuf/reflect/protoreflect"
12-
1310
"github.com/mindersec/minder/internal/db"
1411
"github.com/mindersec/minder/internal/entities/handlers/message"
1512
"github.com/mindersec/minder/internal/entities/handlers/strategies"
1613
"github.com/mindersec/minder/internal/entities/models"
1714
propertyService "github.com/mindersec/minder/internal/entities/properties/service"
1815
entityService "github.com/mindersec/minder/internal/entities/service"
1916
"github.com/mindersec/minder/internal/providers/manager"
20-
minderv1 "github.com/mindersec/minder/pkg/api/protobuf/go/minder/v1"
2117
"github.com/mindersec/minder/pkg/entities/properties"
2218
)
2319

@@ -84,14 +80,3 @@ func (a *addOriginatingEntityStrategy) GetEntity(
8480
func (*addOriginatingEntityStrategy) GetName() string {
8581
return "addOriginatingEntityStrategy"
8682
}
87-
88-
func (*addOriginatingEntityStrategy) upsertLegacyEntity(
89-
_ context.Context,
90-
_ minderv1.Entity,
91-
_ *models.EntityWithProperties, _ protoreflect.ProtoMessage,
92-
_ db.ExtendQuerier,
93-
) (uuid.UUID, error) {
94-
// Legacy entity writes have been removed as part of Phase 1 of the legacy table removal plan.
95-
// All entities are now written only to entity_instances and properties tables.
96-
return uuid.Nil, nil
97-
}

0 commit comments

Comments
 (0)