159 lines
3.3 KiB
Go
159 lines
3.3 KiB
Go
package projectsrepo
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
)
|
|
|
|
type Repo struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
type Project struct {
|
|
ID int64
|
|
GuildID int64
|
|
Name string
|
|
Description string
|
|
IsActive bool
|
|
CreatedBy int64
|
|
}
|
|
|
|
type ProjectWithMembers struct {
|
|
Project *Project
|
|
Creator int64
|
|
Helpers []int64
|
|
}
|
|
|
|
func NewRepo(db *sql.DB) *Repo {
|
|
return &Repo{db: db}
|
|
}
|
|
|
|
func (r *Repo) CreateProject(ctx context.Context, guildID, creatorID int64, name, description string) (int64, error) {
|
|
const q = `
|
|
INSERT INTO projects (guild_id, name, description, created_by)
|
|
VALUES ($1, $2, $3, $4)
|
|
RETURNING id
|
|
`
|
|
var id int64
|
|
if err := r.db.QueryRowContext(ctx, q, guildID, name, description, creatorID).Scan(&id); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
const qMember = `
|
|
INSERT INTO project_members (project_id, user_id, is_creator)
|
|
VALUES ($1, $2, TRUE)
|
|
ON CONFLICT (project_id, user_id) DO NOTHING
|
|
`
|
|
if _, err := r.db.ExecContext(ctx, qMember, id, creatorID); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return id, nil
|
|
}
|
|
|
|
func (r *Repo) AddHelper(ctx context.Context, projectID, userID int64) error {
|
|
const q = `
|
|
INSERT INTO project_members (project_id, user_id, is_creator)
|
|
VALUES ($1, $2, FALSE)
|
|
ON CONFLICT (project_id, user_id) DO NOTHING
|
|
`
|
|
_, err := r.db.ExecContext(ctx, q, projectID, userID)
|
|
return err
|
|
}
|
|
|
|
func (r *Repo) ProjectExistsInGuild(ctx context.Context, guildID, projectID int64) (bool, error) {
|
|
const q = `
|
|
SELECT 1
|
|
FROM projects
|
|
WHERE guild_id = $1 AND id = $2
|
|
`
|
|
row := r.db.QueryRowContext(ctx, q, guildID, projectID)
|
|
var one int
|
|
switch err := row.Scan(&one); err {
|
|
case nil:
|
|
return true, nil
|
|
case sql.ErrNoRows:
|
|
return false, nil
|
|
default:
|
|
return false, err
|
|
}
|
|
}
|
|
|
|
func (r *Repo) ListActiveProjectsWithMembers(ctx context.Context, guildID int64, limit int) ([]*ProjectWithMembers, error) {
|
|
const qProjects = `
|
|
SELECT id, name, description, created_by
|
|
FROM projects
|
|
WHERE guild_id = $1 AND is_active = TRUE
|
|
ORDER BY created_at DESC
|
|
LIMIT $2
|
|
`
|
|
rows, err := r.db.QueryContext(ctx, qProjects, guildID, limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var out []*ProjectWithMembers
|
|
for rows.Next() {
|
|
p := &Project{GuildID: guildID, IsActive: true}
|
|
if err := rows.Scan(&p.ID, &p.Name, &p.Description, &p.CreatedBy); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, &ProjectWithMembers{
|
|
Project: p,
|
|
Creator: p.CreatedBy,
|
|
})
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
if len(out) == 0 {
|
|
return out, nil
|
|
}
|
|
|
|
// Load members per project.
|
|
const qMembers = `
|
|
SELECT project_id, user_id, is_creator
|
|
FROM project_members
|
|
WHERE project_id = ANY($1)
|
|
`
|
|
ids := make([]int64, len(out))
|
|
for i, p := range out {
|
|
ids[i] = p.Project.ID
|
|
}
|
|
|
|
rowsM, err := r.db.QueryContext(ctx, qMembers, ids)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rowsM.Close()
|
|
|
|
index := make(map[int64]*ProjectWithMembers, len(out))
|
|
for _, p := range out {
|
|
index[p.Project.ID] = p
|
|
}
|
|
|
|
for rowsM.Next() {
|
|
var pid, uid int64
|
|
var isCreator bool
|
|
if err := rowsM.Scan(&pid, &uid, &isCreator); err != nil {
|
|
return nil, err
|
|
}
|
|
item, ok := index[pid]
|
|
if !ok {
|
|
continue
|
|
}
|
|
if isCreator {
|
|
item.Creator = uid
|
|
} else {
|
|
item.Helpers = append(item.Helpers, uid)
|
|
}
|
|
}
|
|
if err := rowsM.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|