Skip to content

Commit

Permalink
Merge pull request #13 from connorwalsh/poet-crud
Browse files Browse the repository at this point in the history
Poets CRUD
  • Loading branch information
c authored Feb 18, 2018
2 parents 0bf771b + 7eda56e commit 15b6ddf
Show file tree
Hide file tree
Showing 5 changed files with 378 additions and 22 deletions.
4 changes: 4 additions & 0 deletions server/types/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,7 @@ func teardown() error {
func TestUserSuite(t *testing.T) {
suite.Run(t, &UserTestSuite{db: testDB})
}

func TestPoetSuite(t *testing.T) {
suite.Run(t, &PoetTestSuite{db: testDB})
}
181 changes: 169 additions & 12 deletions server/types/poet.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ package types

import (
"database/sql"
"fmt"
"time"

"github.com/connorwalsh/new-yorken-poesry-magazine/server/utils"
_ "github.com/lib/pq"
)

// Notes about poet executables:
Expand All @@ -11,18 +15,27 @@ import (
// executables will be stored on the filesystem in a safe dir with the path /some/path/bin/<poetId>/

type Poet struct {
Id string
God string // the writer of the poet (user)
BirthDate time.Time // so we can show years active
DeathDate time.Time // this should be set to null for currently active poets
Name string
Description string
ExecPath string // or possibly a Path, this is the path to the source code
Id string `json:"id"`
Designer string `json:"designer"` // the writer of the poet (user)
BirthDate time.Time `json:"birthDate"` // so we can show years active
DeathDate time.Time `json:"deathDate"` // this should be set to null for currently active poets
Name string `json:"name"`
Description string `json:"description"`
ExecPath string `json:"execPath"` // or possibly a Path, this is the path to the source code
// TODO additional statistics: specifically, it would be cool to see the success rate
// of a particular poet along with the timeline of how their poems have been recieved

// what if we also had a poet obituary for when poets are "retired"
}

func (p *Poet) Validate() error {
func (p *Poet) Validate(action string) error {
// make sure id, if not an empty string, is a uuid
if !utils.IsValidUUIDV4(p.Id) && p.Id != "" {
return fmt.Errorf("User Id must be a valid uuid, given %s", p.Id)
}

// TODO ensure that only the user namking the create and delete request can perform
// those actions!

return nil
}
Expand All @@ -31,14 +44,22 @@ func (p *Poet) Validate() error {
db methods
*/

func (*Poet) CreateTable(db *sql.DB) error {
// package level globals for storing prepared sql statements
var (
poetCreateStmt *sql.Stmt
poetReadStmt *sql.Stmt
poetReadAllStmt *sql.Stmt
poetDeleteStmt *sql.Stmt
)

func CreatePoetsTable(db *sql.DB) error {
mkTableStmt := `CREATE TABLE IF NOT EXISTS poets (
id UUID NOT NULL UNIQUE,
god UUID REFERENCES users NOT NULL,
designer UUID REFERENCES users NOT NULL,
birthDate TIMESTAMP WITH TIME ZONE NOT NULL,
deathDate TIMESTAMP WITH TIME ZONE,
deathDate TIMESTAMP WITH TIME ZONE NOT NULL,
name VARCHAR(255) NOT NULL UNIQUE,
description TEXT,
description TEXT NOT NULL,
execPath VARCHAR(255) NOT NULL UNIQUE,
PRIMARY KEY (id)
)`
Expand All @@ -50,3 +71,139 @@ func (*Poet) CreateTable(db *sql.DB) error {

return nil
}

// TODO persist files to the filesystem for poet execs
func (p *Poet) Create(id string, db *sql.DB) error {
var (
err error
)

// assign id
p.Id = id

// set birthday
p.BirthDate = time.Now().Truncate(time.Millisecond)

// prepare statement if not already done so.
if poetCreateStmt == nil {
// create statement
stmt := `INSERT INTO poets (
id, designer, name, birthDate, deathDate, description, execPath
) VALUES ($1, $2, $3, $4, $5, $6, $7)`
poetCreateStmt, err = db.Prepare(stmt)
if err != nil {
return err
}
}

_, err = poetCreateStmt.Exec(
p.Id,
p.Designer,
p.Name,
p.BirthDate,
p.DeathDate,
p.Description,
p.ExecPath,
)
if err != nil {
return err
}

return nil
}

func (p *Poet) Read(db *sql.DB) error {
var (
err error
)

// prepare statement if not already done so.
if poetReadStmt == nil {
// read statement
stmt := `SELECT id, designer, name, birthDate, deathDate, description, execPath
FROM poets WHERE id = $1`
poetReadStmt, err = db.Prepare(stmt)
if err != nil {
return err
}
}

// make sure user Id is actually populated

// run prepared query over arguments
err = poetReadStmt.
QueryRow(p.Id).
Scan(
&p.Id,
&p.Designer,
&p.Name,
&p.BirthDate,
&p.DeathDate,
&p.Description,
&p.ExecPath,
)
switch {
case err == sql.ErrNoRows:
return fmt.Errorf("No poet with id %s", p.Id)
case err != nil:
return err
}

return nil
}

// delete should keep meta about poets in the system along with their poems, but
// should remove all files from the file system and assign a death date.
func (p *Poet) Delete(db *sql.DB) error {

return nil
}

func ReadPoets(db *sql.DB) ([]*Poet, error) {
var (
poets []*Poet = []*Poet{}
err error
)

// prepare statement if not already done so.
if poetReadAllStmt == nil {
// readAll statement
// TODO pagination
stmt := `SELECT id, designer, name, birthDate, deathDate, description, execPath
FROM poets`
poetReadAllStmt, err = db.Prepare(stmt)
if err != nil {
return poets, nil
}
}

rows, err := poetReadAllStmt.Query()
if err != nil {
return poets, err
}

defer rows.Close()
for rows.Next() {
poet := &Poet{}
err = rows.Scan(
&poet.Id,
&poet.Designer,
&poet.Name,
&poet.BirthDate,
&poet.DeathDate,
&poet.Description,
&poet.ExecPath,
)
if err != nil {
return poets, err
}

// append scanned user into list of all users
poets = append(poets, poet)
}
if err := rows.Err(); err != nil {
return poets, err
}

return poets, nil
}
Loading

0 comments on commit 15b6ddf

Please sign in to comment.