Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create event api #8

Merged
merged 9 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/push-docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
password: ${{ secrets.DOCKER_GH_TOKEN }}

- name: Build and push Docker image
uses: docker/build-push-action@v5
Expand Down
10 changes: 10 additions & 0 deletions apimodels/create_event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package apimodels

type CreateEvent struct {
Name string `json:"name"`
Description string `json:"description"`
StartDate string `json:"startDate"`
EndDate string `json:"endDate"`
VolunteersRequired int `json:"volunteersRequired"`
Location string `json:"location"`
}
3 changes: 2 additions & 1 deletion db/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ CREATE TABLE IF NOT EXISTS events (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
location TEXT,
start_date TIMESTAMP NOT NULL,
end_date TIMESTAMP NOT NULL,
volunteers_required INTEGER NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);

--- Users
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ require github.com/labstack/echo v3.3.10+incompatible

require (
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/joho/godotenv v1.5.1
github.com/labstack/gommon v0.4.1 // indirect
github.com/lib/pq v1.10.9
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/gommon v0.4.1 h1:gqEff0p/hTENGMABzezPoPSRtIh1Cvw0ueMOe0/dfOk=
github.com/labstack/gommon v0.4.1/go.mod h1:TyTrpPqxR5KMk8LKVtLmfMjeQ5FEkBYdxLYPw/WfrOM=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
Expand Down
76 changes: 75 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,89 @@
package main

import (
"blood-for-life-backend/apimodels"
"blood-for-life-backend/store"
"fmt"
"net/http"
"os"
"time"

"github.com/jmoiron/sqlx"
"github.com/joho/godotenv"
"github.com/labstack/echo"
_ "github.com/lib/pq"
)

func main() {
e := echo.New()

envErr := godotenv.Load(".env")

if envErr != nil {
fmt.Println(envErr.Error())
}

dbConnection := os.Getenv("DB")

db, err := sqlx.Connect("postgres", dbConnection)

if err != nil {
fmt.Println(err.Error())
}

defer db.Close()

if err := db.Ping(); err != nil {
fmt.Println(err.Error())
} else {
fmt.Println("Successfully Connected")
}

loadSchema(db)

eventStore := store.NewPGEventStore(db)
bind(e, eventStore)

e.Logger.Fatal(e.Start(":1323"))

}

func loadSchema(db *sqlx.DB) {
file, err := os.ReadFile("./db/schema.sql")
if err != nil {
fmt.Println(err.Error())
}

_, err = db.Exec(string(file))
if err != nil {
fmt.Println(err.Error())
}
}

func bind(e *echo.Echo, eventStore store.EventStore) {
rustamch marked this conversation as resolved.
Show resolved Hide resolved
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})
e.Logger.Fatal(e.Start(":1323"))

e.POST("/api/create", func(c echo.Context) error {
event := new(apimodels.CreateEvent)

// parse request body
bindErr := c.Bind(event)
if bindErr != nil {
return c.JSON(http.StatusBadRequest, bindErr)
}

// convert string to time.Time
start, _ := time.Parse("01/02/2006 03:04 PM", event.StartDate)
end, _ := time.Parse("01/02/2006 03:04 PM", event.EndDate)

_, err := eventStore.Create(c.Request().Context(), event.Name, event.Description, start, end, event.VolunteersRequired, event.Location)

if err != nil {
return c.JSON(http.StatusBadRequest, err)
}

return c.JSON(http.StatusOK, event)
})
}
123 changes: 65 additions & 58 deletions store/event.go
Original file line number Diff line number Diff line change
@@ -1,91 +1,98 @@
package store

import (
"context"
"fmt"
"time"
"github.com/jmoiron/sqlx"
"context"
"fmt"
"time"

"github.com/jmoiron/sqlx"
)

type Event struct {
ID int `db:"id" json:"id"`
Name string `db:"name" json:"name"`
Description string `db:"description" json:"description"`
StartDate time.Time `db:"start_date" json:"startDate"`
EndDate time.Time `db:"end_date" json:"endDate"`
VolunteersRequired int `db:"volunteers_required" json:"volunteersRequired"`
CreatedAt time.Time `db:"created_at" json:"createdAt"`
ID int `db:"id"`
Name string `db:"name"`
Description string `db:"description"`
StartDate time.Time `db:"start_date"`
EndDate time.Time `db:"end_date"`
VolunteersRequired int `db:"volunteers_required"`
Location string `db:"location"`
CreatedAt time.Time `db:"created_at"`
}

type EventStore interface {
GetAll(ctx context.Context) ([]Event, error)
GetOne(ctx context.Context, id int) (*Event, error)
GetOneByStartDate(ctx context.Context, startDate time.Time) (*Event, error)
GetOneByName(ctx context.Context, name string) (*Event, error) // ??
Create(ctx context.Context, event Event) (*Event, error)
Update(ctx context.Context, event Event) (*Event, error)
Delete(ctx context.Context, id int) error
GetAll(ctx context.Context) ([]Event, error)
rustamch marked this conversation as resolved.
Show resolved Hide resolved
GetOne(ctx context.Context, id int) (*Event, error)
GetOneByStartDate(ctx context.Context, startDate time.Time) (*Event, error)
GetOneByName(ctx context.Context, name string) (*Event, error) // ??
Create(ctx context.Context, name string, description string, start time.Time, end time.Time, volunteers int, location string) (*Event, error)
Update(ctx context.Context, event Event) (*Event, error)
Delete(ctx context.Context, id int) error
}

