satellite/console/consoleweb/consoleql: include invites in member pages
Project member invitations may now be requested through GraphQL queries. This is necessary for the satellite frontend to display invitations in the Team page. References #5855 Change-Id: Ibc8526ba768fd82c1b1890201004ef0f066df2fc
This commit is contained in:
parent
0cbb0ee12e
commit
5b1c22a1e7
@ -32,13 +32,15 @@ const (
|
|||||||
BucketUsageCursorInputType = "bucketUsageCursor"
|
BucketUsageCursorInputType = "bucketUsageCursor"
|
||||||
// BucketUsageType is a graphql type name for bucket usage.
|
// BucketUsageType is a graphql type name for bucket usage.
|
||||||
BucketUsageType = "bucketUsage"
|
BucketUsageType = "bucketUsage"
|
||||||
// BucketUsagePageType is a field name for bucket usage page.
|
// BucketUsagePageType is a graphql type name for bucket usage page.
|
||||||
BucketUsagePageType = "bucketUsagePage"
|
BucketUsagePageType = "bucketUsagePage"
|
||||||
// ProjectMembersPageType is a field name for project members page.
|
// ProjectMembersPageType is a graphql type name for project members page.
|
||||||
ProjectMembersPageType = "projectMembersPage"
|
ProjectMembersPageType = "projectMembersPage"
|
||||||
|
// ProjectMembersAndInvitationsPageType is a graphql type name for a page of project members and invitations.
|
||||||
|
ProjectMembersAndInvitationsPageType = "projectMembersAndInvitationsPage"
|
||||||
// ProjectMembersCursorInputType is a graphql type name for project members.
|
// ProjectMembersCursorInputType is a graphql type name for project members.
|
||||||
ProjectMembersCursorInputType = "projectMembersCursor"
|
ProjectMembersCursorInputType = "projectMembersCursor"
|
||||||
// APIKeysPageType is a field name for api keys page.
|
// APIKeysPageType is a graphql type name for api keys page.
|
||||||
APIKeysPageType = "apiKeysPage"
|
APIKeysPageType = "apiKeysPage"
|
||||||
// APIKeysCursorInputType is a graphql type name for api keys.
|
// APIKeysCursorInputType is a graphql type name for api keys.
|
||||||
APIKeysCursorInputType = "apiKeysCursor"
|
APIKeysCursorInputType = "apiKeysCursor"
|
||||||
@ -54,6 +56,8 @@ const (
|
|||||||
FieldDescription = "description"
|
FieldDescription = "description"
|
||||||
// FieldMembers is field name for members.
|
// FieldMembers is field name for members.
|
||||||
FieldMembers = "members"
|
FieldMembers = "members"
|
||||||
|
// FieldMembersAndInvitations is field name for members and invitations.
|
||||||
|
FieldMembersAndInvitations = "membersAndInvitations"
|
||||||
// FieldAPIKeys is a field name for api keys.
|
// FieldAPIKeys is a field name for api keys.
|
||||||
FieldAPIKeys = "apiKeys"
|
FieldAPIKeys = "apiKeys"
|
||||||
// FieldUsage is a field name for usage rollup.
|
// FieldUsage is a field name for usage rollup.
|
||||||
@ -84,6 +88,8 @@ const (
|
|||||||
FieldProjects = "projects"
|
FieldProjects = "projects"
|
||||||
// FieldProjectMembers is a field name for project members.
|
// FieldProjectMembers is a field name for project members.
|
||||||
FieldProjectMembers = "projectMembers"
|
FieldProjectMembers = "projectMembers"
|
||||||
|
// FieldProjectInvitations is a field name for project member invitations.
|
||||||
|
FieldProjectInvitations = "projectInvitations"
|
||||||
// CursorArg is an argument name for cursor.
|
// CursorArg is an argument name for cursor.
|
||||||
CursorArg = "cursor"
|
CursorArg = "cursor"
|
||||||
// PageArg ia an argument name for page number.
|
// PageArg ia an argument name for page number.
|
||||||
@ -106,6 +112,48 @@ const (
|
|||||||
|
|
||||||
// graphqlProject creates *graphql.Object type representation of satellite.ProjectInfo.
|
// graphqlProject creates *graphql.Object type representation of satellite.ProjectInfo.
|
||||||
func graphqlProject(service *console.Service, types *TypeCreator) *graphql.Object {
|
func graphqlProject(service *console.Service, types *TypeCreator) *graphql.Object {
|
||||||
|
resolveMembersAndInvites := func(p graphql.ResolveParams, pagingType console.ProjectMembersPagingType) (interface{}, error) {
|
||||||
|
project, _ := p.Source.(*console.Project)
|
||||||
|
|
||||||
|
_, err := console.GetUser(p.Context)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor := cursorArgsToProjectMembersCursor(p.Args[CursorArg].(map[string]interface{}))
|
||||||
|
page, err := service.GetProjectMembers(p.Context, project.ID, cursor, pagingType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var users []projectMember
|
||||||
|
for _, member := range page.ProjectMembers {
|
||||||
|
user, err := service.GetUser(p.Context, member.MemberID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
users = append(users, projectMember{
|
||||||
|
User: user,
|
||||||
|
JoinedAt: member.CreatedAt,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
projectMembersPage := projectMembersPage{
|
||||||
|
ProjectMembers: users,
|
||||||
|
ProjectInvitations: page.ProjectInvitations,
|
||||||
|
TotalCount: page.TotalCount,
|
||||||
|
Offset: page.Offset,
|
||||||
|
Limit: page.Limit,
|
||||||
|
Order: int(page.Order),
|
||||||
|
OrderDirection: int(page.OrderDirection),
|
||||||
|
Search: page.Search,
|
||||||
|
CurrentPage: page.CurrentPage,
|
||||||
|
PageCount: page.PageCount,
|
||||||
|
}
|
||||||
|
return projectMembersPage, nil
|
||||||
|
}
|
||||||
|
|
||||||
return graphql.NewObject(graphql.ObjectConfig{
|
return graphql.NewObject(graphql.ObjectConfig{
|
||||||
Name: ProjectType,
|
Name: ProjectType,
|
||||||
Fields: graphql.Fields{
|
Fields: graphql.Fields{
|
||||||
@ -130,6 +178,7 @@ func graphqlProject(service *console.Service, types *TypeCreator) *graphql.Objec
|
|||||||
FieldMemberCount: &graphql.Field{
|
FieldMemberCount: &graphql.Field{
|
||||||
Type: graphql.Int,
|
Type: graphql.Int,
|
||||||
},
|
},
|
||||||
|
// TODO: Remove once the frontend has been updated to select membersAndInvitations.
|
||||||
FieldMembers: &graphql.Field{
|
FieldMembers: &graphql.Field{
|
||||||
Type: types.projectMemberPage,
|
Type: types.projectMemberPage,
|
||||||
Args: graphql.FieldConfigArgument{
|
Args: graphql.FieldConfigArgument{
|
||||||
@ -138,44 +187,18 @@ func graphqlProject(service *console.Service, types *TypeCreator) *graphql.Objec
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
|
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
|
||||||
project, _ := p.Source.(*console.Project)
|
return resolveMembersAndInvites(p, console.Members)
|
||||||
|
},
|
||||||
_, err := console.GetUser(p.Context)
|
},
|
||||||
if err != nil {
|
FieldMembersAndInvitations: &graphql.Field{
|
||||||
return nil, err
|
Type: types.projectMembersAndInvitationsPage,
|
||||||
}
|
Args: graphql.FieldConfigArgument{
|
||||||
|
CursorArg: &graphql.ArgumentConfig{
|
||||||
cursor := cursorArgsToProjectMembersCursor(p.Args[CursorArg].(map[string]interface{}))
|
Type: graphql.NewNonNull(types.projectMembersCursor),
|
||||||
page, err := service.GetProjectMembers(p.Context, project.ID, cursor)
|
},
|
||||||
if err != nil {
|
},
|
||||||
return nil, err
|
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
|
||||||
}
|
return resolveMembersAndInvites(p, console.MembersAndInvitations)
|
||||||
|
|
||||||
var users []projectMember
|
|
||||||
for _, member := range page.ProjectMembers {
|
|
||||||
user, err := service.GetUser(p.Context, member.MemberID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
users = append(users, projectMember{
|
|
||||||
User: user,
|
|
||||||
JoinedAt: member.CreatedAt,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
projectMembersPage := projectMembersPage{
|
|
||||||
ProjectMembers: users,
|
|
||||||
TotalCount: page.TotalCount,
|
|
||||||
Offset: page.Offset,
|
|
||||||
Limit: page.Limit,
|
|
||||||
Order: int(page.Order),
|
|
||||||
OrderDirection: int(page.OrderDirection),
|
|
||||||
Search: page.Search,
|
|
||||||
CurrentPage: page.CurrentPage,
|
|
||||||
PageCount: page.PageCount,
|
|
||||||
}
|
|
||||||
return projectMembersPage, nil
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
FieldAPIKeys: &graphql.Field{
|
FieldAPIKeys: &graphql.Field{
|
||||||
|
@ -14,6 +14,8 @@ import (
|
|||||||
const (
|
const (
|
||||||
// ProjectMemberType is a graphql type name for project member.
|
// ProjectMemberType is a graphql type name for project member.
|
||||||
ProjectMemberType = "projectMember"
|
ProjectMemberType = "projectMember"
|
||||||
|
// ProjectInvitationType is a graphql type name for project member invitation.
|
||||||
|
ProjectInvitationType = "projectInvitation"
|
||||||
// FieldJoinedAt is a field name for joined at timestamp.
|
// FieldJoinedAt is a field name for joined at timestamp.
|
||||||
FieldJoinedAt = "joinedAt"
|
FieldJoinedAt = "joinedAt"
|
||||||
)
|
)
|
||||||
@ -38,6 +40,21 @@ func graphqlProjectMember(service *console.Service, types *TypeCreator) *graphql
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// graphqlProjectInvitation creates projectInvitation type.
|
||||||
|
func graphqlProjectInvitation() *graphql.Object {
|
||||||
|
return graphql.NewObject(graphql.ObjectConfig{
|
||||||
|
Name: ProjectInvitationType,
|
||||||
|
Fields: graphql.Fields{
|
||||||
|
FieldEmail: &graphql.Field{
|
||||||
|
Type: graphql.String,
|
||||||
|
},
|
||||||
|
FieldCreatedAt: &graphql.Field{
|
||||||
|
Type: graphql.DateTime,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func graphqlProjectMembersCursor() *graphql.InputObject {
|
func graphqlProjectMembersCursor() *graphql.InputObject {
|
||||||
return graphql.NewInputObject(graphql.InputObjectConfig{
|
return graphql.NewInputObject(graphql.InputObjectConfig{
|
||||||
Name: ProjectMembersCursorInputType,
|
Name: ProjectMembersCursorInputType,
|
||||||
@ -62,40 +79,67 @@ func graphqlProjectMembersCursor() *graphql.InputObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func graphqlProjectMembersPage(types *TypeCreator) *graphql.Object {
|
func graphqlProjectMembersPage(types *TypeCreator) *graphql.Object {
|
||||||
return graphql.NewObject(graphql.ObjectConfig{
|
fields := graphql.Fields{
|
||||||
Name: ProjectMembersPageType,
|
FieldProjectMembers: &graphql.Field{
|
||||||
Fields: graphql.Fields{
|
Type: graphql.NewList(types.projectMember),
|
||||||
FieldProjectMembers: &graphql.Field{
|
|
||||||
Type: graphql.NewList(types.projectMember),
|
|
||||||
},
|
|
||||||
SearchArg: &graphql.Field{
|
|
||||||
Type: graphql.String,
|
|
||||||
},
|
|
||||||
LimitArg: &graphql.Field{
|
|
||||||
Type: graphql.Int,
|
|
||||||
},
|
|
||||||
OrderArg: &graphql.Field{
|
|
||||||
Type: graphql.Int,
|
|
||||||
},
|
|
||||||
OrderDirectionArg: &graphql.Field{
|
|
||||||
Type: graphql.Int,
|
|
||||||
},
|
|
||||||
OffsetArg: &graphql.Field{
|
|
||||||
Type: graphql.Int,
|
|
||||||
},
|
|
||||||
FieldPageCount: &graphql.Field{
|
|
||||||
Type: graphql.Int,
|
|
||||||
},
|
|
||||||
FieldCurrentPage: &graphql.Field{
|
|
||||||
Type: graphql.Int,
|
|
||||||
},
|
|
||||||
FieldTotalCount: &graphql.Field{
|
|
||||||
Type: graphql.Int,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
for k, v := range commonProjectMembersPageFields() {
|
||||||
|
fields[k] = v
|
||||||
|
}
|
||||||
|
return graphql.NewObject(graphql.ObjectConfig{
|
||||||
|
Name: ProjectMembersPageType,
|
||||||
|
Fields: fields,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func graphqlProjectMembersAndInvitationsPage(types *TypeCreator) *graphql.Object {
|
||||||
|
fields := graphql.Fields{
|
||||||
|
FieldProjectMembers: &graphql.Field{
|
||||||
|
Type: graphql.NewList(types.projectMember),
|
||||||
|
},
|
||||||
|
FieldProjectInvitations: &graphql.Field{
|
||||||
|
Type: graphql.NewList(types.projectInvitation),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for k, v := range commonProjectMembersPageFields() {
|
||||||
|
fields[k] = v
|
||||||
|
}
|
||||||
|
return graphql.NewObject(graphql.ObjectConfig{
|
||||||
|
Name: ProjectMembersAndInvitationsPageType,
|
||||||
|
Fields: fields,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func commonProjectMembersPageFields() graphql.Fields {
|
||||||
|
return graphql.Fields{
|
||||||
|
SearchArg: &graphql.Field{
|
||||||
|
Type: graphql.String,
|
||||||
|
},
|
||||||
|
LimitArg: &graphql.Field{
|
||||||
|
Type: graphql.Int,
|
||||||
|
},
|
||||||
|
OrderArg: &graphql.Field{
|
||||||
|
Type: graphql.Int,
|
||||||
|
},
|
||||||
|
OrderDirectionArg: &graphql.Field{
|
||||||
|
Type: graphql.Int,
|
||||||
|
},
|
||||||
|
OffsetArg: &graphql.Field{
|
||||||
|
Type: graphql.Int,
|
||||||
|
},
|
||||||
|
FieldPageCount: &graphql.Field{
|
||||||
|
Type: graphql.Int,
|
||||||
|
},
|
||||||
|
FieldCurrentPage: &graphql.Field{
|
||||||
|
Type: graphql.Int,
|
||||||
|
},
|
||||||
|
FieldTotalCount: &graphql.Field{
|
||||||
|
Type: graphql.Int,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// projectMember encapsulates User and joinedAt.
|
// projectMember encapsulates User and joinedAt.
|
||||||
type projectMember struct {
|
type projectMember struct {
|
||||||
User *console.User
|
User *console.User
|
||||||
@ -103,7 +147,8 @@ type projectMember struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type projectMembersPage struct {
|
type projectMembersPage struct {
|
||||||
ProjectMembers []projectMember
|
ProjectMembers []projectMember
|
||||||
|
ProjectInvitations []console.ProjectInvitation
|
||||||
|
|
||||||
Search string
|
Search string
|
||||||
Limit uint
|
Limit uint
|
||||||
|
@ -16,18 +16,20 @@ type TypeCreator struct {
|
|||||||
query *graphql.Object
|
query *graphql.Object
|
||||||
mutation *graphql.Object
|
mutation *graphql.Object
|
||||||
|
|
||||||
user *graphql.Object
|
user *graphql.Object
|
||||||
reward *graphql.Object
|
reward *graphql.Object
|
||||||
project *graphql.Object
|
project *graphql.Object
|
||||||
projectUsage *graphql.Object
|
projectUsage *graphql.Object
|
||||||
projectsPage *graphql.Object
|
projectsPage *graphql.Object
|
||||||
bucketUsage *graphql.Object
|
bucketUsage *graphql.Object
|
||||||
bucketUsagePage *graphql.Object
|
bucketUsagePage *graphql.Object
|
||||||
projectMember *graphql.Object
|
projectMember *graphql.Object
|
||||||
projectMemberPage *graphql.Object
|
projectInvitation *graphql.Object
|
||||||
apiKeyPage *graphql.Object
|
projectMemberPage *graphql.Object
|
||||||
apiKeyInfo *graphql.Object
|
projectMembersAndInvitationsPage *graphql.Object
|
||||||
createAPIKey *graphql.Object
|
apiKeyPage *graphql.Object
|
||||||
|
apiKeyInfo *graphql.Object
|
||||||
|
createAPIKey *graphql.Object
|
||||||
|
|
||||||
userInput *graphql.InputObject
|
userInput *graphql.InputObject
|
||||||
projectInput *graphql.InputObject
|
projectInput *graphql.InputObject
|
||||||
@ -112,11 +114,21 @@ func (c *TypeCreator) Create(log *zap.Logger, service *console.Service, mailServ
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.projectInvitation = graphqlProjectInvitation()
|
||||||
|
if err := c.projectInvitation.Error(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
c.projectMemberPage = graphqlProjectMembersPage(c)
|
c.projectMemberPage = graphqlProjectMembersPage(c)
|
||||||
if err := c.projectMemberPage.Error(); err != nil {
|
if err := c.projectMemberPage.Error(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.projectMembersAndInvitationsPage = graphqlProjectMembersAndInvitationsPage(c)
|
||||||
|
if err := c.projectMembersAndInvitationsPage.Error(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
c.apiKeyPage = graphqlAPIKeysPage(c)
|
c.apiKeyPage = graphqlAPIKeysPage(c)
|
||||||
if err := c.apiKeyPage.Error(); err != nil {
|
if err := c.apiKeyPage.Error(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -62,6 +62,18 @@ type ProjectMembersPage struct {
|
|||||||
TotalCount uint64
|
TotalCount uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProjectMembersPagingType determines what types of results should be returned
|
||||||
|
// in a project members page.
|
||||||
|
type ProjectMembersPagingType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Members indicates that only project members should be returned in a project members page.
|
||||||
|
Members ProjectMembersPagingType = iota
|
||||||
|
// MembersAndInvitations indicates that project members and project invitations should be
|
||||||
|
// returned in a project members page.
|
||||||
|
MembersAndInvitations
|
||||||
|
)
|
||||||
|
|
||||||
// ProjectMemberOrder is used for querying project members in specified order.
|
// ProjectMemberOrder is used for querying project members in specified order.
|
||||||
type ProjectMemberOrder int8
|
type ProjectMemberOrder int8
|
||||||
|
|
||||||
|
@ -2113,7 +2113,7 @@ func (s *Service) DeleteProjectMembersAndInvitations(ctx context.Context, projec
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetProjectMembers returns ProjectMembers for given Project.
|
// GetProjectMembers returns ProjectMembers for given Project.
|
||||||
func (s *Service) GetProjectMembers(ctx context.Context, projectID uuid.UUID, cursor ProjectMembersCursor) (pmp *ProjectMembersPage, err error) {
|
func (s *Service) GetProjectMembers(ctx context.Context, projectID uuid.UUID, cursor ProjectMembersCursor, pagingType ProjectMembersPagingType) (pmp *ProjectMembersPage, err error) {
|
||||||
defer mon.Task()(&ctx)(&err)
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
user, err := s.getUserAndAuditLog(ctx, "get project members", zap.String("projectID", projectID.String()))
|
user, err := s.getUserAndAuditLog(ctx, "get project members", zap.String("projectID", projectID.String()))
|
||||||
@ -2130,7 +2130,11 @@ func (s *Service) GetProjectMembers(ctx context.Context, projectID uuid.UUID, cu
|
|||||||
cursor.Limit = maxLimit
|
cursor.Limit = maxLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
pmp, err = s.store.ProjectMembers().GetPagedByProjectID(ctx, projectID, cursor)
|
if pagingType == MembersAndInvitations {
|
||||||
|
pmp, err = s.store.ProjectMembers().GetPagedWithInvitationsByProjectID(ctx, projectID, cursor)
|
||||||
|
} else {
|
||||||
|
pmp, err = s.store.ProjectMembers().GetPagedByProjectID(ctx, projectID, cursor)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, Error.Wrap(err)
|
return nil, Error.Wrap(err)
|
||||||
}
|
}
|
||||||
|
@ -289,17 +289,17 @@ func TestService(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("GetProjectMembers", func(t *testing.T) {
|
t.Run("GetProjectMembers", func(t *testing.T) {
|
||||||
// Getting the project members of an own project that one is a part of should work
|
// Getting the project members of an own project that one is a part of should work
|
||||||
userPage, err := service.GetProjectMembers(userCtx1, up1Proj.ID, console.ProjectMembersCursor{Page: 1, Limit: 10})
|
userPage, err := service.GetProjectMembers(userCtx1, up1Proj.ID, console.ProjectMembersCursor{Page: 1, Limit: 10}, console.Members)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, userPage.ProjectMembers, 2)
|
require.Len(t, userPage.ProjectMembers, 2)
|
||||||
|
|
||||||
// Getting the project members of a foreign project that one is a part of should work
|
// Getting the project members of a foreign project that one is a part of should work
|
||||||
userPage, err = service.GetProjectMembers(userCtx2, up1Proj.ID, console.ProjectMembersCursor{Page: 1, Limit: 10})
|
userPage, err = service.GetProjectMembers(userCtx2, up1Proj.ID, console.ProjectMembersCursor{Page: 1, Limit: 10}, console.Members)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, userPage.ProjectMembers, 2)
|
require.Len(t, userPage.ProjectMembers, 2)
|
||||||
|
|
||||||
// Getting the project members of a foreign project that one is not a part of should not work
|
// Getting the project members of a foreign project that one is not a part of should not work
|
||||||
userPage, err = service.GetProjectMembers(userCtx1, up2Proj.ID, console.ProjectMembersCursor{Page: 1, Limit: 10})
|
userPage, err = service.GetProjectMembers(userCtx1, up2Proj.ID, console.ProjectMembersCursor{Page: 1, Limit: 10}, console.Members)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Nil(t, userPage)
|
require.Nil(t, userPage)
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user