satellite/console: project members refactoring (#2752)

This commit is contained in:
Bogdan Artemenko 2019-08-12 13:22:32 +03:00 committed by GitHub
parent 260f648275
commit 0decd1419b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 308 additions and 104 deletions

View File

@ -415,7 +415,7 @@ func TestGrapqhlMutation(t *testing.T) {
t.Run("Add project members mutation", func(t *testing.T) {
query := fmt.Sprintf(
"mutation {addProjectMembers(projectID:\"%s\",email:[\"%s\",\"%s\"]){id,name,members(limit:50,offset:0){joinedAt}}}",
"mutation {addProjectMembers(projectID:\"%s\",email:[\"%s\",\"%s\"]){id,name,members(cursor: { limit: 50, search: \"\", page: 1, order: 1, orderDirection: 2 }){projectMembers{joinedAt}}}}",
project.ID.String(),
user1.Email,
user2.Email,
@ -426,14 +426,17 @@ func TestGrapqhlMutation(t *testing.T) {
data := result.(map[string]interface{})
proj := data[consoleql.AddProjectMembersMutation].(map[string]interface{})
members := proj[consoleql.FieldMembers].(map[string]interface{})
projectMembers := members[consoleql.FieldProjectMembers].([]interface{})
assert.Equal(t, project.ID.String(), proj[consoleql.FieldID])
assert.Equal(t, project.Name, proj[consoleql.FieldName])
assert.Equal(t, 3, len(proj[consoleql.FieldMembers].([]interface{})))
assert.Equal(t, 3, len(projectMembers))
})
t.Run("Delete project members mutation", func(t *testing.T) {
query := fmt.Sprintf(
"mutation {deleteProjectMembers(projectID:\"%s\",email:[\"%s\",\"%s\"]){id,name,members(limit:50,offset:0){user{id}}}}",
"mutation {deleteProjectMembers(projectID:\"%s\",email:[\"%s\",\"%s\"]){id,name,members(cursor: { limit: 50, search: \"\", page: 1, order: 1, orderDirection: 2 }){projectMembers{user{id}}}}}",
project.ID.String(),
user1.Email,
user2.Email,
@ -444,8 +447,9 @@ func TestGrapqhlMutation(t *testing.T) {
data := result.(map[string]interface{})
proj := data[consoleql.DeleteProjectMembersMutation].(map[string]interface{})
members := proj[consoleql.FieldMembers].([]interface{})
rootMember := members[0].(map[string]interface{})[consoleql.UserType].(map[string]interface{})
members := proj[consoleql.FieldMembers].(map[string]interface{})
projectMembers := members[consoleql.FieldProjectMembers].([]interface{})
rootMember := projectMembers[0].(map[string]interface{})[consoleql.UserType].(map[string]interface{})
assert.Equal(t, project.ID.String(), proj[consoleql.FieldID])
assert.Equal(t, project.Name, proj[consoleql.FieldName])

View File

@ -27,6 +27,10 @@ const (
BucketUsagePageType = "bucketUsagePage"
// PaymentMethodType is a field name for payment method
PaymentMethodType = "paymentMethod"
// ProjectMembersPageType is a field name for project members page
ProjectMembersPageType = "projectMembersPage"
// ProjectMembersCursorInputType is a graphql type name for project members
ProjectMembersCursorInputType = "projectMembersCursor"
// FieldName is a field name for "name"
FieldName = "name"
// FieldBucketName is a field name for "bucket name"
@ -55,6 +59,8 @@ const (
FieldCurrentPage = "currentPage"
// FieldTotalCount is a field name for bucket usage count total
FieldTotalCount = "totalCount"
// FieldProjectMembers is a field name for project members
FieldProjectMembers = "projectMembers"
// FieldCardBrand is a field name for credit card brand
FieldCardBrand = "brand"
// FieldCardLastFour is a field name for credit card last four digits
@ -75,6 +81,8 @@ const (
SearchArg = "search"
// OrderArg is argument name for order
OrderArg = "order"
// OrderDirectionArg is argument name for order direction
OrderDirectionArg = "orderDirection"
// SinceArg marks start of the period
SinceArg = "since"
// BeforeArg marks end of the period
@ -99,48 +107,28 @@ func graphqlProject(service *console.Service, types *TypeCreator) *graphql.Objec
Type: graphql.DateTime,
},
FieldMembers: &graphql.Field{
Type: graphql.NewList(types.projectMember),
Type: types.projectMemberPage,
Args: graphql.FieldConfigArgument{
OffsetArg: &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.Int),
},
LimitArg: &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.Int),
},
SearchArg: &graphql.ArgumentConfig{
Type: graphql.String,
},
OrderArg: &graphql.ArgumentConfig{
Type: graphql.Int,
CursorArg: &graphql.ArgumentConfig{
Type: graphql.NewNonNull(types.projectMembersCursor),
},
},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
project, _ := p.Source.(*console.Project)
offs, _ := p.Args[OffsetArg].(int)
lim, _ := p.Args[LimitArg].(int)
search, _ := p.Args[SearchArg].(string)
order, _ := p.Args[OrderArg].(int)
pagination := console.Pagination{
Limit: lim,
Offset: int64(offs),
Search: search,
Order: console.ProjectMemberOrder(order),
}
members, err := service.GetProjectMembers(p.Context, project.ID, pagination)
_, err := console.GetAuth(p.Context)
if err != nil {
return nil, err
}
_, err = console.GetAuth(p.Context)
cursor := cursorArgsToProjectMembersCursor(p.Args[CursorArg].(map[string]interface{}))
page, err := service.GetProjectMembers(p.Context, project.ID, cursor)
if err != nil {
return nil, err
}
var users []projectMember
for _, member := range members {
for _, member := range page.ProjectMembers {
user, err := service.GetUser(p.Context, member.MemberID)
if err != nil {
return nil, err
@ -152,7 +140,18 @@ func graphqlProject(service *console.Service, types *TypeCreator) *graphql.Objec
})
}
return users, nil
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{
@ -421,3 +420,20 @@ func fromMapBucketUsageCursor(args map[string]interface{}) (cursor console.Bucke
cursor.Search, _ = args[SearchArg].(string)
return
}
func cursorArgsToProjectMembersCursor(args map[string]interface{}) console.ProjectMembersCursor {
limit, _ := args[LimitArg].(int)
page, _ := args[PageArg].(int)
order, _ := args[OrderArg].(int)
orderDirection, _ := args[OrderDirectionArg].(int)
var cursor console.ProjectMembersCursor
cursor.Limit = uint(limit)
cursor.Page = uint(page)
cursor.Order = console.ProjectMemberOrder(order)
cursor.OrderDirection = console.ProjectMemberOrderDirection(orderDirection)
cursor.Search, _ = args[SearchArg].(string)
return cursor
}

View File

@ -38,8 +38,80 @@ func graphqlProjectMember(service *console.Service, types *TypeCreator) *graphql
})
}
func graphqlProjectMembersCursor() *graphql.InputObject {
return graphql.NewInputObject(graphql.InputObjectConfig{
Name: ProjectMembersCursorInputType,
Fields: graphql.InputObjectConfigFieldMap{
SearchArg: &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.String),
},
LimitArg: &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.Int),
},
PageArg: &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.Int),
},
OrderArg: &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.Int),
},
OrderDirectionArg: &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.Int),
},
},
})
}
func graphqlProjectMembersPage(types *TypeCreator) *graphql.Object {
return graphql.NewObject(graphql.ObjectConfig{
Name: ProjectMembersPageType,
Fields: graphql.Fields{
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,
},
},
})
}
// projectMember encapsulates User and joinedAt
type projectMember struct {
User *console.User
JoinedAt time.Time
}
type projectMembersPage struct {
ProjectMembers []projectMember
Search string
Limit uint
Order int
OrderDirection int
Offset uint64
PageCount uint
CurrentPage uint
TotalCount uint64
}

