Skip to content

Commit 7156326

Browse files
committed
make context fields optional
Signed-off-by: Javier Rodriguez <javier@chainloop.dev>
1 parent c299c71 commit 7156326

6 files changed

Lines changed: 95 additions & 59 deletions

File tree

app/controlplane/pkg/biz/group.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ func (uc *GroupUseCase) handleNonExistingUser(ctx context.Context, orgID, groupI
557557

558558
// Create an organization invitation with group context
559559
invitationContext := &OrgInvitationContext{
560-
GroupIDToJoin: groupID,
560+
GroupIDToJoin: &groupID,
561561
GroupMaintainer: opts.Maintainer,
562562
}
563563

app/controlplane/pkg/biz/orginvitation.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ type OrgInvitation struct {
5959
// OrgInvitationContext is used to pass additional context when accepting an invitation
6060
type OrgInvitationContext struct {
6161
// GroupIDToJoin is the ID of the group to join when accepting the invitation
62-
GroupIDToJoin uuid.UUID `json:"group_id_to_join,omitempty"`
62+
GroupIDToJoin *uuid.UUID `json:"group_id_to_join,omitempty"`
6363
// GroupMaintainer indicates if the user should be added as a maintainer of the group
6464
GroupMaintainer bool `json:"group_maintainer,omitempty"`
6565
// ProjectIDToJoin is the ID of the project to join when accepting the invitation
66-
ProjectIDToJoin uuid.UUID `json:"project_id_to_join,omitempty"`
66+
ProjectIDToJoin *uuid.UUID `json:"project_id_to_join,omitempty"`
6767
// ProjectRole is the role to assign to the user in the project
6868
ProjectRole authz.Role `json:"project_role,omitempty"`
6969
// ExternalMetadata can be used to store additional information
@@ -341,20 +341,20 @@ func (uc *OrgInvitationUseCase) FindByID(ctx context.Context, invitationID strin
341341
// processGroupMembership adds a user to a group if the invitation context contains a group to join
342342
func (uc *OrgInvitationUseCase) processGroupMembership(ctx context.Context, invitation *OrgInvitation, orgUUID uuid.UUID, userUUID uuid.UUID) error {
343343
// Skip if there's no group to join in the invitation context
344-
if invitation.Context == nil || invitation.Context.GroupIDToJoin == uuid.Nil {
344+
if invitation.Context == nil || invitation.Context.GroupIDToJoin == nil || *invitation.Context.GroupIDToJoin == uuid.Nil {
345345
return nil
346346
}
347347

348348
groupID := invitation.Context.GroupIDToJoin
349349
uc.logger.Infow("msg", "Adding user to group", "invitation_id", invitation.ID.String(), "org_id", invitation.Org.ID, "user_id", userUUID, "group_id", groupID)
350350

351351
// Check if the group exists
352-
gr, err := uc.groupRepo.FindByOrgAndID(ctx, orgUUID, groupID)
352+
gr, err := uc.groupRepo.FindByOrgAndID(ctx, orgUUID, *groupID)
353353
if err != nil {
354354
return fmt.Errorf("error finding group %s: %w", groupID.String(), err)
355355
}
356356

357-
if _, err := uc.groupRepo.AddMemberToGroup(ctx, orgUUID, groupID, userUUID, invitation.Context.GroupMaintainer); err != nil {
357+
if _, err := uc.groupRepo.AddMemberToGroup(ctx, orgUUID, *groupID, userUUID, invitation.Context.GroupMaintainer); err != nil {
358358
if IsErrAlreadyExists(err) {
359359
// User is already a member of the group, nothing to do
360360
uc.logger.Infow("msg", "User already in group", "invitation_id", invitation.ID.String(), "org_id", invitation.Org.ID, "user_id", userUUID.String(), "group_id", groupID.String())
@@ -367,7 +367,7 @@ func (uc *OrgInvitationUseCase) processGroupMembership(ctx context.Context, invi
367367
// Dispatch event to the audit log for group membership addition
368368
uc.auditor.Dispatch(ctx, &events.GroupMemberAdded{
369369
GroupBase: &events.GroupBase{
370-
GroupID: &groupID,
370+
GroupID: groupID,
371371
GroupName: gr.Name,
372372
},
373373
UserID: &userUUID,
@@ -383,15 +383,15 @@ func (uc *OrgInvitationUseCase) processGroupMembership(ctx context.Context, invi
383383
// processProjectMembership adds a user to a project if the invitation context contains a project to join
384384
func (uc *OrgInvitationUseCase) processProjectMembership(ctx context.Context, invitation *OrgInvitation, orgUUID uuid.UUID, userUUID uuid.UUID) error {
385385
// Skip if there's no group to join in the invitation context
386-
if invitation.Context == nil || invitation.Context.ProjectIDToJoin == uuid.Nil {
386+
if invitation.Context == nil || invitation.Context.ProjectIDToJoin == nil || *invitation.Context.ProjectIDToJoin == uuid.Nil {
387387
return nil
388388
}
389389

390390
projectID := invitation.Context.ProjectIDToJoin
391391
uc.logger.Infow("msg", "Adding user to project", "invitation_id", invitation.ID.String(), "org_id", invitation.Org.ID, "user_id", userUUID, "project_id", projectID)
392392

393393
// Check if the project exists
394-
project, err := uc.projectRepo.FindProjectByOrgIDAndID(ctx, orgUUID, projectID)
394+
project, err := uc.projectRepo.FindProjectByOrgIDAndID(ctx, orgUUID, *projectID)
395395
if err != nil {
396396
return fmt.Errorf("error finding project %s: %w", projectID.String(), err)
397397
}
@@ -409,7 +409,7 @@ func (uc *OrgInvitationUseCase) processProjectMembership(ctx context.Context, in
409409
}
410410

411411
// Check if the user is already a member of the project
412-
existingMembership, err := uc.projectRepo.FindProjectMembershipByProjectAndID(ctx, orgUUID, projectID, userUUID, authz.MembershipTypeUser)
412+
existingMembership, err := uc.projectRepo.FindProjectMembershipByProjectAndID(ctx, orgUUID, *projectID, userUUID, authz.MembershipTypeUser)
413413
if err != nil && !IsNotFound(err) {
414414
return fmt.Errorf("error checking project membership for user %s: %w", userUUID, err)
415415
}
@@ -421,14 +421,14 @@ func (uc *OrgInvitationUseCase) processProjectMembership(ctx context.Context, in
421421
}
422422

423423
// Add the user to the project
424-
if _, err := uc.projectRepo.AddMemberToProject(ctx, orgUUID, projectID, userUUID, authz.MembershipTypeUser, role); err != nil {
424+
if _, err := uc.projectRepo.AddMemberToProject(ctx, orgUUID, *projectID, userUUID, authz.MembershipTypeUser, role); err != nil {
425425
return fmt.Errorf("error adding user %s to project %s: %w", userUUID, projectID.String(), err)
426426
}
427427

428428
// Dispatch event to the audit log for project membership addition
429429
uc.auditor.Dispatch(ctx, &events.ProjectMembershipAdded{
430430
ProjectBase: &events.ProjectBase{
431-
ProjectID: &projectID,
431+
ProjectID: projectID,
432432
ProjectName: project.Name,
433433
},
434434
UserID: &userUUID,

app/controlplane/pkg/biz/orginvitation_integration_test.go

Lines changed: 66 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ func (s *OrgInvitationIntegrationTestSuite) TestInvitationWithGroupContext() {
314314
s.T().Run("invitation with group context adds user to group when accepted", func(t *testing.T) {
315315
// Create invitation context with group information
316316
invitationContext := &biz.OrgInvitationContext{
317-
GroupIDToJoin: group.ID,
317+
GroupIDToJoin: &group.ID,
318318
GroupMaintainer: true,
319319
}
320320

@@ -332,7 +332,7 @@ func (s *OrgInvitationIntegrationTestSuite) TestInvitationWithGroupContext() {
332332

333333
// Verify context was saved properly
334334
assert.NotNil(t, invite.Context)
335-
assert.Equal(t, group.ID, invite.Context.GroupIDToJoin)
335+
assert.Equal(t, group.ID, *invite.Context.GroupIDToJoin)
336336
assert.Equal(t, true, invite.Context.GroupMaintainer)
337337

338338
// Accept the invitation
@@ -380,7 +380,7 @@ func (s *OrgInvitationIntegrationTestSuite) TestInvitationWithGroupContext() {
380380

381381
// Create invitation context with group information, but not as maintainer
382382
invitationContext := &biz.OrgInvitationContext{
383-
GroupIDToJoin: group.ID,
383+
GroupIDToJoin: &group.ID,
384384
GroupMaintainer: false,
385385
}
386386

@@ -447,7 +447,7 @@ func (s *OrgInvitationIntegrationTestSuite) TestInvitationWithProjectContext() {
447447
s.T().Run("invitation with project context adds user to project when accepted", func(t *testing.T) {
448448
// Create invitation context with project information
449449
invitationContext := &biz.OrgInvitationContext{
450-
ProjectIDToJoin: project.ID,
450+
ProjectIDToJoin: &project.ID,
451451
ProjectRole: authz.RoleProjectAdmin,
452452
}
453453

@@ -465,7 +465,7 @@ func (s *OrgInvitationIntegrationTestSuite) TestInvitationWithProjectContext() {
465465

466466
// Verify context was saved properly
467467
assert.NotNil(t, invite.Context)
468-
assert.Equal(t, project.ID, invite.Context.ProjectIDToJoin)
468+
assert.Equal(t, project.ID, *invite.Context.ProjectIDToJoin)
469469
assert.Equal(t, authz.RoleProjectAdmin, invite.Context.ProjectRole)
470470

471471
// Accept the invitation
@@ -511,7 +511,7 @@ func (s *OrgInvitationIntegrationTestSuite) TestInvitationWithProjectContext() {
511511

512512
// Create invitation context with project information, but with viewer role
513513
invitationContext := &biz.OrgInvitationContext{
514-
ProjectIDToJoin: project.ID,
514+
ProjectIDToJoin: &project.ID,
515515
ProjectRole: authz.RoleProjectViewer,
516516
}
517517

@@ -570,9 +570,9 @@ func (s *OrgInvitationIntegrationTestSuite) TestInvitationWithProjectContext() {
570570

571571
// Create invitation context with both group and project information
572572
invitationContext := &biz.OrgInvitationContext{
573-
GroupIDToJoin: group.ID,
573+
GroupIDToJoin: &group.ID,
574574
GroupMaintainer: true,
575-
ProjectIDToJoin: project.ID,
575+
ProjectIDToJoin: &project.ID,
576576
ProjectRole: authz.RoleProjectViewer,
577577
}
578578

@@ -590,9 +590,9 @@ func (s *OrgInvitationIntegrationTestSuite) TestInvitationWithProjectContext() {
590590

591591
// Verify context was saved properly
592592
assert.NotNil(t, invite.Context)
593-
assert.Equal(t, group.ID, invite.Context.GroupIDToJoin)
593+
assert.Equal(t, group.ID, *invite.Context.GroupIDToJoin)
594594
assert.True(t, invite.Context.GroupMaintainer)
595-
assert.Equal(t, project.ID, invite.Context.ProjectIDToJoin)
595+
assert.Equal(t, project.ID, *invite.Context.ProjectIDToJoin)
596596
assert.Equal(t, authz.RoleProjectViewer, invite.Context.ProjectRole)
597597

598598
// Accept the invitation
@@ -645,30 +645,64 @@ func (s *OrgInvitationIntegrationTestSuite) TestInvitationWithProjectContext() {
645645
assert.True(t, foundProjectMember, "The user should be a member of the project")
646646
assert.Equal(t, authz.RoleProjectViewer, projectRole, "The user should have the project contributor role")
647647
})
648-
}
649648

650-
func (s *OrgInvitationIntegrationTestSuite) TestInvitationWithExternalMetadata() {
651-
ctx := context.Background()
652-
receiverEmail := "externalmeta@cyberdyne.io"
653-
externalMeta := []byte(`{"foo":"bar","baz":123}`)
654-
invitationContext := &biz.OrgInvitationContext{
655-
ExternalMetadata: externalMeta,
656-
}
649+
s.T().Run("invitation with nil UUID on project is rejected", func(t *testing.T) {
650+
// Create a new receiver that isn't a member of any org yet
651+
newReceiverEmail := "combined-receiver@cyberdyne.io"
652+
newReceiver, err := s.User.UpsertByEmail(ctx, newReceiverEmail, nil)
653+
require.NoError(t, err)
654+
require.NotNil(t, newReceiver)
655+
656+
// Create invitation context with nil project ID
657+
invitationContext := &biz.OrgInvitationContext{
658+
ProjectIDToJoin: &uuid.Nil,
659+
}
657660

658-
invite, err := s.OrgInvitation.Create(
659-
ctx,
660-
s.org1.ID,
661-
s.user.ID,
662-
receiverEmail,
663-
biz.WithInvitationRole(authz.RoleViewer),
664-
biz.WithInvitationContext(invitationContext),
665-
)
666-
s.Require().NoError(err)
667-
s.Require().NotNil(invite)
668-
669-
s.Require().NotNil(invite.Context)
670-
s.Require().NotNil(invite.Context.ExternalMetadata)
671-
s.JSONEq(string(externalMeta), string(invite.Context.ExternalMetadata), "ExternalMetadata should be persisted and retrievable")
661+
// Create invitation with combined context
662+
invite, err := s.Repos.OrgInvitationRepo.Create(
663+
ctx,
664+
uuid.MustParse(s.org1.ID),
665+
uuid.MustParse(s.user.ID),
666+
newReceiverEmail,
667+
authz.RoleViewer,
668+
invitationContext,
669+
)
670+
require.NoError(t, err)
671+
require.NotNil(t, invite)
672+
673+
// Accept the invitation and check that there is no error
674+
err = s.OrgInvitation.AcceptPendingInvitations(ctx, newReceiverEmail)
675+
require.NoError(t, err, "Accepting invitation with nil project ID should not fail just skip the project context")
676+
})
677+
678+
s.T().Run("invitation with nil UUID on group is rejected", func(t *testing.T) {
679+
// Create a new receiver that isn't a member of any org yet
680+
newReceiverEmail := "combined-receiver@cyberdyne.io"
681+
newReceiver, err := s.User.UpsertByEmail(ctx, newReceiverEmail, nil)
682+
require.NoError(t, err)
683+
require.NotNil(t, newReceiver)
684+
685+
// Create invitation context with nil group ID
686+
invitationContext := &biz.OrgInvitationContext{
687+
GroupIDToJoin: &uuid.Nil,
688+
}
689+
690+
// Create invitation with combined context
691+
invite, err := s.Repos.OrgInvitationRepo.Create(
692+
ctx,
693+
uuid.MustParse(s.org1.ID),
694+
uuid.MustParse(s.user.ID),
695+
newReceiverEmail,
696+
authz.RoleViewer,
697+
invitationContext,
698+
)
699+
require.NoError(t, err)
700+
require.NotNil(t, invite)
701+
702+
// Accept the invitation and check that there is no error
703+
err = s.OrgInvitation.AcceptPendingInvitations(ctx, newReceiverEmail)
704+
require.NoError(t, err, "Accepting invitation with nil group ID should not fail just skip the project context")
705+
})
672706
}
673707

674708
// Utility struct to hold the test suite

app/controlplane/pkg/biz/project.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ func (uc *ProjectUseCase) handleNonExistingUser(ctx context.Context, orgID, proj
392392

393393
// Create an organization invitation with project context
394394
invitationContext := &OrgInvitationContext{
395-
ProjectIDToJoin: projectID,
395+
ProjectIDToJoin: &projectID,
396396
ProjectRole: opts.Role,
397397
}
398398

app/controlplane/pkg/biz/testhelpers/database.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,14 @@ type TestingUseCases struct {
8282
}
8383

8484
type TestingRepos struct {
85-
Membership biz.MembershipRepo
86-
Referrer biz.ReferrerRepo
87-
Workflow biz.WorkflowRepo
88-
WorkflowRunRepo biz.WorkflowRunRepo
89-
AttestationState biz.AttestationStateRepo
90-
OrganizationRepo biz.OrganizationRepo
91-
GroupRepo biz.GroupRepo
85+
Membership biz.MembershipRepo
86+
Referrer biz.ReferrerRepo
87+
Workflow biz.WorkflowRepo
88+
WorkflowRunRepo biz.WorkflowRunRepo
89+
AttestationState biz.AttestationStateRepo
90+
OrganizationRepo biz.OrganizationRepo
91+
GroupRepo biz.GroupRepo
92+
OrgInvitationRepo biz.OrgInvitationRepo
9293
}
9394

9495
type newTestingOpts struct {

app/controlplane/pkg/biz/testhelpers/wire_gen.go

Lines changed: 8 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)