Skip to content

Commit

Permalink
Add trips feature
Browse files Browse the repository at this point in the history
  • Loading branch information
charlieegan3 committed Sep 10, 2023
1 parent 0852ffa commit e5925d2
Show file tree
Hide file tree
Showing 23 changed files with 1,541 additions and 25 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ test:
go test ./... $(GO_TEST_ARGS)

test_watch:
find . | grep $(FILE_PATTERN) | entr bash -c 'clear; make test'
find . | grep $(FILE_PATTERN) | entr bash -c 'clear; make GO_TEST_ARGS="$(GO_TEST_ARGS)" test'

test_db:
docker run -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres
Expand Down
18 changes: 18 additions & 0 deletions internal/pkg/database/databasetest/database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ import (
"github.com/charlieegan3/photos/internal/pkg/server/handlers/admin/medias"
"github.com/charlieegan3/photos/internal/pkg/server/handlers/admin/posts"
"github.com/charlieegan3/photos/internal/pkg/server/handlers/admin/tags"
"github.com/charlieegan3/photos/internal/pkg/server/handlers/admin/trips"
publicdevices "github.com/charlieegan3/photos/internal/pkg/server/handlers/public/devices"
publiclocations "github.com/charlieegan3/photos/internal/pkg/server/handlers/public/locations"
publicmedias "github.com/charlieegan3/photos/internal/pkg/server/handlers/public/medias"
publicposts "github.com/charlieegan3/photos/internal/pkg/server/handlers/public/posts"
publictags "github.com/charlieegan3/photos/internal/pkg/server/handlers/public/tags"
publictrips "github.com/charlieegan3/photos/internal/pkg/server/handlers/public/trips"
)

func TestDatabaseSuite(t *testing.T) {
Expand Down Expand Up @@ -162,6 +164,10 @@ func (s *DatabaseSuite) TestTaggingsSuite() {
suite.Run(s.T(), &database.TaggingsSuite{DB: s.DB})
}

func (s *DatabaseSuite) TestTripsSuite() {
suite.Run(s.T(), &database.TripsSuite{DB: s.DB})
}

func (s *DatabaseSuite) TestEndpointsDevicesSuite() {
// TODO move to suite to be shared
bucketBaseURL := "mem://test_bucket/"
Expand All @@ -176,6 +182,12 @@ func (s *DatabaseSuite) TestEndpointsDevicesSuite() {
})
}

func (s *DatabaseSuite) TestEndpointsTripsSuite() {
suite.Run(s.T(), &trips.EndpointsTripsSuite{
DB: s.DB,
})
}

func (s *DatabaseSuite) TestEndpointsLensesSuite() {
// TODO move to suite to be shared
bucketBaseURL := "mem://test_bucket/"
Expand Down Expand Up @@ -237,6 +249,12 @@ func (s *DatabaseSuite) TestPublicLocationsSuite() {
})
}

func (s *DatabaseSuite) TestPublicTripsSuite() {
suite.Run(s.T(), &publictrips.TripsSuite{
DB: s.DB,
})
}

