storj/satellite/satellitedb/projectmembers.go
Egon Elbre 2268cc1df3 all: fix linter complaints
Change-Id: Ia01404dbb6bdd19a146fa10ff7302e08f87a8c95
2020-10-13 15:59:01 +03:00

232 lines
6.0 KiB
Go

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package satellitedb
import (
"context"
"strings"
"github.com/zeebo/errs"
"storj.io/common/uuid"
"storj.io/storj/satellite/console"
"storj.io/storj/satellite/satellitedb/dbx"
)
// ensures that projectMembers implements console.ProjectMembers.
var _ console.ProjectMembers = (*projectMembers)(nil)
// ProjectMembers exposes methods to manage ProjectMembers table in database.
type projectMembers struct {
methods dbx.Methods
db *satelliteDB
}
// GetByMemberID is a method for querying project member from the database by memberID.
func (pm *projectMembers) GetByMemberID(ctx context.Context, memberID uuid.UUID) (_ []console.ProjectMember, err error) {
defer mon.Task()(&ctx)(&err)
projectMembersDbx, err := pm.methods.All_ProjectMember_By_MemberId(ctx, dbx.ProjectMember_MemberId(memberID[:]))
if err != nil {
return nil, err
}
return projectMembersFromDbxSlice(ctx, projectMembersDbx)
}
// GetByProjectID is a method for querying project members from the database by projectID, offset and limit.
func (pm *projectMembers) GetPagedByProjectID(ctx context.Context, projectID uuid.UUID, cursor console.ProjectMembersCursor) (_ *console.ProjectMembersPage, err error) {
defer mon.Task()(&ctx)(&err)
search := "%" + strings.ReplaceAll(cursor.Search, " ", "%") + "%"
if cursor.Limit > 50 {
cursor.Limit = 50
}
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(cursor.Order) + `
` + sanitizeOrderDirectionName(page.OrderDirection) + `
LIMIT ? OFFSET ?`)
rows, err := pm.db.QueryContext(ctx,
reboundQuery,
projectID[:],
search,
search,
search,
page.Limit,
page.Offset)
defer func() {
err = errs.Combine(err, rows.Close())
}()
if err != nil {
return nil, err
}
var projectMembers []console.ProjectMember
for rows.Next() {
pm := console.ProjectMember{}
err = rows.Scan(&pm.MemberID, &pm.ProjectID, &pm.CreatedAt)
if err != nil {
return nil, err
}
projectMembers = append(projectMembers, pm)
}
if err := rows.Err(); err != nil {
return nil, 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.
func (pm *projectMembers) Insert(ctx context.Context, memberID, projectID uuid.UUID) (_ *console.ProjectMember, err error) {
defer mon.Task()(&ctx)(&err)
createdProjectMember, err := pm.methods.Create_ProjectMember(ctx,
dbx.ProjectMember_MemberId(memberID[:]),
dbx.ProjectMember_ProjectId(projectID[:]))
if err != nil {
return nil, err
}
return projectMemberFromDBX(ctx, createdProjectMember)
}
// Delete is a method for deleting project member by memberID and projectID from the database.
func (pm *projectMembers) Delete(ctx context.Context, memberID, projectID uuid.UUID) (err error) {
defer mon.Task()(&ctx)(&err)
_, err = pm.methods.Delete_ProjectMember_By_MemberId_And_ProjectId(
ctx,
dbx.ProjectMember_MemberId(memberID[:]),
dbx.ProjectMember_ProjectId(projectID[:]),
)
return err
}
// projectMemberFromDBX is used for creating ProjectMember entity from autogenerated dbx.ProjectMember struct.
func projectMemberFromDBX(ctx context.Context, projectMember *dbx.ProjectMember) (_ *console.ProjectMember, err error) {
defer mon.Task()(&ctx)(&err)
if projectMember == nil {
return nil, errs.New("projectMember parameter is nil")
}
memberID, err := uuid.FromBytes(projectMember.MemberId)
if err != nil {
return nil, err
}
projectID, err := uuid.FromBytes(projectMember.ProjectId)
if err != nil {
return nil, err
}
return &console.ProjectMember{
MemberID: memberID,
ProjectID: projectID,
CreatedAt: projectMember.CreatedAt,
}, nil
}
// sanitizedOrderColumnName return valid order by column.
func sanitizedOrderColumnName(pmo console.ProjectMemberOrder) string {
switch pmo {
case 2:
return "u.email"
case 3:
return "pm.created_at"
default:
return "u.full_name"
}
}
func sanitizeOrderDirectionName(pmo console.OrderDirection) 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)
var projectMembers []console.ProjectMember
var errors []error
// Generating []dbo from []dbx and collecting all errors
for _, projectMemberDbx := range projectMembersDbx {
projectMember, err := projectMemberFromDBX(ctx, projectMemberDbx)
if err != nil {
errors = append(errors, err)
continue
}
projectMembers = append(projectMembers, *projectMember)
}
return projectMembers, errs.Combine(errors...)
}