package lishwist import ( "database/sql" "errors" "fmt" "strings" "lishwist/core/internal/db" ) type Wish struct { Id string Name string ClaimantId string `json:",omitempty"` ClaimantName string `json:",omitempty"` Sent bool RecipientId string `json:",omitempty"` RecipientName string `json:",omitempty"` RecipientRef string `json:",omitempty"` CreatorId string `json:",omitempty"` CreatorName string `json:",omitempty"` } func (s *Session) GetWishes() ([]Wish, error) { stmt := "SELECT wish.id, wish.name, wish.sent FROM wish WHERE wish.creator_id = ?1 AND wish.recipient_id = ?1" rows, err := db.Connection.Query(stmt, s.User.Id) if err != nil { return nil, fmt.Errorf("Query execution failed: %w", err) } defer rows.Close() wishs := []Wish{} for rows.Next() { var id string var name string var sent bool err = rows.Scan(&id, &name, &sent) if err != nil { return nil, fmt.Errorf("Failed to scan a row: %w", err) } wish := Wish{ Id: id, Name: name, Sent: sent, } wishs = append(wishs, wish) } err = rows.Err() if err != nil { return nil, fmt.Errorf("Rows returned an error: %w", err) } return wishs, nil } func (s *Session) MakeWish(name string) error { stmt := "INSERT INTO wish (name, recipient_id, creator_id) VALUES (?, ?, ?)" _, err := db.Connection.Exec(stmt, strings.TrimSpace(name), s.User.Id, s.User.Id) if err != nil { return fmt.Errorf("Query execution failed: %w", err) } return nil } func (u *Session) deleteWishes(tx *sql.Tx, ids []string) error { stmt := "DELETE FROM wish WHERE wish.creator_id = ? AND wish.id = ?" for _, id := range ids { r, err := tx.Exec(stmt, u.Id, id) if err != nil { return err } rE, err := r.RowsAffected() if err != nil { return err } if rE < 1 { return fmt.Errorf("Wish deletion failed for '%s'", id) } } return nil } func (s *Session) RevokeWishes(ids ...string) error { if len(ids) < 1 { return fmt.Errorf("Attempt to remove zero wishes") } tx, err := db.Connection.Begin() if err != nil { return err } err = s.deleteWishes(tx, ids) if err != nil { rollBackErr := tx.Rollback() if rollBackErr != nil { return err } return err } err = tx.Commit() return err } func (s *Session) GetOthersWishes(userReference string) ([]Wish, error) { otherUser, err := getUserByReference(userReference) if err != nil { return nil, fmt.Errorf("Failed to get other user: %w", err) } if otherUser.Id == s.User.Id { return nil, errors.New("Use (s *Session) GetWishes() to view your own wishes") } stmt := "SELECT wish.id, wish.name, claimant.id, claimant.name, wish.sent, wish.creator_id, creator.name, wish.recipient_id FROM wish JOIN v_user AS user ON wish.recipient_id = user.id LEFT JOIN v_user AS claimant ON wish.claimant_id = claimant.id LEFT JOIN v_user AS creator ON wish.creator_id = creator.id WHERE user.id = ?" rows, err := db.Connection.Query(stmt, otherUser.Id) if err != nil { return nil, fmt.Errorf("Failed to execute query: %w", err) } defer rows.Close() wishes := []Wish{} for rows.Next() { var id string var name string var claimantId sql.NullString var claimantName sql.NullString var sent bool var creatorId string var creatorName string var recipientId string err = rows.Scan(&id, &name, &claimantId, &claimantName, &sent, &creatorId, &creatorName, &recipientId) if err != nil { return nil, fmt.Errorf("Failed to scan a row: %w", err) } wish := Wish{ Id: id, Name: name, ClaimantId: claimantId.String, ClaimantName: claimantName.String, Sent: sent, CreatorId: creatorId, CreatorName: creatorName, RecipientId: recipientId, } wishes = append(wishes, wish) } err = rows.Err() if err != nil { return nil, fmt.Errorf("Rows returned an error: %w", err) } return wishes, nil } // NOTE: This could just be a field on the user... but only if we get this often func (u *User) WishCount() (int, error) { stmt := "SELECT COUNT(wish.id) AS wish_count FROM wish WHERE wish.creator_id = ?1 AND wish.recipient_id = ?1" var wishCount int err := db.Connection.QueryRow(stmt, u.Id).Scan(&wishCount) if err != nil { return 0, err } return wishCount, nil } func (s *Session) executeClaims(tx *sql.Tx, claims, unclaims []string) error { claimStmt := "UPDATE wish SET claimant_id = ? WHERE id = ?" unclaimStmt := "UPDATE wish SET claimant_id = NULL WHERE id = ?" for _, id := range claims { r, err := tx.Exec(claimStmt, s.Id, id) if err != nil { return err } rE, err := r.RowsAffected() if err != nil { return err } if rE < 1 { return fmt.Errorf("Wish claim failed for '%s'", id) } } for _, id := range unclaims { r, err := tx.Exec(unclaimStmt, id) if err != nil { return err } rE, err := r.RowsAffected() if err != nil { return err } if rE < 1 { return fmt.Errorf("Wish unclaim failed for '%s'", id) } } return nil } // Undertake or abandon wishes made by other users func (s *Session) ClaimWishes(claims, unclaims []string) error { if len(claims) < 1 && len(unclaims) < 1 { return fmt.Errorf("Attempt to claim/unclaim zero wishes") } tx, err := db.Connection.Begin() if err != nil { return err } err = s.executeClaims(tx, claims, unclaims) if err != nil { rollBackErr := tx.Rollback() if rollBackErr != nil { return err } return err } err = tx.Commit() return err } func executeCompletions(tx *sql.Tx, claims []string) error { claimStmt := "UPDATE wish SET sent = 1 WHERE id = ?" for _, id := range claims { r, err := tx.Exec(claimStmt, id) if err != nil { return err } rE, err := r.RowsAffected() if err != nil { return err } if rE < 1 { return fmt.Errorf("Wish completion failed for '%s'", id) } } return nil } // TODO: User ought not be able to interact with wishes outside their group network func (s *Session) CompleteWishes(claims []string) error { if len(claims) < 1 { return fmt.Errorf("Attempt to complete zero wishes") } tx, err := db.Connection.Begin() if err != nil { return err } err = executeCompletions(tx, claims) if err != nil { rollBackErr := tx.Rollback() if rollBackErr != nil { return err } return err } err = tx.Commit() return err } func (u *Session) SuggestWishForUser(otherUserReference string, wishName string) error { otherUser, err := GetUserByReference(otherUserReference) if err != nil { return err } stmt := "INSERT INTO wish (name, recipient_id, creator_id) VALUES (?, ?, ?)" _, err = db.Connection.Exec(stmt, wishName, otherUser.Id, u.Id) if err != nil { return err } return nil } func (s *Session) RecindWishesForUser(ids ...string) error { if len(ids) < 1 { return fmt.Errorf("Attempt to remove zero wishes") } tx, err := db.Connection.Begin() if err != nil { return err } err = s.deleteWishes(tx, ids) if err != nil { rollBackErr := tx.Rollback() if rollBackErr != nil { return err } return err } err = tx.Commit() return err }