Skip to content

Commit b72ed16

Browse files
committed
Add general entity messages to registration-related calls
This allows us to use more general structures to handle repo registration as opposed to relying on the github-specific repo message. In the future, we'll have an `EntityService` that will work for any entity. But that's for another PR. Related-To: #4331 Signed-off-by: Juan Antonio Osorio <[email protected]>
1 parent ab6696c commit b72ed16

File tree

16 files changed

+3963
-3601
lines changed

16 files changed

+3963
-3601
lines changed

docs/docs/ref/proto.md

+14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/controlplane/handlers_entities.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,14 @@ func (s *Server) ReconcileEntityRegistration(
7979
}
8080

8181
for _, repo := range repos {
82-
if repo.Registered {
82+
if repo.Repo.Registered {
8383
continue
8484
}
8585

86-
msg, err := createEntityMessage(ctx, &l, projectID, providerID, repo.GetName(), repo.GetOwner())
86+
msg, err := createEntityMessage(ctx, &l, projectID, providerID, repo.Repo.GetName(), repo.Repo.GetOwner())
8787
if err != nil {
8888
l.Error().Err(err).
89-
Int64("repoID", repo.RepoId).
89+
Int64("repoID", repo.Repo.RepoId).
9090
Str("providerName", providerT.Name).
9191
Msg("error creating registration entity message")
9292
// This message will not be sent, but we can continue with the rest.

internal/controlplane/handlers_repositories.go

+143-35
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/rs/zerolog"
2727
"google.golang.org/grpc/codes"
2828
"google.golang.org/grpc/status"
29+
"google.golang.org/protobuf/types/known/structpb"
2930

3031
"github.com/stacklok/minder/internal/db"
3132
"github.com/stacklok/minder/internal/engine/engcontext"
@@ -56,32 +57,30 @@ func (s *Server) RegisterRepository(
5657
projectID := GetProjectID(ctx)
5758
providerName := GetProviderName(ctx)
5859

59-
// Validate that the Repository struct in the request
60-
githubRepo := in.GetRepository()
61-
// If the repo owner is missing, GitHub will assume a default value based
62-
// on the user's credentials. An explicit check for owner is left out to
63-
// avoid breaking backwards compatibility.
64-
if githubRepo.GetName() == "" {
65-
return nil, util.UserVisibleError(codes.InvalidArgument, "missing repository name")
60+
var fetchByProps *properties.Properties
61+
var provider *db.Provider
62+
var err error
63+
if in.GetEntity() != nil {
64+
fetchByProps, provider, err = s.repoCreateInfoFromUpstreamEntityRef(
65+
ctx, projectID, providerName, in.GetEntity())
66+
} else if in.GetRepository() != nil {
67+
fetchByProps, provider, err = s.repoCreateInfoFromUpstreamRepositoryRef(
68+
ctx, projectID, providerName, in.GetRepository())
69+
} else {
70+
return nil, util.UserVisibleError(codes.InvalidArgument, "missing entity or repository field")
71+
}
72+
73+
if err != nil {
74+
return nil, err
6675
}
6776

6877
l := zerolog.Ctx(ctx).With().
69-
Str("repoName", githubRepo.GetName()).
70-
Str("repoOwner", githubRepo.GetOwner()).
78+
Dict("properties", fetchByProps.ToLogDict()).
7179
Str("projectID", projectID.String()).
7280
Logger()
7381
ctx = l.WithContext(ctx)
7482

75-
provider, err := s.inferProviderByOwner(ctx, githubRepo.GetOwner(), projectID, providerName)
76-
if err != nil {
77-
pErr := providers.ErrProviderNotFoundBy{}
78-
if errors.As(err, &pErr) {
79-
return nil, util.UserVisibleError(codes.NotFound, "no suitable provider found, please enroll a provider")
80-
}
81-
return nil, status.Errorf(codes.Internal, "cannot get provider: %v", err)
82-
}
83-
84-
newRepo, err := s.repos.CreateRepository(ctx, provider, projectID, githubRepo.GetOwner(), githubRepo.GetName())
83+
newRepo, err := s.repos.CreateRepository(ctx, provider, projectID, fetchByProps)
8584
if err != nil {
8685
if errors.Is(err, repositories.ErrPrivateRepoForbidden) || errors.Is(err, repositories.ErrArchivedRepoForbidden) {
8786
return nil, util.UserVisibleError(codes.InvalidArgument, "%s", err.Error())
@@ -99,6 +98,62 @@ func (s *Server) RegisterRepository(
9998
}, nil
10099
}
101100

101+
func (s *Server) repoCreateInfoFromUpstreamRepositoryRef(
102+
ctx context.Context,
103+
projectID uuid.UUID,
104+
providerName string,
105+
rep *pb.UpstreamRepositoryRef,
106+
) (*properties.Properties, *db.Provider, error) {
107+
// If the repo owner is missing, GitHub will assume a default value based
108+
// on the user's credentials. An explicit check for owner is left out to
109+
// avoid breaking backwards compatibility.
110+
if rep.GetName() == "" {
111+
return nil, nil, util.UserVisibleError(codes.InvalidArgument, "missing repository name")
112+
}
113+
114+
fetchByProps, err := properties.NewProperties(map[string]any{
115+
properties.PropertyUpstreamID: fmt.Sprintf("%d", rep.GetRepoId()),
116+
properties.PropertyName: fmt.Sprintf("%s/%s", rep.GetOwner(), rep.GetName()),
117+
})
118+
if err != nil {
119+
return nil, nil, fmt.Errorf("error creating properties: %w", err)
120+
}
121+
122+
provider, err := s.inferProviderByOwner(ctx, rep.GetOwner(), projectID, providerName)
123+
if err != nil {
124+
pErr := providers.ErrProviderNotFoundBy{}
125+
if errors.As(err, &pErr) {
126+
return nil, nil, util.UserVisibleError(codes.NotFound, "no suitable provider found, please enroll a provider")
127+
}
128+
return nil, nil, status.Errorf(codes.Internal, "cannot get provider: %v", err)
129+
}
130+
131+
return fetchByProps, provider, nil
132+
}
133+
134+
func (s *Server) repoCreateInfoFromUpstreamEntityRef(
135+
ctx context.Context,
136+
projectID uuid.UUID,
137+
providerName string,
138+
entity *pb.UpstreamEntityRef,
139+
) (*properties.Properties, *db.Provider, error) {
140+
inPropsMap := entity.GetProperties().AsMap()
141+
fetchByProps, err := properties.NewProperties(inPropsMap)
142+
if err != nil {
143+
return nil, nil, fmt.Errorf("error creating properties: %w", err)
144+
}
145+
146+
provider, err := s.providerStore.GetByName(ctx, projectID, providerName)
147+
if err != nil {
148+
if errors.Is(err, sql.ErrNoRows) {
149+
return nil, nil, util.UserVisibleError(codes.NotFound, "provider not found")
150+
}
151+
return nil, nil, status.Errorf(codes.Internal, "cannot get provider: %v", err)
152+
}
153+
154+
return fetchByProps, provider, nil
155+
}
156+
102157
// ListRepositories returns a list of repositories for a given project
103158
// This function will typically be called by the client to get a list of
104159
// repositories that are registered present in the minder database
@@ -330,7 +385,8 @@ func (s *Server) ListRemoteRepositoriesFromProvider(
330385
}
331386

332387
out := &pb.ListRemoteRepositoriesFromProviderResponse{
333-
Results: []*pb.UpstreamRepositoryRef{},
388+
Results: []*pb.UpstreamRepositoryRef{},
389+
Entities: []*pb.RegistrableUpstreamEntityRef{},
334390
}
335391

336392
for providerID, providerT := range provs {
@@ -342,7 +398,10 @@ func (s *Server) ListRemoteRepositoriesFromProvider(
342398
errorProvs = append(errorProvs, providerT.Name)
343399
continue
344400
}
345-
out.Results = append(out.Results, results...)
401+
for _, result := range results {
402+
out.Results = append(out.Results, result.Repo)
403+
out.Entities = append(out.Entities, result.Entity)
404+
}
346405
}
347406

348407
// If all providers failed, return an error
@@ -362,7 +421,7 @@ func (s *Server) fetchRepositoriesForProvider(
362421
providerID uuid.UUID,
363422
providerName string,
364423
provider v1.Provider,
365-
) ([]*pb.UpstreamRepositoryRef, error) {
424+
) ([]*UpstreamRepoAndEntityRef, error) {
366425
zerolog.Ctx(ctx).Trace().
367426
Str("provider_id", providerID.String()).
368427
Str("project_id", projectID.String()).
@@ -412,10 +471,35 @@ func (s *Server) fetchRepositoriesForProvider(
412471
}
413472

414473
for _, result := range results {
415-
// TEMPORARY: This will be changed to use properties.
416-
// for now, we transform the repo ID to a string
417-
uid := fmt.Sprintf("%d", result.RepoId)
418-
result.Registered = registered[uid]
474+
uprops := result.Entity.GetEntity().GetProperties()
475+
upropsMap := uprops.AsMap()
476+
if upropsMap == nil {
477+
zerolog.Ctx(ctx).Warn().
478+
Str("provider_id", providerID.String()).
479+
Str("project_id", projectID.String()).
480+
Msg("upstream repository entry has no properties")
481+
continue
482+
}
483+
uidAny, ok := upropsMap[properties.PropertyUpstreamID]
484+
if !ok {
485+
zerolog.Ctx(ctx).Warn().
486+
Str("provider_id", providerID.String()).
487+
Str("project_id", projectID.String()).
488+
Msg("upstream repository entry has no upstream ID")
489+
continue
490+
}
491+
492+
uid, ok := uidAny.(string)
493+
if !ok {
494+
zerolog.Ctx(ctx).Warn().
495+
Str("provider_id", providerID.String()).
496+
Str("project_id", projectID.String()).
497+
Msg("upstream repository entry has invalid upstream ID")
498+
continue
499+
}
500+
501+
result.Repo.Registered = registered[uid]
502+
result.Entity.Registered = registered[uid]
419503
}
420504

421505
return results, nil
@@ -426,7 +510,7 @@ func (s *Server) listRemoteRepositoriesForProvider(
426510
provName string,
427511
repoLister v1.RepoLister,
428512
projectID uuid.UUID,
429-
) ([]*pb.UpstreamRepositoryRef, error) {
513+
) ([]*UpstreamRepoAndEntityRef, error) {
430514
tmoutCtx, cancel := context.WithTimeout(ctx, github.ExpensiveRestCallTimeout)
431515
defer cancel()
432516

@@ -442,22 +526,40 @@ func (s *Server) listRemoteRepositoriesForProvider(
442526
zerolog.Ctx(ctx).Info().Msg("including private repositories")
443527
}
444528

445-
results := make([]*pb.UpstreamRepositoryRef, 0, len(remoteRepos))
529+
results := make([]*UpstreamRepoAndEntityRef, 0, len(remoteRepos))
446530

447531
for idx, rem := range remoteRepos {
448532
// Skip private repositories
449533
if rem.IsPrivate && !allowsPrivateRepos {
450534
continue
451535
}
452536
remoteRepo := remoteRepos[idx]
453-
repo := &pb.UpstreamRepositoryRef{
454-
Context: &pb.Context{
455-
Provider: &provName,
456-
Project: ptr.Ptr(projectID.String()),
537+
538+
var props *structpb.Struct
539+
if remoteRepo.Properties != nil {
540+
props = remoteRepo.Properties
541+
}
542+
543+
repo := &UpstreamRepoAndEntityRef{
544+
Repo: &pb.UpstreamRepositoryRef{
545+
Context: &pb.Context{
546+
Provider: &provName,
547+
Project: ptr.Ptr(projectID.String()),
548+
},
549+
Owner: remoteRepo.Owner,
550+
Name: remoteRepo.Name,
551+
RepoId: remoteRepo.RepoId,
552+
},
553+
Entity: &pb.RegistrableUpstreamEntityRef{
554+
Entity: &pb.UpstreamEntityRef{
555+
Context: &pb.ContextV2{
556+
Provider: provName,
557+
ProjectId: projectID.String(),
558+
},
559+
Type: pb.Entity_ENTITY_REPOSITORIES,
560+
Properties: props,
561+
},
457562
},
458-
Owner: remoteRepo.Owner,
459-
Name: remoteRepo.Name,
460-
RepoId: remoteRepo.RepoId,
461563
}
462564
results = append(results, repo)
463565
}
@@ -496,3 +598,9 @@ func (s *Server) inferProviderByOwner(ctx context.Context, owner string, project
496598

497599
return nil, fmt.Errorf("no providers can handle repo owned by %s", owner)
498600
}
601+
602+
// UpstreamRepoAndEntityRef is a pair of upstream repository and entity references
603+
type UpstreamRepoAndEntityRef struct {
604+
Repo *pb.UpstreamRepositoryRef
605+
Entity *pb.RegistrableUpstreamEntityRef
606+
}

0 commit comments

Comments
 (0)