type pgEventStore struct {
db *sqlx.DB
db *sqlx.DB
}

func NewPGEventStore(db *sqlx.DB) EventStore {
return &pgEventStore{db}
return &pgEventStore{db}
}

func (s *pgEventStore) GetAll(ctx context.Context) ([]Event, error) {
var e []Event
err := s.db.SelectContext(ctx, &e, "SELECT * FROM events")
if err != nil {
return nil, fmt.Errorf("unable to retrieve events from database, error %w", err)
}
return e, nil
var e []Event
err := s.db.SelectContext(ctx, &e, "SELECT * FROM events")
if err != nil {
return nil, fmt.Errorf("unable to retrieve events from database, error %w", err)
}
return e, nil
}

func (s *pgEventStore) GetOne(ctx context.Context, id int) (*Event, error) {
var e Event
err := s.db.GetContext(ctx, &e, "SELECT * FROM events WHERE id = $1", id)
if err != nil {
return nil, fmt.Errorf("unable to find event with id, error %w", err)
}
return &e, nil
var e Event
err := s.db.GetContext(ctx, &e, "SELECT * FROM events WHERE id = $1", id)
if err != nil {
return nil, fmt.Errorf("unable to find event with id, error %w", err)
}
return &e, nil
}


func (s *pgEventStore) GetOneByName(ctx context.Context, name string) (*Event, error) {
var e Event
err := s.db.GetContext(ctx, &e, "SELECT * FROM events WHERE LOWER(name) = LOWER($1)", name)
if err != nil {
return nil, fmt.Errorf("unable to find event with name %s, with error %w", name, err)
}
return &e, nil
var e Event
err := s.db.GetContext(ctx, &e, "SELECT * FROM events WHERE LOWER(name) = LOWER($1)", name)
if err != nil {
return nil, fmt.Errorf("unable to find event with name %s, with error %w", name, err)
}
return &e, nil
}

// Don't know if I handled date right here
func (s *pgEventStore) GetOneByStartDate(ctx context.Context, date time.Time) (*Event, error) {
var e Event
formattedDate := date.Format("2006-01-02 15:04");
err := s.db.GetContext(ctx, &e, "SELECT * FROM events WHERE TO_CHAR(start_date, 'YYYY-MM-DD HH24:MI') = $1", formattedDate)
if err != nil {
return nil, fmt.Errorf("unable to find event with start date %s, with error %w", date, err)
}
return &e, nil
var e Event
formattedDate := date.Format("2006-01-02 15:04")
err := s.db.GetContext(ctx, &e, "SELECT * FROM events WHERE TO_CHAR(start_date, 'YYYY-MM-DD HH24:MI') = $1", formattedDate)
if err != nil {
return nil, fmt.Errorf("unable to find event with start date %s, with error %w", date, err)
}
return &e, nil
}

func (s *pgEventStore) Create(ctx context.Context, event Event) (*Event, error) {
query := "INSERT INTO events (name, description, start_date, end_date, volunteers_required) VALUES ($1, $2, $3, $4, $5) RETURNING id, created_at"
err := s.db.QueryRowContext(ctx, query, event.Name, event.Description, event.StartDate, event.EndDate, event.VolunteersRequired).Scan(&event.ID, &event.CreatedAt)
if err != nil {
return nil, fmt.Errorf("unable to create and store an event, error %w", err)
}
return &event, nil
func (s *pgEventStore) Create(ctx context.Context, name string, description string, start time.Time, end time.Time, volunteers int, location string) (*Event, error) {
id := 0
createdAt := time.Time{}

query := "INSERT INTO events (name, description, start_date, end_date, volunteers_required, location) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, created_at"
err := s.db.QueryRowContext(ctx, query, name, description, start, end, volunteers, location).Scan(&id, &createdAt)
if err != nil {
return nil, fmt.Errorf("unable to create and store an event, error %w", err)
}

event := Event{id, name, description, start, end, volunteers, location, createdAt}
return &event, nil
}

func (s *pgEventStore) Update(ctx context.Context, event Event) (*Event, error) {
query := "UPDATE events SET name = $1, description = $2, start_date = $3, end_date = $4, volunteers_required = $5, WHERE id = $6"
query := "UPDATE events SET name = $1, description = $2, start_date = $3, end_date = $4, volunteers_required = $5, location = $6, WHERE id = $7"

_, err := s.db.ExecContext(ctx, query, event.Name, event.Description, event.StartDate, event.EndDate, event.VolunteersRequired, event.ID)
_, err := s.db.ExecContext(ctx, query, event.Name, event.Description, event.StartDate, event.EndDate, event.VolunteersRequired, event.Location, event.ID)

if err != nil {
return nil, fmt.Errorf("unable to update event, error %w", err)
Expand All @@ -95,7 +102,7 @@ func (s *pgEventStore) Update(ctx context.Context, event Event) (*Event, error)
return &event, nil
}
func (s *pgEventStore) Delete(ctx context.Context, id int) error {
query := "DELETE FROM events WHERE id = $1"
query := "DELETE FROM events WHERE id = $1"

_, err := s.db.ExecContext(ctx, query, id)

Expand All @@ -104,5 +111,5 @@ func (s *pgEventStore) Delete(ctx context.Context, id int) error {

}

return nil
}
return nil
}
Loading