satellite/{console,satellitedb}: add methods for project invite table
This change immplements methods for interacting with the project member invitations table. Resolves #5766 Change-Id: I0090c50f9fde5bcdae4ebdaa72cdcaa84d341b54
This commit is contained in:
parent
defb9eae82
commit
7a17dc5e07
@ -19,6 +19,8 @@ type DB interface {
|
|||||||
Projects() Projects
|
Projects() Projects
|
||||||
// ProjectMembers is a getter for ProjectMembers repository.
|
// ProjectMembers is a getter for ProjectMembers repository.
|
||||||
ProjectMembers() ProjectMembers
|
ProjectMembers() ProjectMembers
|
||||||
|
// ProjectInvitations is a getter for ProjectInvitations repository.
|
||||||
|
ProjectInvitations() ProjectInvitations
|
||||||
// APIKeys is a getter for APIKeys repository.
|
// APIKeys is a getter for APIKeys repository.
|
||||||
APIKeys() APIKeys
|
APIKeys() APIKeys
|
||||||
// RegistrationTokens is a getter for RegistrationTokens repository.
|
// RegistrationTokens is a getter for RegistrationTokens repository.
|
||||||
|
32
satellite/console/projectinvitations.go
Normal file
32
satellite/console/projectinvitations.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (C) 2023 Storj Labs, Inc.
|
||||||
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
|
package console
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"storj.io/common/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProjectInvitations exposes methods to manage pending project member invitations in the database.
|
||||||
|
//
|
||||||
|
// architecture: Database
|
||||||
|
type ProjectInvitations interface {
|
||||||
|
// Insert is a method for inserting a project member invitation into the database.
|
||||||
|
Insert(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(ctx context.Context, projectID uuid.UUID) ([]ProjectInvitation, error)
|
||||||
|
// GetByEmail returns all of the project member invitations for the specified email address.
|
||||||
|
GetByEmail(ctx context.Context, email string) ([]ProjectInvitation, error)
|
||||||
|
// Delete is a method for deleting a project member invitation from the database.
|
||||||
|
Delete(ctx context.Context, projectID uuid.UUID, email string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectInvitation represents a pending project member invitation.
|
||||||
|
type ProjectInvitation struct {
|
||||||
|
ProjectID uuid.UUID
|
||||||
|
Email string
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
@ -46,6 +46,11 @@ func (db *ConsoleDB) ProjectMembers() console.ProjectMembers {
|
|||||||
return &projectMembers{db.methods, db.db}
|
return &projectMembers{db.methods, db.db}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProjectInvitations is a getter for ProjectInvitations repository.
|
||||||
|
func (db *ConsoleDB) ProjectInvitations() console.ProjectInvitations {
|
||||||
|
return &projectInvitations{db.methods}
|
||||||
|
}
|
||||||
|
|
||||||
// APIKeys is a getter for APIKeys repository.
|
// APIKeys is a getter for APIKeys repository.
|
||||||
func (db *ConsoleDB) APIKeys() console.APIKeys {
|
func (db *ConsoleDB) APIKeys() console.APIKeys {
|
||||||
db.apikeysOnce.Do(func() {
|
db.apikeysOnce.Do(func() {
|
||||||
|
101
satellite/satellitedb/projectinvitations.go
Normal file
101
satellite/satellitedb/projectinvitations.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// Copyright (C) 2023 Storj Labs, Inc.
|
||||||
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
|
package satellitedb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"storj.io/common/uuid"
|
||||||
|
"storj.io/storj/satellite/console"
|
||||||
|
"storj.io/storj/satellite/satellitedb/dbx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure that projectInvitations implements console.ProjectInvitations.
|
||||||
|
var _ console.ProjectInvitations = (*projectInvitations)(nil)
|
||||||
|
|
||||||
|
// projectInvitations is an implementation of console.ProjectInvitations.
|
||||||
|
type projectInvitations struct {
|
||||||
|
db dbx.Methods
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert is a method for inserting a project member invitation into the database.
|
||||||
|
func (invites *projectInvitations) Insert(ctx context.Context, projectID uuid.UUID, email string) (_ *console.ProjectInvitation, err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
dbxInvite, err := invites.db.Create_ProjectInvitation(ctx,
|
||||||
|
dbx.ProjectInvitation_ProjectId(projectID[:]),
|
||||||
|
dbx.ProjectInvitation_Email(normalizeEmail(email)),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectInvitationFromDBX(dbxInvite)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByProjectID returns all of the project member invitations for the project specified by the given ID.
|
||||||
|
func (invites *projectInvitations) GetByProjectID(ctx context.Context, projectID uuid.UUID) (_ []console.ProjectInvitation, err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
dbxInvites, err := invites.db.All_ProjectInvitation_By_ProjectId(ctx, dbx.ProjectInvitation_ProjectId(projectID[:]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectInvitationSliceFromDBX(dbxInvites)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByEmail returns all of the project member invitations for the specified email address.
|
||||||
|
func (invites *projectInvitations) GetByEmail(ctx context.Context, email string) (_ []console.ProjectInvitation, err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
dbxInvites, err := invites.db.All_ProjectInvitation_By_Email(ctx, dbx.ProjectInvitation_Email(normalizeEmail(email)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectInvitationSliceFromDBX(dbxInvites)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete is a method for deleting a project member invitation from the database.
|
||||||
|
func (invites *projectInvitations) Delete(ctx context.Context, projectID uuid.UUID, email string) (err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
_, err = invites.db.Delete_ProjectInvitation_By_ProjectId_And_Email(ctx,
|
||||||
|
dbx.ProjectInvitation_ProjectId(projectID[:]),
|
||||||
|
dbx.ProjectInvitation_Email(normalizeEmail(email)),
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// projectInvitationFromDBX converts a project member invitation from the database to a *console.ProjectInvitation.
|
||||||
|
func projectInvitationFromDBX(dbxInvite *dbx.ProjectInvitation) (_ *console.ProjectInvitation, err error) {
|
||||||
|
if dbxInvite == nil {
|
||||||
|
return nil, Error.New("dbx invitation is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
projectID, err := uuid.FromBytes(dbxInvite.ProjectId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &console.ProjectInvitation{
|
||||||
|
ProjectID: projectID,
|
||||||
|
Email: dbxInvite.Email,
|
||||||
|
CreatedAt: dbxInvite.CreatedAt,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// projectInvitationSliceFromDBX converts a project member invitation slice from the database to a
|
||||||
|
// slice of console.ProjectInvitation.
|
||||||
|
func projectInvitationSliceFromDBX(dbxInvites []*dbx.ProjectInvitation) (invites []console.ProjectInvitation, err error) {
|
||||||
|
for _, dbxInvite := range dbxInvites {
|
||||||
|
invite, err := projectInvitationFromDBX(dbxInvite)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
invites = append(invites, *invite)
|
||||||
|
}
|
||||||
|
return invites, nil
|
||||||
|
}
|
96
satellite/satellitedb/projectinvitations_test.go
Normal file
96
satellite/satellitedb/projectinvitations_test.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// Copyright (C) 2023 Storj Labs, Inc.
|
||||||
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
|
package satellitedb_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"storj.io/common/testcontext"
|
||||||
|
"storj.io/common/testrand"
|
||||||
|
"storj.io/storj/satellite"
|
||||||
|
"storj.io/storj/satellite/console"
|
||||||
|
"storj.io/storj/satellite/satellitedb/satellitedbtest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProjectInvitations(t *testing.T) {
|
||||||
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
||||||
|
invitesDB := db.Console().ProjectInvitations()
|
||||||
|
projectsDB := db.Console().Projects()
|
||||||
|
projID := testrand.UUID()
|
||||||
|
projID2 := testrand.UUID()
|
||||||
|
email := "user@mail.test"
|
||||||
|
email2 := "user2@mail.test"
|
||||||
|
|
||||||
|
_, err := projectsDB.Insert(ctx, &console.Project{ID: projID})
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = projectsDB.Insert(ctx, &console.Project{ID: projID2})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
invite, err := invitesDB.Insert(ctx, projID, email)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.WithinDuration(t, time.Now(), invite.CreatedAt, time.Minute)
|
||||||
|
require.Equal(t, projID, invite.ProjectID)
|
||||||
|
require.Equal(t, strings.ToUpper(email), invite.Email)
|
||||||
|
|
||||||
|
_, err = invitesDB.Insert(ctx, projID, email)
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
|
inviteSameEmail, err := invitesDB.Insert(ctx, projID2, email)
|
||||||
|
require.NoError(t, err)
|
||||||
|
inviteSameProject, err := invitesDB.Insert(ctx, projID, email2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Run("get invitations by email", func(t *testing.T) {
|
||||||
|
ctx := testcontext.New(t)
|
||||||
|
|
||||||
|
invites, err := invitesDB.GetByEmail(ctx, "nobody@mail.test")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Empty(t, invites)
|
||||||
|
|
||||||
|
invites, err = invitesDB.GetByEmail(ctx, "uSeR@mAiL.tEsT")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.ElementsMatch(t, invites, []console.ProjectInvitation{*invite, *inviteSameEmail})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("get invitations by project ID", func(t *testing.T) {
|
||||||
|
ctx := testcontext.New(t)
|
||||||
|
|
||||||
|
invites, err := invitesDB.GetByProjectID(ctx, testrand.UUID())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Empty(t, invites)
|
||||||
|
|
||||||
|
invites, err = invitesDB.GetByProjectID(ctx, projID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.ElementsMatch(t, invites, []console.ProjectInvitation{*invite, *inviteSameProject})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("delete invitation", func(t *testing.T) {
|
||||||
|
ctx := testcontext.New(t)
|
||||||
|
|
||||||
|
require.NoError(t, invitesDB.Delete(ctx, projID, email))
|
||||||
|
|
||||||
|
invites, err := invitesDB.GetByEmail(ctx, email)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, invites, []console.ProjectInvitation{*inviteSameEmail})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ensure project removal deletes invitations", func(t *testing.T) {
|
||||||
|
ctx := testcontext.New(t)
|
||||||
|
|
||||||
|
require.NoError(t, projectsDB.Delete(ctx, projID))
|
||||||
|
|
||||||
|
invites, err := invitesDB.GetByProjectID(ctx, projID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Empty(t, invites)
|
||||||
|
|
||||||
|
invites, err = invitesDB.GetByEmail(ctx, email)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, invites, []console.ProjectInvitation{*inviteSameEmail})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user