From 032d2b46c6be34d5e0da3e42ae703492b97ff5f8 Mon Sep 17 00:00:00 2001 From: wgroeneveld Date: Mon, 15 Apr 2024 10:27:31 +0200 Subject: [PATCH] initial commit --- .gitignore | 2 ++ go.mod | 11 +++++++++++ go.sum | 10 ++++++++++ main.go | 26 ++++++++++++++++++++++++++ pokemon/handler.go | 35 +++++++++++++++++++++++++++++++++++ pokemon/pokemon.go | 15 +++++++++++++++ pokemon/repo.go | 36 ++++++++++++++++++++++++++++++++++++ pokemon/seeder.go | 34 ++++++++++++++++++++++++++++++++++ rest/restutils.go | 29 +++++++++++++++++++++++++++++ server.go | 25 +++++++++++++++++++++++++ 10 files changed, 223 insertions(+) create mode 100644 .gitignore create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 pokemon/handler.go create mode 100644 pokemon/pokemon.go create mode 100644 pokemon/repo.go create mode 100644 pokemon/seeder.go create mode 100644 rest/restutils.go create mode 100644 server.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4b6d04c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +pokedex.db diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a62f532 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module pokedex + +go 1.22 + +require ( + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect + gorm.io/driver/sqlite v1.5.5 // indirect + gorm.io/gorm v1.25.9 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..9ec6af8 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E= +gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE= +gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8= +gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= diff --git a/main.go b/main.go new file mode 100644 index 0000000..6d41cd9 --- /dev/null +++ b/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "gorm.io/driver/sqlite" + "gorm.io/gorm" + "net/http" + "os" + "pokedex/pokemon" +) + +func openNewDb() *gorm.DB { + os.Remove("pokedex.db") + db, err := gorm.Open(sqlite.Open("pokedex.db"), &gorm.Config{}) + if err != nil { + panic("failed to connect database") + } + return db +} + +func main() { + db := openNewDb() + pokemon.Seed(db) + + routes(db) + http.ListenAndServe(":8080", nil) +} diff --git a/pokemon/handler.go b/pokemon/handler.go new file mode 100644 index 0000000..999e2e5 --- /dev/null +++ b/pokemon/handler.go @@ -0,0 +1,35 @@ +package pokemon + +import ( + "fmt" + "gorm.io/gorm" + "net/http" + "pokedex/rest" +) + +func HandleFindSingle(db *gorm.DB, rw http.ResponseWriter, req *http.Request) { + repo := NewRepo(db) + name := req.PathValue("name") + fmt.Println("Path " + name) + + pk, err := repo.Find(name) + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + + rest.Json(rw, []Pokemon{ + pk, + }) +} + +func HandleFindAll(db *gorm.DB, rw http.ResponseWriter, req *http.Request) { + repo := NewRepo(db) + pokemons, err := repo.FindAll() + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + + rest.Json(rw, pokemons) +} diff --git a/pokemon/pokemon.go b/pokemon/pokemon.go new file mode 100644 index 0000000..5225360 --- /dev/null +++ b/pokemon/pokemon.go @@ -0,0 +1,15 @@ +package pokemon + +type Move struct { + Name string + Url string + PokemonID uint // implicit; see https://gorm.io/docs/has_many.html +} + +type Pokemon struct { + Id int + Name string + Height int + Weight int + Moves []Move +} diff --git a/pokemon/repo.go b/pokemon/repo.go new file mode 100644 index 0000000..c72d266 --- /dev/null +++ b/pokemon/repo.go @@ -0,0 +1,36 @@ +package pokemon + +import ( + "fmt" + "gorm.io/gorm" +) + +type Repo struct { + db *gorm.DB +} + +func NewRepo(db *gorm.DB) Repo { + return Repo{ + db: db, + } +} + +func (r Repo) Find(name string) (Pokemon, error) { + var pokemon Pokemon + result := r.db.Find(&pokemon, "name = ?", name) // .First() generates an error if none found + if result.Error != nil { + return Pokemon{}, fmt.Errorf("Unable to retrive pokemon named %s: %w", name, result.Error) + } + + return pokemon, nil +} + +func (r Repo) FindAll() ([]Pokemon, error) { + var pokemons []Pokemon + result := r.db.Model(&Pokemon{}).Preload("Moves").Find(&pokemons) + if result.Error != nil { + return nil, fmt.Errorf("Unable to retrive pokemons: %w", result.Error) + } + + return pokemons, nil +} diff --git a/pokemon/seeder.go b/pokemon/seeder.go new file mode 100644 index 0000000..c3b5729 --- /dev/null +++ b/pokemon/seeder.go @@ -0,0 +1,34 @@ +package pokemon + +import "gorm.io/gorm" + +func Seed(db *gorm.DB) { + pokemons := []Pokemon{ + { + Id: 0, + Name: "Jaak", + Height: 0, + Weight: 0, + Moves: []Move{ + { + Name: "Boojakasja", + Url: "http://sjakka.be", + }, + { + Name: "Uppercut", + Url: "https://vlampatat.co", + }, + }, + }, + } + + db.AutoMigrate(&Pokemon{}) + db.AutoMigrate(&Move{}) + + for _, pokemon := range pokemons { + res := db.Create(&pokemon) + if res.Error != nil { + panic(res.Error) + } + } +} diff --git a/rest/restutils.go b/rest/restutils.go new file mode 100644 index 0000000..075fd62 --- /dev/null +++ b/rest/restutils.go @@ -0,0 +1,29 @@ +package rest + +import ( + "encoding/json" + "net/http" +) + +func Json(w http.ResponseWriter, data any) { + bytes, err := json.MarshalIndent(data, "", " ") + if err != nil { + http.Error(w, "Oops, something went wrong", http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) + w.Write(bytes) +} + +func BadRequest(w http.ResponseWriter) { + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) +} + +func TooManyRequests(w http.ResponseWriter) { + http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests) +} + +func Unauthorized(w http.ResponseWriter) { + http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) +} diff --git a/server.go b/server.go new file mode 100644 index 0000000..05eeb30 --- /dev/null +++ b/server.go @@ -0,0 +1,25 @@ +package main + +import ( + "gorm.io/gorm" + "net/http" + "pokedex/pokemon" + "pokedex/rest" +) + +func magda(db *gorm.DB, fn func(db *gorm.DB, w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + auth := r.Header.Get("authorization") + if auth != "Jefklak is cool" { + rest.Unauthorized(w) + return + } + + fn(db, w, r) + } +} + +func routes(db *gorm.DB) { + http.HandleFunc("GET /pokemon/", magda(db, pokemon.HandleFindAll)) + http.HandleFunc("GET /pokemon/{name}", magda(db, pokemon.HandleFindSingle)) +}