satellite/console: fix transaction error when inviting project members
The SQL transaction that inserted project invitations relied on the error result of one of its statements in order to determine whether an invitation should be updated. This was inappropriate since any errors returned from a transaction statement should end the transaction immediately. This change resolves that issue. Change-Id: I354e430df293054d8583fb4faa5dc1bcf9053836
This commit is contained in:
parent
1b912ec167
commit
22f8b029b9
@ -14,16 +14,14 @@ import (
|
|||||||
//
|
//
|
||||||
// architecture: Database
|
// architecture: Database
|
||||||
type ProjectInvitations interface {
|
type ProjectInvitations interface {
|
||||||
// Insert inserts a project member invitation into the database.
|
// Upsert updates a project member invitation if it exists and inserts it otherwise.
|
||||||
Insert(ctx context.Context, invite *ProjectInvitation) (*ProjectInvitation, error)
|
Upsert(ctx context.Context, invite *ProjectInvitation) (*ProjectInvitation, error)
|
||||||
// Get returns a project member invitation from the database.
|
// Get returns a project member invitation from the database.
|
||||||
Get(ctx context.Context, projectID uuid.UUID, email string) (*ProjectInvitation, error)
|
Get(ctx context.Context, projectID uuid.UUID, email string) (*ProjectInvitation, error)
|
||||||
// GetByProjectID returns all of the project member invitations for the project specified by the given ID.
|
// GetByProjectID returns all of the project member invitations for the project specified by the given ID.
|
||||||
GetByProjectID(ctx context.Context, projectID uuid.UUID) ([]ProjectInvitation, error)
|
GetByProjectID(ctx context.Context, projectID uuid.UUID) ([]ProjectInvitation, error)
|
||||||
// GetByEmail returns all of the project member invitations for the specified email address.
|
// GetByEmail returns all of the project member invitations for the specified email address.
|
||||||
GetByEmail(ctx context.Context, email string) ([]ProjectInvitation, error)
|
GetByEmail(ctx context.Context, email string) ([]ProjectInvitation, error)
|
||||||
// Update updates the project member invitation specified by the given project ID and email address.
|
|
||||||
Update(ctx context.Context, projectID uuid.UUID, email string, request UpdateProjectInvitationRequest) (*ProjectInvitation, error)
|
|
||||||
// Delete removes a project member invitation from the database.
|
// Delete removes a project member invitation from the database.
|
||||||
Delete(ctx context.Context, projectID uuid.UUID, email string) error
|
Delete(ctx context.Context, projectID uuid.UUID, email string) error
|
||||||
// DeleteBefore deletes project member invitations created prior to some time from the database.
|
// DeleteBefore deletes project member invitations created prior to some time from the database.
|
||||||
@ -37,9 +35,3 @@ type ProjectInvitation struct {
|
|||||||
InviterID *uuid.UUID
|
InviterID *uuid.UUID
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateProjectInvitationRequest contains all fields which may be updated by ProjectInvitations.Update.
|
|
||||||
type UpdateProjectInvitationRequest struct {
|
|
||||||
CreatedAt *time.Time
|
|
||||||
InviterID *uuid.UUID
|
|
||||||
}
|
|
||||||
|
@ -3609,24 +3609,14 @@ func (s *Service) InviteProjectMembers(ctx context.Context, projectID uuid.UUID,
|
|||||||
// add project invites in transaction scope
|
// add project invites in transaction scope
|
||||||
err = s.store.WithTx(ctx, func(ctx context.Context, tx DBTx) error {
|
err = s.store.WithTx(ctx, func(ctx context.Context, tx DBTx) error {
|
||||||
for _, invited := range users {
|
for _, invited := range users {
|
||||||
invite, err := tx.ProjectInvitations().Insert(ctx, &ProjectInvitation{
|
invite, err := tx.ProjectInvitations().Upsert(ctx, &ProjectInvitation{
|
||||||
ProjectID: projectID,
|
ProjectID: projectID,
|
||||||
Email: invited.Email,
|
Email: invited.Email,
|
||||||
InviterID: &user.ID,
|
InviterID: &user.ID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !dbx.IsConstraintError(err) {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
now := time.Now()
|
|
||||||
invite, err = tx.ProjectInvitations().Update(ctx, projectID, invited.Email, UpdateProjectInvitationRequest{
|
|
||||||
CreatedAt: &now,
|
|
||||||
InviterID: &user.ID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
token, err := s.CreateInviteToken(ctx, isMember.project.PublicID, invited.Email, invite.CreatedAt)
|
token, err := s.CreateInviteToken(ctx, isMember.project.PublicID, invited.Email, invite.CreatedAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -314,7 +315,7 @@ func TestService(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for _, id := range []uuid.UUID{up1Proj.ID, up2Proj.ID} {
|
for _, id := range []uuid.UUID{up1Proj.ID, up2Proj.ID} {
|
||||||
_, err = sat.DB.Console().ProjectInvitations().Insert(ctx, &console.ProjectInvitation{
|
_, err = sat.DB.Console().ProjectInvitations().Upsert(ctx, &console.ProjectInvitation{
|
||||||
ProjectID: id,
|
ProjectID: id,
|
||||||
Email: invitedUser.Email,
|
Email: invitedUser.Email,
|
||||||
})
|
})
|
||||||
@ -1975,7 +1976,7 @@ func TestProjectInvitations(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addInvite := func(t *testing.T, ctx context.Context, project *console.Project, email string) *console.ProjectInvitation {
|
addInvite := func(t *testing.T, ctx context.Context, project *console.Project, email string) *console.ProjectInvitation {
|
||||||
invite, err := sat.DB.Console().ProjectInvitations().Insert(ctx, &console.ProjectInvitation{
|
invite, err := sat.DB.Console().ProjectInvitations().Upsert(ctx, &console.ProjectInvitation{
|
||||||
ProjectID: project.ID,
|
ProjectID: project.ID,
|
||||||
Email: email,
|
Email: email,
|
||||||
InviterID: &project.OwnerID,
|
InviterID: &project.OwnerID,
|
||||||
@ -1985,11 +1986,18 @@ func TestProjectInvitations(t *testing.T) {
|
|||||||
return invite
|
return invite
|
||||||
}
|
}
|
||||||
|
|
||||||
expireInvite := func(t *testing.T, ctx context.Context, invite *console.ProjectInvitation) {
|
setInviteDate := func(t *testing.T, ctx context.Context, invite *console.ProjectInvitation, createdAt time.Time) {
|
||||||
createdAt := time.Now().Add(-sat.Config.Console.ProjectInvitationExpiration)
|
result, err := sat.DB.Testing().RawDB().ExecContext(ctx,
|
||||||
newInvite, err := sat.DB.Console().ProjectInvitations().Update(ctx, invite.ProjectID, invite.Email, console.UpdateProjectInvitationRequest{
|
"UPDATE project_invitations SET created_at = $1 WHERE project_id = $2 AND email = $3",
|
||||||
CreatedAt: &createdAt,
|
createdAt, invite.ProjectID, strings.ToUpper(invite.Email),
|
||||||
})
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
count, err := result.RowsAffected()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, 1, count)
|
||||||
|
|
||||||
|
newInvite, err := sat.DB.Console().ProjectInvitations().Get(ctx, invite.ProjectID, invite.Email)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
*invite = *newInvite
|
*invite = *newInvite
|
||||||
}
|
}
|
||||||
@ -2035,7 +2043,7 @@ func TestProjectInvitations(t *testing.T) {
|
|||||||
// expire the invitation.
|
// expire the invitation.
|
||||||
require.False(t, service.IsProjectInvitationExpired(&user3Invite))
|
require.False(t, service.IsProjectInvitationExpired(&user3Invite))
|
||||||
oldCreatedAt := user3Invite.CreatedAt
|
oldCreatedAt := user3Invite.CreatedAt
|
||||||
expireInvite(t, ctx, &user3Invite)
|
setInviteDate(t, ctx, &user3Invite, time.Now().Add(-sat.Config.Console.ProjectInvitationExpiration))
|
||||||
require.True(t, service.IsProjectInvitationExpired(&user3Invite))
|
require.True(t, service.IsProjectInvitationExpired(&user3Invite))
|
||||||
|
|
||||||
// resending an expired invitation should succeed.
|
// resending an expired invitation should succeed.
|
||||||
@ -2066,7 +2074,7 @@ func TestProjectInvitations(t *testing.T) {
|
|||||||
require.Equal(t, invite.InviterID, invites[0].InviterID)
|
require.Equal(t, invite.InviterID, invites[0].InviterID)
|
||||||
require.WithinDuration(t, invite.CreatedAt, invites[0].CreatedAt, time.Second)
|
require.WithinDuration(t, invite.CreatedAt, invites[0].CreatedAt, time.Second)
|
||||||
|
|
||||||
expireInvite(t, ctx, &invites[0])
|
setInviteDate(t, ctx, &invites[0], time.Now().Add(-sat.Config.Console.ProjectInvitationExpiration))
|
||||||
invites, err = service.GetUserProjectInvitations(ctx)
|
invites, err = service.GetUserProjectInvitations(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Empty(t, invites)
|
require.Empty(t, invites)
|
||||||
@ -2155,7 +2163,7 @@ func TestProjectInvitations(t *testing.T) {
|
|||||||
require.NotNil(t, inviteFromToken)
|
require.NotNil(t, inviteFromToken)
|
||||||
require.Equal(t, invite, inviteFromToken)
|
require.Equal(t, invite, inviteFromToken)
|
||||||
|
|
||||||
expireInvite(t, ctx, invite)
|
setInviteDate(t, ctx, invite, time.Now().Add(-sat.Config.Console.ProjectInvitationExpiration))
|
||||||
invites, err := service.GetUserProjectInvitations(ctx)
|
invites, err := service.GetUserProjectInvitations(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Empty(t, invites)
|
require.Empty(t, invites)
|
||||||
@ -2178,7 +2186,7 @@ func TestProjectInvitations(t *testing.T) {
|
|||||||
proj := addProject(t, ctx)
|
proj := addProject(t, ctx)
|
||||||
|
|
||||||
invite := addInvite(t, ctx, proj, user.Email)
|
invite := addInvite(t, ctx, proj, user.Email)
|
||||||
expireInvite(t, ctx, invite)
|
setInviteDate(t, ctx, invite, time.Now().Add(-sat.Config.Console.ProjectInvitationExpiration))
|
||||||
err := service.RespondToProjectInvitation(ctx, proj.ID, console.ProjectInvitationAccept)
|
err := service.RespondToProjectInvitation(ctx, proj.ID, console.ProjectInvitationAccept)
|
||||||
require.True(t, console.ErrProjectInviteInvalid.Has(err))
|
require.True(t, console.ErrProjectInviteInvalid.Has(err))
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ model project_invitation (
|
|||||||
field created_at timestamp ( autoinsert, updatable )
|
field created_at timestamp ( autoinsert, updatable )
|
||||||
)
|
)
|
||||||
|
|
||||||
create project_invitation ( )
|
create project_invitation ( replace )
|
||||||
|
|
||||||
read one (
|
read one (
|
||||||
select project_invitation
|
select project_invitation
|
||||||
|
@ -12869,7 +12869,7 @@ func (obj *pgxImpl) Create_ProjectMember(ctx context.Context,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (obj *pgxImpl) Create_ProjectInvitation(ctx context.Context,
|
func (obj *pgxImpl) Replace_ProjectInvitation(ctx context.Context,
|
||||||
project_invitation_project_id ProjectInvitation_ProjectId_Field,
|
project_invitation_project_id ProjectInvitation_ProjectId_Field,
|
||||||
project_invitation_email ProjectInvitation_Email_Field,
|
project_invitation_email ProjectInvitation_Email_Field,
|
||||||
optional ProjectInvitation_Create_Fields) (
|
optional ProjectInvitation_Create_Fields) (
|
||||||
@ -12882,7 +12882,7 @@ func (obj *pgxImpl) Create_ProjectInvitation(ctx context.Context,
|
|||||||
__inviter_id_val := optional.InviterId.value()
|
__inviter_id_val := optional.InviterId.value()
|
||||||
__created_at_val := __now
|
__created_at_val := __now
|
||||||
|
|
||||||
var __embed_stmt = __sqlbundle_Literal("INSERT INTO project_invitations ( project_id, email, inviter_id, created_at ) VALUES ( ?, ?, ?, ? ) RETURNING project_invitations.project_id, project_invitations.email, project_invitations.inviter_id, project_invitations.created_at")
|
var __embed_stmt = __sqlbundle_Literal("INSERT INTO project_invitations ( project_id, email, inviter_id, created_at ) VALUES ( ?, ?, ?, ? ) ON CONFLICT ( project_id, email ) DO UPDATE SET project_id = EXCLUDED.project_id, email = EXCLUDED.email, inviter_id = EXCLUDED.inviter_id, created_at = EXCLUDED.created_at RETURNING project_invitations.project_id, project_invitations.email, project_invitations.inviter_id, project_invitations.created_at")
|
||||||
|
|
||||||
var __values []interface{}
|
var __values []interface{}
|
||||||
__values = append(__values, __project_id_val, __email_val, __inviter_id_val, __created_at_val)
|
__values = append(__values, __project_id_val, __email_val, __inviter_id_val, __created_at_val)
|
||||||
@ -20876,7 +20876,7 @@ func (obj *pgxcockroachImpl) Create_ProjectMember(ctx context.Context,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (obj *pgxcockroachImpl) Create_ProjectInvitation(ctx context.Context,
|
func (obj *pgxcockroachImpl) Replace_ProjectInvitation(ctx context.Context,
|
||||||
project_invitation_project_id ProjectInvitation_ProjectId_Field,
|
project_invitation_project_id ProjectInvitation_ProjectId_Field,
|
||||||
project_invitation_email ProjectInvitation_Email_Field,
|
project_invitation_email ProjectInvitation_Email_Field,
|
||||||
optional ProjectInvitation_Create_Fields) (
|
optional ProjectInvitation_Create_Fields) (
|
||||||
@ -20889,7 +20889,7 @@ func (obj *pgxcockroachImpl) Create_ProjectInvitation(ctx context.Context,
|
|||||||
__inviter_id_val := optional.InviterId.value()
|
__inviter_id_val := optional.InviterId.value()
|
||||||
__created_at_val := __now
|
__created_at_val := __now
|
||||||
|
|
||||||
var __embed_stmt = __sqlbundle_Literal("INSERT INTO project_invitations ( project_id, email, inviter_id, created_at ) VALUES ( ?, ?, ?, ? ) RETURNING project_invitations.project_id, project_invitations.email, project_invitations.inviter_id, project_invitations.created_at")
|
var __embed_stmt = __sqlbundle_Literal("UPSERT INTO project_invitations ( project_id, email, inviter_id, created_at ) VALUES ( ?, ?, ?, ? ) RETURNING project_invitations.project_id, project_invitations.email, project_invitations.inviter_id, project_invitations.created_at")
|
||||||
|
|
||||||
var __values []interface{}
|
var __values []interface{}
|
||||||
__values = append(__values, __project_id_val, __email_val, __inviter_id_val, __created_at_val)
|
__values = append(__values, __project_id_val, __email_val, __inviter_id_val, __created_at_val)
|
||||||
@ -28506,19 +28506,6 @@ func (rx *Rx) Create_Project(ctx context.Context,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rx *Rx) Create_ProjectInvitation(ctx context.Context,
|
|
||||||
project_invitation_project_id ProjectInvitation_ProjectId_Field,
|
|
||||||
project_invitation_email ProjectInvitation_Email_Field,
|
|
||||||
optional ProjectInvitation_Create_Fields) (
|
|
||||||
project_invitation *ProjectInvitation, err error) {
|
|
||||||
var tx *Tx
|
|
||||||
if tx, err = rx.getTx(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return tx.Create_ProjectInvitation(ctx, project_invitation_project_id, project_invitation_email, optional)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rx *Rx) Create_ProjectMember(ctx context.Context,
|
func (rx *Rx) Create_ProjectMember(ctx context.Context,
|
||||||
project_member_member_id ProjectMember_MemberId_Field,
|
project_member_member_id ProjectMember_MemberId_Field,
|
||||||
project_member_project_id ProjectMember_ProjectId_Field) (
|
project_member_project_id ProjectMember_ProjectId_Field) (
|
||||||
@ -29707,6 +29694,19 @@ func (rx *Rx) Replace_AccountFreezeEvent(ctx context.Context,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rx *Rx) Replace_ProjectInvitation(ctx context.Context,
|
||||||
|
project_invitation_project_id ProjectInvitation_ProjectId_Field,
|
||||||
|
project_invitation_email ProjectInvitation_Email_Field,
|
||||||
|
optional ProjectInvitation_Create_Fields) (
|
||||||
|
project_invitation *ProjectInvitation, err error) {
|
||||||
|
var tx *Tx
|
||||||
|
if tx, err = rx.getTx(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return tx.Replace_ProjectInvitation(ctx, project_invitation_project_id, project_invitation_email, optional)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (rx *Rx) UpdateNoReturn_AccountingTimestamps_By_Name(ctx context.Context,
|
func (rx *Rx) UpdateNoReturn_AccountingTimestamps_By_Name(ctx context.Context,
|
||||||
accounting_timestamps_name AccountingTimestamps_Name_Field,
|
accounting_timestamps_name AccountingTimestamps_Name_Field,
|
||||||
update AccountingTimestamps_Update_Fields) (
|
update AccountingTimestamps_Update_Fields) (
|
||||||
@ -30273,12 +30273,6 @@ type Methods interface {
|
|||||||
optional Project_Create_Fields) (
|
optional Project_Create_Fields) (
|
||||||
project *Project, err error)
|
project *Project, err error)
|
||||||
|
|
||||||
Create_ProjectInvitation(ctx context.Context,
|
|
||||||
project_invitation_project_id ProjectInvitation_ProjectId_Field,
|
|
||||||
project_invitation_email ProjectInvitation_Email_Field,
|
|
||||||
optional ProjectInvitation_Create_Fields) (
|
|
||||||
project_invitation *ProjectInvitation, err error)
|
|
||||||
|
|
||||||
Create_ProjectMember(ctx context.Context,
|
Create_ProjectMember(ctx context.Context,
|
||||||
project_member_member_id ProjectMember_MemberId_Field,
|
project_member_member_id ProjectMember_MemberId_Field,
|
||||||
project_member_project_id ProjectMember_ProjectId_Field) (
|
project_member_project_id ProjectMember_ProjectId_Field) (
|
||||||
@ -30808,6 +30802,12 @@ type Methods interface {
|
|||||||
optional AccountFreezeEvent_Create_Fields) (
|
optional AccountFreezeEvent_Create_Fields) (
|
||||||
account_freeze_event *AccountFreezeEvent, err error)
|
account_freeze_event *AccountFreezeEvent, err error)
|
||||||
|
|
||||||
|
Replace_ProjectInvitation(ctx context.Context,
|
||||||
|
project_invitation_project_id ProjectInvitation_ProjectId_Field,
|
||||||
|
project_invitation_email ProjectInvitation_Email_Field,
|
||||||
|
optional ProjectInvitation_Create_Fields) (
|
||||||
|
project_invitation *ProjectInvitation, err error)
|
||||||
|
|
||||||
UpdateNoReturn_AccountingTimestamps_By_Name(ctx context.Context,
|
UpdateNoReturn_AccountingTimestamps_By_Name(ctx context.Context,
|
||||||
accounting_timestamps_name AccountingTimestamps_Name_Field,
|
accounting_timestamps_name AccountingTimestamps_Name_Field,
|
||||||
update AccountingTimestamps_Update_Fields) (
|
update AccountingTimestamps_Update_Fields) (
|
||||||
|
@ -22,8 +22,8 @@ type projectInvitations struct {
|
|||||||
db *satelliteDB
|
db *satelliteDB
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert inserts a project member invitation into the database.
|
// Upsert updates a project member invitation if it exists and inserts it otherwise.
|
||||||
func (invites *projectInvitations) Insert(ctx context.Context, invite *console.ProjectInvitation) (_ *console.ProjectInvitation, err error) {
|
func (invites *projectInvitations) Upsert(ctx context.Context, invite *console.ProjectInvitation) (_ *console.ProjectInvitation, err error) {
|
||||||
defer mon.Task()(&ctx)(&err)
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
if invite == nil {
|
if invite == nil {
|
||||||
@ -36,7 +36,7 @@ func (invites *projectInvitations) Insert(ctx context.Context, invite *console.P
|
|||||||
createFields.InviterId = dbx.ProjectInvitation_InviterId(id)
|
createFields.InviterId = dbx.ProjectInvitation_InviterId(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
dbxInvite, err := invites.db.Create_ProjectInvitation(ctx,
|
dbxInvite, err := invites.db.Replace_ProjectInvitation(ctx,
|
||||||
dbx.ProjectInvitation_ProjectId(invite.ProjectID[:]),
|
dbx.ProjectInvitation_ProjectId(invite.ProjectID[:]),
|
||||||
dbx.ProjectInvitation_Email(normalizeEmail(invite.Email)),
|
dbx.ProjectInvitation_Email(normalizeEmail(invite.Email)),
|
||||||
createFields,
|
createFields,
|
||||||
@ -87,30 +87,6 @@ func (invites *projectInvitations) GetByEmail(ctx context.Context, email string)
|
|||||||
return projectInvitationSliceFromDBX(dbxInvites)
|
return projectInvitationSliceFromDBX(dbxInvites)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update updates the project member invitation specified by the given project ID and email address.
|
|
||||||
func (invites *projectInvitations) Update(ctx context.Context, projectID uuid.UUID, email string, request console.UpdateProjectInvitationRequest) (_ *console.ProjectInvitation, err error) {
|
|
||||||
defer mon.Task()(&ctx)(&err)
|
|
||||||
|
|
||||||
update := dbx.ProjectInvitation_Update_Fields{}
|
|
||||||
if request.CreatedAt != nil {
|
|
||||||
update.CreatedAt = dbx.ProjectInvitation_CreatedAt(*request.CreatedAt)
|
|
||||||
}
|
|
||||||
if request.InviterID != nil {
|
|
||||||
update.InviterId = dbx.ProjectInvitation_InviterId((*request.InviterID)[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
dbxInvite, err := invites.db.Update_ProjectInvitation_By_ProjectId_And_Email(ctx,
|
|
||||||
dbx.ProjectInvitation_ProjectId(projectID[:]),
|
|
||||||
dbx.ProjectInvitation_Email(normalizeEmail(email)),
|
|
||||||
update,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return projectInvitationFromDBX(dbxInvite)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete removes a project member invitation from the database.
|
// Delete removes a project member invitation from the database.
|
||||||
func (invites *projectInvitations) Delete(ctx context.Context, projectID uuid.UUID, email string) (err error) {
|
func (invites *projectInvitations) Delete(ctx context.Context, projectID uuid.UUID, email string) (err error) {
|
||||||
defer mon.Task()(&ctx)(&err)
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
@ -50,7 +50,7 @@ func TestProjectInvitations(t *testing.T) {
|
|||||||
|
|
||||||
if !t.Run("insert invitations", func(t *testing.T) {
|
if !t.Run("insert invitations", func(t *testing.T) {
|
||||||
// Expect failure because no user with inviterID exists.
|
// Expect failure because no user with inviterID exists.
|
||||||
_, err = invitesDB.Insert(ctx, invite)
|
_, err = invitesDB.Upsert(ctx, invite)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|
||||||
_, err = db.Console().Users().Insert(ctx, &console.User{
|
_, err = db.Console().Users().Insert(ctx, &console.User{
|
||||||
@ -59,19 +59,15 @@ func TestProjectInvitations(t *testing.T) {
|
|||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
invite, err = invitesDB.Insert(ctx, invite)
|
invite, err = invitesDB.Upsert(ctx, invite)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.WithinDuration(t, time.Now(), invite.CreatedAt, time.Minute)
|
require.WithinDuration(t, time.Now(), invite.CreatedAt, time.Minute)
|
||||||
require.Equal(t, projID, invite.ProjectID)
|
require.Equal(t, projID, invite.ProjectID)
|
||||||
require.Equal(t, strings.ToUpper(email), invite.Email)
|
require.Equal(t, strings.ToUpper(email), invite.Email)
|
||||||
|
|
||||||
// Duplicate invitations should be rejected.
|
inviteSameEmail, err = invitesDB.Upsert(ctx, inviteSameEmail)
|
||||||
_, err = invitesDB.Insert(ctx, invite)
|
|
||||||
require.Error(t, err)
|
|
||||||
|
|
||||||
inviteSameEmail, err = invitesDB.Insert(ctx, inviteSameEmail)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
inviteSameProject, err = invitesDB.Insert(ctx, inviteSameProject)
|
inviteSameProject, err = invitesDB.Upsert(ctx, inviteSameProject)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}) {
|
}) {
|
||||||
// None of the following subtests will pass if invitation insertion failed.
|
// None of the following subtests will pass if invitation insertion failed.
|
||||||
@ -126,22 +122,19 @@ func TestProjectInvitations(t *testing.T) {
|
|||||||
t.Run("update invitation", func(t *testing.T) {
|
t.Run("update invitation", func(t *testing.T) {
|
||||||
ctx := testcontext.New(t)
|
ctx := testcontext.New(t)
|
||||||
|
|
||||||
req := console.UpdateProjectInvitationRequest{}
|
|
||||||
newCreatedAt := invite.CreatedAt.Add(time.Hour)
|
|
||||||
req.CreatedAt = &newCreatedAt
|
|
||||||
newInvite, err := invitesDB.Update(ctx, projID, email, req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, newCreatedAt, newInvite.CreatedAt)
|
|
||||||
|
|
||||||
inviter, err := db.Console().Users().Insert(ctx, &console.User{
|
inviter, err := db.Console().Users().Insert(ctx, &console.User{
|
||||||
ID: testrand.UUID(),
|
ID: testrand.UUID(),
|
||||||
PasswordHash: testrand.Bytes(8),
|
PasswordHash: testrand.Bytes(8),
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
req.InviterID = &inviter.ID
|
invite.InviterID = &inviter.ID
|
||||||
newInvite, err = invitesDB.Update(ctx, projID, email, req)
|
|
||||||
|
oldCreatedAt := invite.CreatedAt
|
||||||
|
|
||||||
|
invite, err = invitesDB.Upsert(ctx, invite)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, inviter.ID, *newInvite.InviterID)
|
require.Equal(t, inviter.ID, *invite.InviterID)
|
||||||
|
require.True(t, invite.CreatedAt.After(oldCreatedAt))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("delete invitation", func(t *testing.T) {
|
t.Run("delete invitation", func(t *testing.T) {
|
||||||
@ -187,20 +180,22 @@ func TestDeleteBefore(t *testing.T) {
|
|||||||
_, err := db.Console().Projects().Insert(ctx, &console.Project{ID: projID})
|
_, err := db.Console().Projects().Insert(ctx, &console.Project{ID: projID})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
invite, err := invitesDB.Insert(ctx, &console.ProjectInvitation{ProjectID: projID})
|
invite, err := invitesDB.Upsert(ctx, &console.ProjectInvitation{ProjectID: projID})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
return invite
|
return invite
|
||||||
}
|
}
|
||||||
|
|
||||||
newInvite := createInvite()
|
newInvite := createInvite()
|
||||||
|
|
||||||
oldInvite := createInvite()
|
oldInvite := createInvite()
|
||||||
oldCreatedAt := expiration.Add(-time.Second)
|
result, err := db.Testing().RawDB().ExecContext(ctx,
|
||||||
oldInvite, err := invitesDB.Update(ctx, oldInvite.ProjectID, oldInvite.Email, console.UpdateProjectInvitationRequest{
|
"UPDATE project_invitations SET created_at = $1 WHERE project_id = $2",
|
||||||
CreatedAt: &oldCreatedAt,
|
expiration.Add(-time.Second), oldInvite.ProjectID,
|
||||||
})
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
count, err := result.RowsAffected()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, 1, count)
|
||||||
|
|
||||||
require.NoError(t, invitesDB.DeleteBefore(ctx, expiration, 0, 1))
|
require.NoError(t, invitesDB.DeleteBefore(ctx, expiration, 0, 1))
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ func TestGetPagedWithInvitationsByProjectID(t *testing.T) {
|
|||||||
_, err = db.Console().ProjectMembers().Insert(ctx, memberUser.ID, projectID)
|
_, err = db.Console().ProjectMembers().Insert(ctx, memberUser.ID, projectID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = db.Console().ProjectInvitations().Insert(ctx, &console.ProjectInvitation{
|
_, err = db.Console().ProjectInvitations().Upsert(ctx, &console.ProjectInvitation{
|
||||||
ProjectID: projectID,
|
ProjectID: projectID,
|
||||||
Email: "bob@mail.test",
|
Email: "bob@mail.test",
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user