View File

@ -239,17 +239,22 @@ func TestGraphqlQuery(t *testing.T) {
t.Run("Project query team members", func(t *testing.T) {
query := fmt.Sprintf(
"query {project(id:\"%s\"){members(offset:0, limit:50){user{id,fullName,shortName,email,createdAt}}}}",
"query {project(id: \"%s\") {members( cursor: { limit: %d, search: \"%s\", page: %d, order: %d, orderDirection: %d } ) { projectMembers{ user { id, fullName, shortName, email, createdAt }, joinedAt }, search, limit, order, offset, pageCount, currentPage, totalCount } } }",
createdProject.ID.String(),
)
5,
"",
1,
1,
2)
result := testQuery(t, query)
data := result.(map[string]interface{})
project := data[consoleql.ProjectQuery].(map[string]interface{})
members := project[consoleql.FieldMembers].([]interface{})
members := project[consoleql.FieldMembers].(map[string]interface{})
projectMembers := members[consoleql.FieldProjectMembers].([]interface{})
assert.Equal(t, 3, len(members))
assert.Equal(t, 3, len(projectMembers))
testUser := func(t *testing.T, actual map[string]interface{}, expected *console.User) {
assert.Equal(t, expected.Email, actual[consoleql.FieldEmail])
@ -265,7 +270,7 @@ func TestGraphqlQuery(t *testing.T) {
var foundRoot, foundU1, foundU2 bool
for _, entry := range members {
for _, entry := range projectMembers {
member := entry.(map[string]interface{})
user := member[consoleql.UserType].(map[string]interface{})

View File

@ -18,21 +18,23 @@ type TypeCreator struct {
token *graphql.Object
user *graphql.Object
reward *graphql.Object
creditUsage *graphql.Object
project *graphql.Object
projectUsage *graphql.Object
bucketUsage *graphql.Object
bucketUsagePage *graphql.Object
paymentMethod *graphql.Object
projectMember *graphql.Object
apiKeyInfo *graphql.Object
createAPIKey *graphql.Object
user *graphql.Object
reward *graphql.Object
creditUsage *graphql.Object
project *graphql.Object
projectUsage *graphql.Object
bucketUsage *graphql.Object
bucketUsagePage *graphql.Object
paymentMethod *graphql.Object
projectMember *graphql.Object
projectMemberPage *graphql.Object
apiKeyInfo *graphql.Object
createAPIKey *graphql.Object
userInput *graphql.InputObject
projectInput *graphql.InputObject
bucketUsageCursor *graphql.InputObject
userInput *graphql.InputObject
projectInput *graphql.InputObject
bucketUsageCursor *graphql.InputObject
projectMembersCursor *graphql.InputObject
}
// Create create types and check for error
@ -53,6 +55,11 @@ func (c *TypeCreator) Create(log *zap.Logger, service *console.Service, mailServ
return err
}
c.projectMembersCursor = graphqlProjectMembersCursor()
if err := c.projectMembersCursor.Error(); err != nil {
return err
}
// entities
c.user = graphqlUser()
if err := c.user.Error(); err != nil {
@ -104,6 +111,11 @@ func (c *TypeCreator) Create(log *zap.Logger, service *console.Service, mailServ
return err
}
c.projectMemberPage = graphqlProjectMembersPage(c)
if err := c.projectMemberPage.Error(); err != nil {
return err
}
c.project = graphqlProject(service, c)
if err := c.project.Error(); err != nil {
return err

View File

@ -14,8 +14,8 @@ import (
type ProjectMembers interface {
// GetByMemberID is a method for querying project members from the database by memberID.
GetByMemberID(ctx context.Context, memberID uuid.UUID) ([]ProjectMember, error)
// GetByProjectID is a method for querying project members from the database by projectID, offset and limit.
GetByProjectID(ctx context.Context, projectID uuid.UUID, pagination Pagination) ([]ProjectMember, error)
// GetPagedByProjectID is a method for querying project members from the database by projectID and cursor
GetPagedByProjectID(ctx context.Context, projectID uuid.UUID, cursor ProjectMembersCursor) (*ProjectMembersPage, error)
// Insert is a method for inserting project member into the database.
Insert(ctx context.Context, memberID, projectID uuid.UUID) (*ProjectMember, error)
// Delete is a method for deleting project member by memberID and projectID from the database.
@ -32,12 +32,28 @@ type ProjectMember struct {
CreatedAt time.Time
}
// Pagination defines pagination, filtering and sorting rules
type Pagination struct {
Limit int
Offset int64
Search string
Order ProjectMemberOrder
// ProjectMembersCursor holds info for project members cursor pagination
type ProjectMembersCursor struct {
Search string
Limit uint
Page uint
Order ProjectMemberOrder
OrderDirection ProjectMemberOrderDirection
}
// ProjectMembersPage represent project members page result
type ProjectMembersPage struct {
ProjectMembers []ProjectMember
Search string
Limit uint
Order ProjectMemberOrder
OrderDirection ProjectMemberOrderDirection
Offset uint64
PageCount uint
CurrentPage uint
TotalCount uint64
}
// ProjectMemberOrder is used for querying project members in specified order
@ -51,3 +67,13 @@ const (
// Created indicates that we should order by created date
Created ProjectMemberOrder = 3
)
// ProjectMemberOrderDirection is used for querying project members in specific order direction
type ProjectMemberOrderDirection uint8
const (
// Ascending indicates that we should order ascending
Ascending ProjectMemberOrderDirection = 1
// Descending indicates that we should order descending
Descending ProjectMemberOrderDirection = 2
)

View File

@ -84,40 +84,45 @@ func TestProjectMembersRepository(t *testing.T) {
t.Run("Get paged", func(t *testing.T) {
// sql injection test. F.E '%SomeText%' = > ''%SomeText%' OR 'x' != '%'' will be true
members, err := projectMembers.GetByProjectID(ctx, createdProjects[0].ID, console.Pagination{Limit: 6, Offset: 0, Search: "son%' OR 'x' != '", Order: 2})
assert.NoError(t, err)
assert.Nil(t, members)
assert.Equal(t, 0, len(members))
members, err = projectMembers.GetByProjectID(ctx, createdProjects[0].ID, console.Pagination{Limit: 3, Offset: 0, Search: "", Order: 1})
members, err := projectMembers.GetPagedByProjectID(ctx, createdProjects[0].ID, console.ProjectMembersCursor{Limit: 6, Search: "son%' OR 'x' != '", Order: 2, Page: 1})
assert.NoError(t, err)
assert.NotNil(t, members)
assert.Equal(t, 3, len(members))
assert.Equal(t, uint64(0), members.TotalCount)
assert.Equal(t, uint(0), members.CurrentPage)
assert.Equal(t, uint(0), members.PageCount)
assert.Equal(t, 0, len(members.ProjectMembers))
members, err = projectMembers.GetByProjectID(ctx, createdProjects[0].ID, console.Pagination{Limit: 2, Offset: 0, Search: "iam", Order: 2}) // TODO: fix case sensitity issues and change back to "Liam"
members, err = projectMembers.GetPagedByProjectID(ctx, createdProjects[0].ID, console.ProjectMembersCursor{Limit: 3, Search: "", Order: 1, Page: 1})
assert.NoError(t, err)
assert.NotNil(t, members)
assert.Equal(t, 2, len(members))
assert.Equal(t, uint64(5), members.TotalCount)
assert.Equal(t, uint(1), members.CurrentPage)
assert.Equal(t, uint(2), members.PageCount)
assert.Equal(t, 3, len(members.ProjectMembers))
members, err = projectMembers.GetByProjectID(ctx, createdProjects[0].ID, console.Pagination{Limit: 2, Offset: 0, Search: "iam", Order: 1}) // TODO: fix case sensitity issues and change back to "Liam"
members, err = projectMembers.GetPagedByProjectID(ctx, createdProjects[0].ID, console.ProjectMembersCursor{Limit: 2, Search: "iam", Order: 2, Page: 1}) // TODO: fix case sensitity issues and change back to "Liam"
assert.NoError(t, err)
assert.NotNil(t, members)
assert.Equal(t, 2, len(members))
assert.Equal(t, uint64(2), members.TotalCount)
assert.Equal(t, 2, len(members.ProjectMembers))
members, err = projectMembers.GetByProjectID(ctx, createdProjects[0].ID, console.Pagination{Limit: 6, Offset: 0, Search: "son", Order: 123})
members, err = projectMembers.GetPagedByProjectID(ctx, createdProjects[0].ID, console.ProjectMembersCursor{Limit: 2, Search: "iam", Order: 1, Page: 1}) // TODO: fix case sensitity issues and change back to "Liam"
assert.NoError(t, err)
assert.NotNil(t, members)
assert.Equal(t, 5, len(members))
assert.Equal(t, uint64(2), members.TotalCount)
assert.Equal(t, 2, len(members.ProjectMembers))
members, err = projectMembers.GetByProjectID(ctx, createdProjects[0].ID, console.Pagination{Limit: 6, Offset: 3, Search: "son", Order: 2})
members, err = projectMembers.GetPagedByProjectID(ctx, createdProjects[0].ID, console.ProjectMembersCursor{Limit: 6, Search: "son", Order: 123, Page: 1})
assert.NoError(t, err)
assert.NotNil(t, members)
assert.Equal(t, 2, len(members))
assert.Equal(t, uint64(5), members.TotalCount)
assert.Equal(t, 5, len(members.ProjectMembers))
members, err = projectMembers.GetByProjectID(ctx, createdProjects[0].ID, console.Pagination{Limit: -123, Offset: -14, Search: "son", Order: 2})
assert.Error(t, err)
assert.Nil(t, members)
assert.Equal(t, 0, len(members))
members, err = projectMembers.GetPagedByProjectID(ctx, createdProjects[0].ID, console.ProjectMembersCursor{Limit: 6, Search: "son", Order: 2, Page: 1})
assert.NoError(t, err)
assert.NotNil(t, members)
assert.Equal(t, uint64(5), members.TotalCount)
assert.Equal(t, 5, len(members.ProjectMembers))
})
t.Run("Get member by memberID success", func(t *testing.T) {
@ -140,15 +145,15 @@ func TestProjectMembersRepository(t *testing.T) {
err := projectMembers.Delete(ctx, createdUsers[0].ID, createdProjects[0].ID)
assert.NoError(t, err)
projMembers, err := projectMembers.GetByProjectID(ctx, createdProjects[0].ID, console.Pagination{
projMembers, err := projectMembers.GetPagedByProjectID(ctx, createdProjects[0].ID, console.ProjectMembersCursor{
Order: 1,
Search: "",
Offset: 0,
Limit: 100,
Page: 1,
})
assert.NoError(t, err)
assert.NotNil(t, projectMembers)
assert.Equal(t, len(projMembers), 4)
assert.Equal(t, len(projMembers.ProjectMembers), 4)
})
})
}

View File

@ -895,7 +895,7 @@ func (s *Service) DeleteProjectMembers(ctx context.Context, projectID uuid.UUID,
}
// GetProjectMembers returns ProjectMembers for given Project
func (s *Service) GetProjectMembers(ctx context.Context, projectID uuid.UUID, pagination Pagination) (pm []ProjectMember, err error) {
func (s *Service) GetProjectMembers(ctx context.Context, projectID uuid.UUID, cursor ProjectMembersCursor) (pmp *ProjectMembersPage, err error) {
defer mon.Task()(&ctx)(&err)
auth, err := GetAuth(ctx)
if err != nil {
@ -907,11 +907,11 @@ func (s *Service) GetProjectMembers(ctx context.Context, projectID uuid.UUID, pa
return nil, ErrUnauthorized.Wrap(err)
}
if pagination.Limit > maxLimit {
pagination.Limit = maxLimit
if cursor.Limit > maxLimit {
cursor.Limit = maxLimit
}
pm, err = s.store.ProjectMembers().GetByProjectID(ctx, projectID, pagination)
pmp, err = s.store.ProjectMembers().GetPagedByProjectID(ctx, projectID, cursor)
if err != nil {
return nil, errs.New(internalErrMsg)
}

View File

@ -328,11 +328,11 @@ func (m *lockedProjectMembers) GetByMemberID(ctx context.Context, memberID uuid.
return m.db.GetByMemberID(ctx, memberID)
}
// GetByProjectID is a method for querying project members from the database by projectID, offset and limit.
func (m *lockedProjectMembers) GetByProjectID(ctx context.Context, projectID uuid.UUID, pagination console.Pagination) ([]console.ProjectMember, error) {
// GetPagedByProjectID is a method for querying project members from the database by projectID and cursor
func (m *lockedProjectMembers) GetPagedByProjectID(ctx context.Context, projectID uuid.UUID, cursor console.ProjectMembersCursor) (*console.ProjectMembersPage, error) {
m.Lock()
defer m.Unlock()
return m.db.GetByProjectID(ctx, projectID, pagination)
return m.db.GetPagedByProjectID(ctx, projectID, cursor)
}
// Insert is a method for inserting project member into the database.

View File

@ -32,30 +32,75 @@ func (pm *projectMembers) GetByMemberID(ctx context.Context, memberID uuid.UUID)
}
// GetByProjectID is a method for querying project members from the database by projectID, offset and limit.
func (pm *projectMembers) GetByProjectID(ctx context.Context, projectID uuid.UUID, pagination console.Pagination) (_ []console.ProjectMember, err error) {
func (pm *projectMembers) GetPagedByProjectID(ctx context.Context, projectID uuid.UUID, cursor console.ProjectMembersCursor) (_ *console.ProjectMembersPage, err error) {
defer mon.Task()(&ctx)(&err)
if pagination.Limit < 0 || pagination.Offset < 0 {
return nil, errs.New("invalid pagination argument")
search := "%" + strings.Replace(cursor.Search, " ", "%", -1) + "%"
if cursor.Limit > 50 {
cursor.Limit = 50
}
var projectMembers []console.ProjectMember
searchSubQuery := "%" + strings.Replace(pagination.Search, " ", "%", -1) + "%"
//`+getOrder(pagination.Order)+`
if cursor.Page == 0 {
return nil, errs.New("page cannot be 0")
}
page := &console.ProjectMembersPage{
Search: cursor.Search,
Limit: cursor.Limit,
Offset: uint64((cursor.Page - 1) * cursor.Limit),
Order: cursor.Order,
OrderDirection: cursor.OrderDirection,
}
countQuery := pm.db.Rebind(`
SELECT COUNT(*)
FROM project_members pm
INNER JOIN users u ON pm.member_id = u.id
WHERE pm.project_id = ?
AND ( u.email LIKE ? OR
u.full_name LIKE ? OR
u.short_name LIKE ?
)`)
countRow := pm.db.QueryRowContext(ctx,
countQuery,
projectID[:],
search,
search,
search)
err = countRow.Scan(&page.TotalCount)
if err != nil {
return nil, err
}
if page.TotalCount == 0 {
return page, nil
}
if page.Offset > page.TotalCount-1 {
return nil, errs.New("page is out of range")
}
// TODO: LIKE is case-sensitive postgres, however this should be case-insensitive and possibly allow typos
reboundQuery := pm.db.Rebind(`
SELECT pm.*
FROM project_members pm
INNER JOIN users u ON pm.member_id = u.id
WHERE pm.project_id = ?
AND ( u.email LIKE ? OR
u.full_name LIKE ? OR
u.short_name LIKE ? )
ORDER BY ` + sanitizedOrderColumnName(pagination.Order) + ` ASC
LIMIT ? OFFSET ?
`)
WHERE pm.project_id = ?
AND ( u.email LIKE ? OR
u.full_name LIKE ? OR
u.short_name LIKE ? )
ORDER BY ` + sanitizedOrderColumnName(cursor.Order) + `
` + sanitizeOrderDirectionName(page.OrderDirection) + `
LIMIT ? OFFSET ?`)
rows, err := pm.db.Query(reboundQuery, projectID[:], searchSubQuery, searchSubQuery, searchSubQuery, pagination.Limit, pagination.Offset)
rows, err := pm.db.QueryContext(ctx,
reboundQuery,
projectID[:],
search,
search,
search,
page.Limit,
page.Offset)
defer func() {
err = errs.Combine(err, rows.Close())
@ -65,6 +110,7 @@ func (pm *projectMembers) GetByProjectID(ctx context.Context, projectID uuid.UUI
return nil, err
}
var projectMembers []console.ProjectMember
for rows.Next() {
pm := console.ProjectMember{}
var memberIDBytes, projectIDBytes []uint8
@ -91,7 +137,17 @@ func (pm *projectMembers) GetByProjectID(ctx context.Context, projectID uuid.UUI
projectMembers = append(projectMembers, pm)
}
return projectMembers, err
page.ProjectMembers = projectMembers
page.Order = cursor.Order
page.PageCount = uint(page.TotalCount / uint64(cursor.Limit))
if page.TotalCount%uint64(cursor.Limit) != 0 {
page.PageCount++
}
page.CurrentPage = cursor.Page
return page, err
}
// Insert is a method for inserting project member into the database.
@ -155,6 +211,14 @@ func sanitizedOrderColumnName(pmo console.ProjectMemberOrder) string {
}
}
func sanitizeOrderDirectionName(pmo console.ProjectMemberOrderDirection) string {
if pmo == 2 {
return "DESC"
}
return "ASC"
}
// projectMembersFromDbxSlice is used for creating []ProjectMember entities from autogenerated []*dbx.ProjectMember struct
func projectMembersFromDbxSlice(ctx context.Context, projectMembersDbx []*dbx.ProjectMember) (_ []console.ProjectMember, err error) {
defer mon.Task()(&ctx)(&err)