Changeset 0.16.0 (#74)
This commit is contained in:
206
server/store/postgres/groups.go
Normal file
206
server/store/postgres/groups.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"git.gobha.me/xcaliber/chat-switchboard/models"
|
||||
)
|
||||
|
||||
// ── GroupStore ──────────────────────────────
|
||||
|
||||
type GroupStore struct{}
|
||||
|
||||
func NewGroupStore() *GroupStore { return &GroupStore{} }
|
||||
|
||||
func (s *GroupStore) Create(ctx context.Context, g *models.Group) error {
|
||||
return DB.QueryRowContext(ctx, `
|
||||
INSERT INTO groups (name, description, scope, team_id, created_by)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING id, created_at, updated_at`,
|
||||
g.Name, g.Description, g.Scope,
|
||||
models.NullString(g.TeamID), g.CreatedBy,
|
||||
).Scan(&g.ID, &g.CreatedAt, &g.UpdatedAt)
|
||||
}
|
||||
|
||||
func (s *GroupStore) GetByID(ctx context.Context, id string) (*models.Group, error) {
|
||||
var g models.Group
|
||||
var teamID sql.NullString
|
||||
err := DB.QueryRowContext(ctx, `
|
||||
SELECT g.id, g.name, g.description, g.scope, g.team_id, g.created_by,
|
||||
g.created_at, g.updated_at,
|
||||
(SELECT COUNT(*) FROM group_members WHERE group_id = g.id) AS member_count
|
||||
FROM groups g WHERE g.id = $1`, id).Scan(
|
||||
&g.ID, &g.Name, &g.Description, &g.Scope, &teamID, &g.CreatedBy,
|
||||
&g.CreatedAt, &g.UpdatedAt, &g.MemberCount,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.TeamID = NullableStringPtr(teamID)
|
||||
return &g, nil
|
||||
}
|
||||
|
||||
func (s *GroupStore) Update(ctx context.Context, id string, name, description *string) error {
|
||||
b := NewUpdate("groups")
|
||||
if name != nil {
|
||||
b.Set("name", *name)
|
||||
}
|
||||
if description != nil {
|
||||
b.Set("description", *description)
|
||||
}
|
||||
if !b.HasSets() {
|
||||
return nil
|
||||
}
|
||||
b.Where("id", id)
|
||||
res, err := b.Exec(DB)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n, _ := res.RowsAffected(); n == 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *GroupStore) Delete(ctx context.Context, id string) error {
|
||||
res, err := DB.ExecContext(ctx, "DELETE FROM groups WHERE id = $1", id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n, _ := res.RowsAffected(); n == 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ── Scoped Listing ──────────────────────────
|
||||
|
||||
func (s *GroupStore) ListAll(ctx context.Context) ([]models.Group, error) {
|
||||
return queryGroups(ctx, `
|
||||
SELECT g.id, g.name, g.description, g.scope, g.team_id, g.created_by,
|
||||
g.created_at, g.updated_at,
|
||||
(SELECT COUNT(*) FROM group_members WHERE group_id = g.id)
|
||||
FROM groups g
|
||||
ORDER BY g.scope, g.name`)
|
||||
}
|
||||
|
||||
func (s *GroupStore) ListForTeam(ctx context.Context, teamID string) ([]models.Group, error) {
|
||||
return queryGroups(ctx, `
|
||||
SELECT g.id, g.name, g.description, g.scope, g.team_id, g.created_by,
|
||||
g.created_at, g.updated_at,
|
||||
(SELECT COUNT(*) FROM group_members WHERE group_id = g.id)
|
||||
FROM groups g
|
||||
WHERE g.scope = 'team' AND g.team_id = $1
|
||||
ORDER BY g.name`, teamID)
|
||||
}
|
||||
|
||||
func (s *GroupStore) ListForUser(ctx context.Context, userID string) ([]models.Group, error) {
|
||||
return queryGroups(ctx, `
|
||||
SELECT g.id, g.name, g.description, g.scope, g.team_id, g.created_by,
|
||||
g.created_at, g.updated_at,
|
||||
(SELECT COUNT(*) FROM group_members WHERE group_id = g.id)
|
||||
FROM groups g
|
||||
WHERE g.id IN (SELECT group_id FROM group_members WHERE user_id = $1)
|
||||
ORDER BY g.scope, g.name`, userID)
|
||||
}
|
||||
|
||||
// ── Members ─────────────────────────────────
|
||||
|
||||
func (s *GroupStore) AddMember(ctx context.Context, groupID, userID, addedBy string) error {
|
||||
_, err := DB.ExecContext(ctx, `
|
||||
INSERT INTO group_members (group_id, user_id, added_by)
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT (group_id, user_id) DO NOTHING`,
|
||||
groupID, userID, addedBy)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *GroupStore) RemoveMember(ctx context.Context, groupID, userID string) error {
|
||||
res, err := DB.ExecContext(ctx,
|
||||
"DELETE FROM group_members WHERE group_id = $1 AND user_id = $2",
|
||||
groupID, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n, _ := res.RowsAffected(); n == 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *GroupStore) ListMembers(ctx context.Context, groupID string) ([]models.GroupMember, error) {
|
||||
rows, err := DB.QueryContext(ctx, `
|
||||
SELECT gm.id, gm.group_id, gm.user_id, gm.added_by, gm.added_at,
|
||||
u.username, u.email, COALESCE(u.display_name, '')
|
||||
FROM group_members gm
|
||||
JOIN users u ON u.id = gm.user_id
|
||||
WHERE gm.group_id = $1
|
||||
ORDER BY u.username`, groupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var result []models.GroupMember
|
||||
for rows.Next() {
|
||||
var m models.GroupMember
|
||||
if err := rows.Scan(&m.ID, &m.GroupID, &m.UserID, &m.AddedBy, &m.AddedAt,
|
||||
&m.Username, &m.Email, &m.DisplayName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, m)
|
||||
}
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func (s *GroupStore) IsMember(ctx context.Context, groupID, userID string) (bool, error) {
|
||||
var exists bool
|
||||
err := DB.QueryRowContext(ctx, `
|
||||
SELECT EXISTS(SELECT 1 FROM group_members WHERE group_id = $1 AND user_id = $2)`,
|
||||
groupID, userID).Scan(&exists)
|
||||
return exists, err
|
||||
}
|
||||
|
||||
func (s *GroupStore) GetUserGroupIDs(ctx context.Context, userID string) ([]string, error) {
|
||||
rows, err := DB.QueryContext(ctx,
|
||||
"SELECT group_id FROM group_members WHERE user_id = $1", userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var ids []string
|
||||
for rows.Next() {
|
||||
var id string
|
||||
if err := rows.Scan(&id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return ids, rows.Err()
|
||||
}
|
||||
|
||||
// ── Scanners ────────────────────────────────
|
||||
|
||||
func queryGroups(ctx context.Context, q string, args ...interface{}) ([]models.Group, error) {
|
||||
rows, err := DB.QueryContext(ctx, q, args...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("queryGroups: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var result []models.Group
|
||||
for rows.Next() {
|
||||
var g models.Group
|
||||
var teamID sql.NullString
|
||||
if err := rows.Scan(&g.ID, &g.Name, &g.Description, &g.Scope, &teamID,
|
||||
&g.CreatedBy, &g.CreatedAt, &g.UpdatedAt, &g.MemberCount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.TeamID = NullableStringPtr(teamID)
|
||||
result = append(result, g)
|
||||
}
|
||||
return result, rows.Err()
|
||||
}
|
||||
Reference in New Issue
Block a user