func (s *DatabaseSuite) TestPublicDevicesSuite() {
bucketBaseURL := "mem://test_bucket/"
bucket, err := blob.OpenBucket(context.Background(), bucketBaseURL)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE IF EXISTS trips;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
CREATE TABLE trips (
id SERIAL NOT NULL PRIMARY KEY,

title text NOT NULL DEFAULT '',
description text NOT NULL DEFAULT '',

start_date TIMESTAMPTZ NOT NULL,
end_date TIMESTAMPTZ NOT NULL,


created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

-- updated_at is set by trigger_set_timestamp
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- trigger_set_timestamp sets updated_at when updating a record
CREATE TRIGGER set_timestamp_update
BEFORE UPDATE ON trips
FOR EACH ROW
EXECUTE PROCEDURE trigger_set_timestamp();

CREATE TRIGGER set_timestamp_insert
BEFORE INSERT ON trips
FOR EACH ROW
EXECUTE PROCEDURE trigger_set_timestamp();
188 changes: 188 additions & 0 deletions internal/pkg/database/trips.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package database

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

"github.com/doug-martin/goqu/v9"
"github.com/pkg/errors"

"github.com/charlieegan3/photos/internal/pkg/models"
)

type dbTrip struct {
ID int `db:"id"`

Title string `db:"title"`
Description string `db:"description"`

StartDate time.Time `db:"start_date"`
EndDate time.Time `db:"end_date"`

CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}

func (d *dbTrip) ToRecord(includeID bool) goqu.Record {
record := goqu.Record{
"title": d.Title,
"description": d.Description,
"start_date": d.StartDate.UTC(),
"end_date": d.EndDate.UTC(),
}

if includeID {
record["id"] = d.ID
}

return record
}

func newTrip(trip dbTrip) models.Trip {
return models.Trip{
ID: trip.ID,

Title: trip.Title,
Description: trip.Description,

StartDate: trip.StartDate.UTC(),
EndDate: trip.EndDate.UTC(),

CreatedAt: trip.CreatedAt,
UpdatedAt: trip.UpdatedAt,
}
}

func newDBTrip(trip models.Trip) dbTrip {
return dbTrip{
ID: trip.ID,

Title: trip.Title,
Description: trip.Description,

StartDate: trip.StartDate.UTC(),
EndDate: trip.EndDate.UTC(),

CreatedAt: trip.CreatedAt,
UpdatedAt: trip.UpdatedAt,
}
}

func CreateTrips(db *sql.DB, trips []models.Trip) (results []models.Trip, err error) {
records := []goqu.Record{}
for _, v := range trips {
d := newDBTrip(v)
records = append(records, d.ToRecord(false))
}

var dbTrips []dbTrip

goquDB := goqu.New("postgres", db)
insert := goquDB.Insert("trips").Returning(goqu.Star()).Rows(records).Executor()
if err := insert.ScanStructs(&dbTrips); err != nil {
return results, errors.Wrap(err, "failed to insert trips")
}

for _, v := range dbTrips {
results = append(results, newTrip(v))
}

return results, nil
}

func FindTripsByID(db *sql.DB, id []int) (results []models.Trip, err error) {
var dbTrips []dbTrip

goquDB := goqu.New("postgres", db)
insert := goquDB.From("trips").Select("*").Where(goqu.Ex{"id": id}).Executor()
if err := insert.ScanStructs(&dbTrips); err != nil {
return results, errors.Wrap(err, "failed to select trips by id")
}

for _, v := range dbTrips {
results = append(results, newTrip(v))
}

return results, nil
}

func AllTrips(db *sql.DB) (results []models.Trip, err error) {
var dbTrips []dbTrip

goquDB := goqu.New("postgres", db)
insert := goquDB.From("trips").Select("*").Order(goqu.I("start_date").Desc()).Executor()

if err := insert.ScanStructs(&dbTrips); err != nil {
return results, errors.Wrap(err, "failed to select trips")
}

// this is needed in case there are no items added, we don't want to return
// nil but rather an empty slice
results = []models.Trip{}
for _, v := range dbTrips {
results = append(results, newTrip(v))
}

return results, nil
}

func DeleteTrips(db *sql.DB, trips []models.Trip) (err error) {
var ids []int
for _, d := range trips {
ids = append(ids, d.ID)
}

goquDB := goqu.New("postgres", db)
del, _, err := goquDB.Delete("trips").Where(
goqu.Ex{"id": ids},
).ToSQL()
if err != nil {
return fmt.Errorf("failed to build trips delete query: %s", err)
}
_, err = db.Exec(del)
if err != nil {
return fmt.Errorf("failed to delete trips: %s", err)
}

return nil
}

// UpdateTrips is not implemented as a single SQL query since update many in
// place is not supported by goqu and it wasn't worth the work (TODO)
func UpdateTrips(db *sql.DB, trips []models.Trip) (results []models.Trip, err error) {
records := []goqu.Record{}
for _, v := range trips {
d := newDBTrip(v)
records = append(records, d.ToRecord(true))
}

goquDB := goqu.New("postgres", db)
tx, err := goquDB.Begin()
if err != nil {
return results, errors.Wrap(err, "failed to open tx for updating trips")
}

for _, record := range records {
var result dbTrip
update := tx.From("trips").
Where(goqu.Ex{"id": record["id"]}).
Update().
Set(record).
Returning(goqu.Star()).
Executor()
if _, err = update.ScanStruct(&result); err != nil {
if rErr := tx.Rollback(); rErr != nil {
return results, errors.Wrap(err, "failed to rollback")
}
return results, errors.Wrap(err, "failed to update, rolled back")
}

results = append(results, newTrip(result))
}
if err = tx.Commit(); err != nil {
return results, errors.Wrap(err, "failed to commit transaction")
}

return results, nil
}
Loading

0 comments on commit e5925d2

Please sign in to comment.