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 }