satellite/console/.../consoleql: add project invite expiration status
Each project member invitation returned from our GraphQL API now contains a field indicating whether the invitation has expired. This is required for us to enable functionality in the satellite frontend that is dependent on this information. References #5752 Change-Id: I4b71738e7a7373c690de188614f8c95009bc3989
This commit is contained in:
parent
d18f4f7d99
commit
28b2384970
@ -166,9 +166,18 @@ func graphqlProject(service *console.Service, types *TypeCreator) *graphql.Objec
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var invites []projectInvitation
|
||||||
|
for _, invite := range page.ProjectInvitations {
|
||||||
|
invites = append(invites, projectInvitation{
|
||||||
|
Email: invite.Email,
|
||||||
|
CreatedAt: invite.CreatedAt,
|
||||||
|
Expired: service.IsProjectInvitationExpired(&invite),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
projectMembersPage := projectMembersPage{
|
projectMembersPage := projectMembersPage{
|
||||||
ProjectMembers: users,
|
ProjectMembers: users,
|
||||||
ProjectInvitations: page.ProjectInvitations,
|
ProjectInvitations: invites,
|
||||||
TotalCount: page.TotalCount,
|
TotalCount: page.TotalCount,
|
||||||
Offset: page.Offset,
|
Offset: page.Offset,
|
||||||
Limit: page.Limit,
|
Limit: page.Limit,
|
||||||
|
@ -18,6 +18,8 @@ const (
|
|||||||
ProjectInvitationType = "projectInvitation"
|
ProjectInvitationType = "projectInvitation"
|
||||||
// FieldJoinedAt is a field name for joined at timestamp.
|
// FieldJoinedAt is a field name for joined at timestamp.
|
||||||
FieldJoinedAt = "joinedAt"
|
FieldJoinedAt = "joinedAt"
|
||||||
|
// FieldExpired is a field name for expiration status.
|
||||||
|
FieldExpired = "expired"
|
||||||
)
|
)
|
||||||
|
|
||||||
// graphqlProjectMember creates projectMember type.
|
// graphqlProjectMember creates projectMember type.
|
||||||
@ -51,6 +53,9 @@ func graphqlProjectInvitation() *graphql.Object {
|
|||||||
FieldCreatedAt: &graphql.Field{
|
FieldCreatedAt: &graphql.Field{
|
||||||
Type: graphql.DateTime,
|
Type: graphql.DateTime,
|
||||||
},
|
},
|
||||||
|
FieldExpired: &graphql.Field{
|
||||||
|
Type: graphql.Boolean,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -122,9 +127,16 @@ type projectMember struct {
|
|||||||
JoinedAt time.Time
|
JoinedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// projectInvitation encapsulates a console.ProjectInvitation and its expiration status.
|
||||||
|
type projectInvitation struct {
|
||||||
|
Email string
|
||||||
|
CreatedAt time.Time
|
||||||
|
Expired bool
|
||||||
|
}
|
||||||
|
|
||||||
type projectMembersPage struct {
|
type projectMembersPage struct {
|
||||||
ProjectMembers []projectMember
|
ProjectMembers []projectMember
|
||||||
ProjectInvitations []console.ProjectInvitation
|
ProjectInvitations []projectInvitation
|
||||||
|
|
||||||
Search string
|
Search string
|
||||||
Limit uint
|
Limit uint
|
||||||
|
@ -3471,7 +3471,7 @@ func (s *Service) GetUserProjectInvitations(ctx context.Context) (_ []ProjectInv
|
|||||||
|
|
||||||
var active []ProjectInvitation
|
var active []ProjectInvitation
|
||||||
for _, invite := range invites {
|
for _, invite := range invites {
|
||||||
if !time.Now().After(invite.CreatedAt.Add(s.config.ProjectInvitationExpiration)) {
|
if !s.IsProjectInvitationExpired(&invite) {
|
||||||
active = append(active, invite)
|
active = append(active, invite)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3544,7 +3544,7 @@ func (s *Service) RespondToProjectInvitation(ctx context.Context, projectID uuid
|
|||||||
return ErrProjectInviteInvalid.New(projInviteInvalidErrMsg)
|
return ErrProjectInviteInvalid.New(projInviteInvalidErrMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Now().After(invite.CreatedAt.Add(s.config.ProjectInvitationExpiration)) {
|
if s.IsProjectInvitationExpired(invite) {
|
||||||
deleteWithLog()
|
deleteWithLog()
|
||||||
return ErrProjectInviteInvalid.New(projInviteInvalidErrMsg)
|
return ErrProjectInviteInvalid.New(projInviteInvalidErrMsg)
|
||||||
}
|
}
|
||||||
@ -3596,7 +3596,7 @@ func (s *Service) InviteProjectMembers(ctx context.Context, projectID uuid.UUID,
|
|||||||
if err != nil && !errs.Is(err, sql.ErrNoRows) {
|
if err != nil && !errs.Is(err, sql.ErrNoRows) {
|
||||||
return nil, Error.Wrap(err)
|
return nil, Error.Wrap(err)
|
||||||
}
|
}
|
||||||
if invite != nil && !time.Now().After(invite.CreatedAt.Add(s.config.ProjectInvitationExpiration)) {
|
if invite != nil && !s.IsProjectInvitationExpired(invite) {
|
||||||
return nil, ErrProjectInviteActive.New(projInviteActiveErrMsg, invitedUser.Email)
|
return nil, ErrProjectInviteActive.New(projInviteActiveErrMsg, invitedUser.Email)
|
||||||
}
|
}
|
||||||
users = append(users, invitedUser)
|
users = append(users, invitedUser)
|
||||||
@ -3662,6 +3662,11 @@ func (s *Service) InviteProjectMembers(ctx context.Context, projectID uuid.UUID,
|
|||||||
return invites, nil
|
return invites, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsProjectInvitationExpired returns whether the project member invitation has expired.
|
||||||
|
func (s *Service) IsProjectInvitationExpired(invite *ProjectInvitation) bool {
|
||||||
|
return time.Now().After(invite.CreatedAt.Add(s.config.ProjectInvitationExpiration))
|
||||||
|
}
|
||||||
|
|
||||||
// GetInviteByToken returns a project invite given an invite token.
|
// GetInviteByToken returns a project invite given an invite token.
|
||||||
func (s *Service) GetInviteByToken(ctx context.Context, token string) (invite *ProjectInvitation, err error) {
|
func (s *Service) GetInviteByToken(ctx context.Context, token string) (invite *ProjectInvitation, err error) {
|
||||||
defer mon.Task()(&ctx)(&err)
|
defer mon.Task()(&ctx)(&err)
|
||||||
@ -3686,7 +3691,7 @@ func (s *Service) GetInviteByToken(ctx context.Context, token string) (invite *P
|
|||||||
}
|
}
|
||||||
return nil, ErrProjectInviteInvalid.New(projInviteInvalidErrMsg)
|
return nil, ErrProjectInviteInvalid.New(projInviteInvalidErrMsg)
|
||||||
}
|
}
|
||||||
if time.Now().After(invite.CreatedAt.Add(s.config.ProjectInvitationExpiration)) {
|
if s.IsProjectInvitationExpired(invite) {
|
||||||
return nil, ErrProjectInviteInvalid.New(projInviteInvalidErrMsg)
|
return nil, ErrProjectInviteInvalid.New(projInviteInvalidErrMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1987,10 +1987,11 @@ func TestProjectInvitations(t *testing.T) {
|
|||||||
|
|
||||||
expireInvite := func(t *testing.T, ctx context.Context, invite *console.ProjectInvitation) {
|
expireInvite := func(t *testing.T, ctx context.Context, invite *console.ProjectInvitation) {
|
||||||
createdAt := time.Now().Add(-sat.Config.Console.ProjectInvitationExpiration)
|
createdAt := time.Now().Add(-sat.Config.Console.ProjectInvitationExpiration)
|
||||||
_, err := sat.DB.Console().ProjectInvitations().Update(ctx, invite.ProjectID, invite.Email, console.UpdateProjectInvitationRequest{
|
newInvite, err := sat.DB.Console().ProjectInvitations().Update(ctx, invite.ProjectID, invite.Email, console.UpdateProjectInvitationRequest{
|
||||||
CreatedAt: &createdAt,
|
CreatedAt: &createdAt,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
*invite = *newInvite
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("invite users", func(t *testing.T) {
|
t.Run("invite users", func(t *testing.T) {
|
||||||
@ -2031,13 +2032,18 @@ func TestProjectInvitations(t *testing.T) {
|
|||||||
require.True(t, console.ErrProjectInviteActive.Has(err))
|
require.True(t, console.ErrProjectInviteActive.Has(err))
|
||||||
require.Empty(t, invites)
|
require.Empty(t, invites)
|
||||||
|
|
||||||
// resending an expired invitation should succeed.
|
// expire the invitation.
|
||||||
|
require.False(t, service.IsProjectInvitationExpired(&user3Invite))
|
||||||
|
oldCreatedAt := user3Invite.CreatedAt
|
||||||
expireInvite(t, ctx, &user3Invite)
|
expireInvite(t, ctx, &user3Invite)
|
||||||
|
require.True(t, service.IsProjectInvitationExpired(&user3Invite))
|
||||||
|
|
||||||
|
// resending an expired invitation should succeed.
|
||||||
invites, err = service.InviteProjectMembers(ctx2, project.ID, []string{user3.Email})
|
invites, err = service.InviteProjectMembers(ctx2, project.ID, []string{user3.Email})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, invites, 1)
|
require.Len(t, invites, 1)
|
||||||
require.Equal(t, user2.ID, *invites[0].InviterID)
|
require.Equal(t, user2.ID, *invites[0].InviterID)
|
||||||
require.True(t, invites[0].CreatedAt.After(user3Invite.CreatedAt))
|
require.True(t, invites[0].CreatedAt.After(oldCreatedAt))
|
||||||
|
|
||||||
// inviting a project member should fail.
|
// inviting a project member should fail.
|
||||||
_, err = service.InviteProjectMembers(ctx, project.ID, []string{user2.Email})
|
_, err = service.InviteProjectMembers(ctx, project.ID, []string{user2.Email})
|
||||||
|
@ -64,7 +64,8 @@ export class ProjectMembersApiGql extends BaseGql implements ProjectMembersApi {
|
|||||||
},
|
},
|
||||||
projectInvitations {
|
projectInvitations {
|
||||||
email,
|
email,
|
||||||
createdAt
|
createdAt,
|
||||||
|
expired
|
||||||
},
|
},
|
||||||
search,
|
search,
|
||||||
limit,
|
limit,
|
||||||
@ -127,6 +128,7 @@ export class ProjectMembersApiGql extends BaseGql implements ProjectMembersApi {
|
|||||||
projectMembersPage.projectInvitations = projectMembers.projectInvitations.map(key => new ProjectInvitationItemModel(
|
projectMembersPage.projectInvitations = projectMembers.projectInvitations.map(key => new ProjectInvitationItemModel(
|
||||||
key.email,
|
key.email,
|
||||||
new Date(key.createdAt),
|
new Date(key.createdAt),
|
||||||
|
key.expired,
|
||||||
));
|
));
|
||||||
|
|
||||||
projectMembersPage.search = projectMembers.search;
|
projectMembersPage.search = projectMembers.search;
|
||||||
|
@ -231,6 +231,7 @@ export class ProjectInvitationItemModel implements ProjectMemberItemModel {
|
|||||||
public constructor(
|
public constructor(
|
||||||
public email: string,
|
public email: string,
|
||||||
public createdAt: Date,
|
public createdAt: Date,
|
||||||
|
public expired: boolean,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user