From d88bf493de95d4fdef82a4afb20b6a41ea09c108 Mon Sep 17 00:00:00 2001 From: Nick Krichevsky Date: Mon, 18 Mar 2019 22:41:19 -0400 Subject: [PATCH] Add ClumpRepository with implementations --- repository/clump.go | 96 +++++++++++++++++++++++++++++++++++++++++++++ repository/paste.go | 12 +++++- 2 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 repository/clump.go diff --git a/repository/clump.go b/repository/clump.go new file mode 100644 index 0000000..b9bd96a --- /dev/null +++ b/repository/clump.go @@ -0,0 +1,96 @@ +package repository + +import ( + "errors" + + "github.com/google/uuid" +) + +// ErrPasteNotInDatabase is returned when one calls PutClump with pastes that are not in the database. +var ErrPasteNotInDatabase = errors.New("repository: given pastes are not in the database") + +// Clump represents a group ("clump") of pastes +type Clump struct { + ID int + Title string + Handle uuid.UUID + Pastes []Paste +} + +// ClumpRepository puts/gets a clump from the database +type ClumpRepository interface { + // GetClump gets a clump from the database + GetClump(handle uuid.UUID) (Clump, error) + // GetClump gets a clump from the database + PutClump(title string, pastes ...Paste) (Clump, error) +} + +// GetClump gets a clump and all of its associated pastes +func (db *DatabaseConnector) GetClump(handle uuid.UUID) (Clump, error) { + pasteRows, err := db.DB.Query("SELECT paste.*, clump.* FROM paste JOIN CLUMP USING (clumpID) WHERE handle = $1", handle.String()) + if err != nil { + return Clump{}, err + } + + clump := Clump{ + Pastes: []Paste{}, + } + + for pasteRows.Next() { + paste := Paste{} + // Per the docs, "The number of values in dest must be the same as the number of columns in Rows." + // Because the clump result will always be the same, we can just pull it on each row and update the same struct + pasteRows.Scan(&paste.ID, &paste.Filename, &paste.Handle, &paste.ClumpID, &clump.ID, &clump.Title, &clump.Handle) + clump.Pastes = append(clump.Pastes, paste) + } + + return clump, pasteRows.Err() +} + +// PutClump puts a clump into the database. +// All given pastes must have already been inserted to the database. Returns ErrPasteNotInDatabase if condition isn't met. +func (db *DatabaseConnector) PutClump(title string, pastes ...Paste) (Clump, error) { + handle, err := uuid.NewUUID() + if err != nil { + return Clump{}, err + } + + clump := Clump{ + Handle: handle, + } + + tx, err := db.DB.Begin() + insertResult := tx.QueryRow("INSERT INTO clump VALUES(DEFAULT, $1, $2) RETURNING clumpID", title, handle.String()) + err = insertResult.Scan(&clump.ID) + if err != nil { + tx.Rollback() + return Clump{}, err + } + + err = db.addPastesToClump(tx, clump.ID, pastes) + if err != nil { + tx.Rollback() + return Clump{}, err + } + + return clump, tx.Commit() +} + +func (db *DatabaseConnector) addPastesToClump(ex executor, clumpID int, pastes []Paste) error { + for _, paste := range pastes { + paste.ClumpID = clumpID + result, err := db.updatePaste(ex, paste) + if err != nil { + return err + } + + numRows, err := result.RowsAffected() + if err != nil { + return err + } else if numRows == 0 { + return ErrPasteNotInDatabase + } + } + + return nil +} diff --git a/repository/paste.go b/repository/paste.go index fd29ddc..1625210 100644 --- a/repository/paste.go +++ b/repository/paste.go @@ -1,6 +1,10 @@ package repository -import "github.com/google/uuid" +import ( + "database/sql" + + "github.com/google/uuid" +) // Paste represents a paste that the user has stored in the database type Paste struct { @@ -55,7 +59,11 @@ func (db *DatabaseConnector) PutPaste(filename string) (Paste, error) { // UpdatePaste allows for a paste to be updated. // Currently, only the filename and clump id can be updated func (db *DatabaseConnector) UpdatePaste(paste Paste) error { - _, err := db.DB.Exec("UPDATE paste SET filename = $1, clumpID = $2 WHERE pasteID = $3", paste.Filename, paste.ClumpID, paste.ID) + _, err := db.updatePaste(db.DB, paste) return err } + +func (db *DatabaseConnector) updatePaste(ex executor, paste Paste) (sql.Result, error) { + return db.DB.Exec("UPDATE paste SET filename = $1, clumpID = $2 WHERE pasteID = $3", paste.Filename, paste.ClumpID, paste.ID) +}