Potion/internal/infrastructure/database/repository/engagement_repository.go
Hayden Hargreaves d2835c636c (DB/FEAT): Began the implementation of the user engagement!
The database requirements have been added, as well as the service/repo
architecture. A few small functions have been created, but the system is
not complete by any means. More work is required to mark this task
complete.
2025-07-13 21:34:54 -07:00

154 lines
4.4 KiB
Go

package repository
import (
"database/sql"
"fmt"
"time"
domain "github.com/haydenhargreaves/Potion/internal/domain/engagement"
_ "github.com/lib/pq"
)
type EngagementRepository struct {
db *sql.DB
}
// Compile-time check to ensure the EngagementRepository implements domain.EngagementRepository
var _ domain.EngagementRepository = (*EngagementRepository)(nil)
// NewUserRepository creates a user repository object which is used by the user service to access
// the database. Any user related database operations will take place in this repository.
func NewEngagementRepository(db *sql.DB) domain.EngagementRepository {
return &EngagementRepository{db: db}
}
// AddUserEngagement creates an engagement record in the database with the user ID provided. This
// function does not accept an entity ID as it should be used when there is no need to reference
// an entity. The message should be provided, but a blank string ("") is acceptable. The engagement
// type parameter determines the labeling of the engagement in the database. Any errors will be
// bubbled to the caller.
func (r *EngagementRepository) AddUserEngagement(userId int, message string, engagementType domain.EngagementType) (domain.Engagement, error) {
tx, err := r.db.Begin()
if err != nil {
tx.Rollback()
return domain.Engagement{}, err
}
query := `
INSERT INTO Engagements (
type, message, entity, userid, created
) VALUES (
$1, $2, NULL, $3, $4
) RETURNING *;
`
var engagement domain.Engagement
if err := tx.QueryRow(query, engagementType, message, userId, time.Now()).Scan(
&engagement.Id,
&engagement.Type,
&engagement.Message,
&engagement.Entity,
&engagement.UserId,
&engagement.Created,
); err != nil {
tx.Rollback()
return domain.Engagement{}, fmt.Errorf("Failed to insert engagement into database. %s", err.Error())
}
if err := tx.Commit(); err != nil {
tx.Rollback()
return domain.Engagement{}, err
}
return engagement, nil
}
// AddUserEngagement creates an engagement record in the database with the user ID provided. This
// function requires an entity ID as it should be used when there is a reference to external an
// entity. The message should be provided, but a blank string ("") is acceptable. The engagement
// type parameter determines the labeling of the engagement in the database. Any errors will be
// bubbled to the caller.
func (r *EngagementRepository) AddUserEntityEngagement(userId, entityId int, message string, engagementType domain.EngagementType) (domain.Engagement, error) {
tx, err := r.db.Begin()
if err != nil {
tx.Rollback()
return domain.Engagement{}, err
}
query := `
INSERT INTO Engagements (
type, message, entity, userid, created
) VALUES (
$1, $2, $3, $4, $5
) RETURNING *;
`
var engagement domain.Engagement
if err := tx.QueryRow(query, engagementType, message, entityId, userId, time.Now()).Scan(
&engagement.Id,
&engagement.Type,
&engagement.Message,
&engagement.Entity,
&engagement.UserId,
&engagement.Created,
); err != nil {
tx.Rollback()
return domain.Engagement{}, fmt.Errorf("Failed to insert engagement into database. %s", err.Error())
}
if err := tx.Commit(); err != nil {
tx.Rollback()
return domain.Engagement{}, err
}
return engagement, nil
}
// GetUserEngagement returns a list of the users most recent engagement entries. The number of records
// is determined by the limit passed into this function. The results are sorted, newest-to-oldest.
func (r *EngagementRepository) GetUserEngagement(userId, limit int) ([]domain.Engagement, error) {
tx, err := r.db.Begin()
if err != nil {
tx.Rollback()
return []domain.Engagement{}, err
}
query := `
SELECT * FROM Engagements
WHERE Userid = $1
ORDER BY created DESC LIMIT $2;
`
rows, err := tx.Query(query, userId, limit)
if err != nil {
tx.Rollback()
return []domain.Engagement{}, fmt.Errorf("Failed to get user engagements. %s", err.Error())
}
defer rows.Close()
var engagements []domain.Engagement
for rows.Next() {
var engagement domain.Engagement
if err := rows.Scan(
&engagement.Id,
&engagement.Type,
&engagement.Message,
&engagement.Entity,
&engagement.UserId,
&engagement.Created,
); err != nil {
tx.Rollback()
return []domain.Engagement{}, fmt.Errorf("Failed to scan user engagement. %s", err.Error())
}
engagements = append(engagements, engagement)
}
if err := tx.Commit(); err != nil {
tx.Rollback()
return []domain.Engagement{}, err
}
return engagements, err
}