Added Functionality To PowerItem Endpoint

TODO: Change "addMultiple" to not require a map. That only exists so I could import easily from my old project.
This commit is contained in:
Annika Merris 2024-01-28 13:57:25 -05:00
parent d52d9968ba
commit 1420b14565
7 changed files with 373 additions and 95 deletions

View file

@ -1,73 +1,180 @@
package Controllers
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strconv"
"isl-api/sql/powerItem"
"isl-api/Services"
"isl-api/Types"
"github.com/jackc/pgx/v4"
"github.com/julienschmidt/httprouter"
)
func SetPowerItemEndpoints(mux *http.ServeMux, prefix string) {
mux.HandleFunc(fmt.Sprintf("/%s", prefix), PowerItemFunc)
mux.HandleFunc(fmt.Sprintf("/%s/all", prefix), PowerItemAll)
type PowerItemController struct {
powerItemService *Services.PowerItemService
}
func PowerItemFunc(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
fmt.Fprintf(w, "Hello")
// handle a GET
case http.MethodPost:
var newItem powerItem.AddNewItemWithIDParams
err := json.NewDecoder(r.Body).Decode(&newItem)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
fmt.Fprintf(w, "%+v", newItem)
// handle a POST
case http.MethodOptions:
w.Header().Set("Allow", "GET, POST, OPTIONS")
w.WriteHeader(http.StatusNoContent)
default:
w.Header().Set("Allow", "GET, POST, OPTIONS")
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
func NewPowerItemController(router *httprouter.Router, powerItemService *Services.PowerItemService) *PowerItemController {
controller := &PowerItemController{
powerItemService: powerItemService,
}
controller.setPowerItemEndpoints(router, "/powerItem")
return controller
}
func (w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
ctx := context.TODO()
conn, err := pgx.Connect(ctx, "postgres://isl:development@localhost:5432")
if err != nil {
panic(err)
}
q := powerItem.NewQuerier(conn)
rows, err := q.GetAllItems(ctx)
if err != nil {
panic(err)
}
data, err := json.Marshal(rows)
if err != nil {
panic(err)
}
w.Header().Add("Content-Type", "application/json")
fmt.Fprintf(w, string(data))
func (p *PowerItemController) setPowerItemEndpoints(router *httprouter.Router, prefix string) {
router.HandlerFunc("GET", fmt.Sprintf("%v", prefix), p.getAll)
router.HandlerFunc("GET", fmt.Sprintf("%v/asMap", prefix), p.getAllAsMap)
router.HandlerFunc("GET", fmt.Sprintf("%v/byType/:type", prefix), p.getAllByType)
router.HandlerFunc("GET", fmt.Sprintf("%v/byType/:type/asMap", prefix), p.getAllByTypeAsMap)
router.HandlerFunc("POST", fmt.Sprintf("%v", prefix), p.add)
router.HandlerFunc("POST", fmt.Sprintf("%v/multiple", prefix), p.addMultiple)
}
case http.MethodOptions:
w.Header().Set("Allow", "GET, OPTIONS")
w.WriteHeader(http.StatusNoContent)
default:
w.Header().Set("Allow", "GET, OPTIONS")
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
func (p *PowerItemController) add(w http.ResponseWriter, r *http.Request) {
var newItem Types.PowerItem
err := json.NewDecoder(r.Body).Decode(&newItem)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
result, err := p.powerItemService.Add(newItem)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
returnValue, err := json.Marshal(result)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Add("Content-Type", "application/json")
fmt.Fprint(w, string(returnValue))
}
func (p *PowerItemController) addMultiple(w http.ResponseWriter, r *http.Request) {
var itemType int32 = 3
var newItems map[string]Types.PowerItem
err := json.NewDecoder(r.Body).Decode(&newItems)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
added, errors := p.powerItemService.AddMultipile(newItems, itemType)
if len(errors) != 0 {
http.Error(w, "Could not add item", http.StatusInternalServerError)
for _, err := range errors {
fmt.Println(err.Error())
}
return
}
returnValue, err := json.Marshal(added)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Add("Content-Type", "application/json")
fmt.Fprint(w, string(returnValue))
}
func (p *PowerItemController) getAll(w http.ResponseWriter, r *http.Request) {
result, err := p.powerItemService.GetAll()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data, err := json.Marshal(result)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Add("Content-Type", "application/json")
fmt.Fprint(w, string(data))
}
func (p *PowerItemController) getAllAsMap(w http.ResponseWriter, r *http.Request) {
items, err := p.powerItemService.GetAll()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
resultMap := make(map[string]Types.PowerItem)
for _, curItem := range items {
uuid := fmt.Sprintf("%x-%x-%x-%x-%x", curItem.ID.Bytes[0:4], curItem.ID.Bytes[4:6], curItem.ID.Bytes[6:8], curItem.ID.Bytes[8:10], curItem.ID.Bytes[10:16])
resultMap[uuid] = curItem
}
data, err := json.Marshal(resultMap)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Add("Content-Type", "application/json")
fmt.Fprint(w, string(data))
}
func (p *PowerItemController) getAllByType(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context())
typeCode, err := strconv.Atoi(params.ByName("type"))
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
result, err := p.powerItemService.GetAllByType(typeCode)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data, err := json.Marshal(result)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Add("Content-Type", "application/json")
fmt.Fprint(w, string(data))
}
func (p *PowerItemController) getAllByTypeAsMap(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context())
typeCode, err := strconv.Atoi(params.ByName("type"))
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
items, err := p.powerItemService.GetAllByType(typeCode)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
resultMap := make(map[string]Types.PowerItem)
for _, curItem := range items {
uuid := fmt.Sprintf("%x-%x-%x-%x-%x", curItem.ID.Bytes[0:4], curItem.ID.Bytes[4:6], curItem.ID.Bytes[6:8], curItem.ID.Bytes[8:10], curItem.ID.Bytes[10:16])
resultMap[uuid] = curItem
}
data, err := json.Marshal(resultMap)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Add("Content-Type", "application/json")
fmt.Fprint(w, string(data))
}

View file

@ -1,3 +1,93 @@
package Services
func GetAll()
import (
"context"
"isl-api/Types"
"isl-api/sql/powerItem"
"github.com/jackc/pgtype"
)
type PowerItemService struct {
querier *powerItem.DBQuerier
context context.Context
}
func NewPowerItemService(querier *powerItem.DBQuerier) *PowerItemService {
return &PowerItemService{
querier: querier,
context: context.Background(),
}
}
func (p *PowerItemService) GetAll() ([]Types.PowerItem, error) {
rows, err := p.querier.GetAllItems(p.context)
if err != nil {
return []Types.PowerItem{}, err
}
var powerItems []Types.PowerItem
for _, sqlItem := range rows {
powerItems = append(powerItems, Types.FromGetAllItemsRow(sqlItem))
}
return powerItems, nil
}
func (p *PowerItemService) GetAllByType(itemType int) ([]Types.PowerItem, error) {
rows, err := p.querier.GetAllByType(p.context, int32(itemType))
if err != nil {
return []Types.PowerItem{}, err
}
var powerItems []Types.PowerItem
for _, sqlItem := range rows {
powerItems = append(powerItems, Types.FromGetAllItemsByTypeRow(sqlItem))
}
return powerItems, nil
}
func (p *PowerItemService) Add(newItem Types.PowerItem) (Types.PowerItem, error) {
sqlItem := powerItem.AddNewItemWithIDParams{
ID: newItem.ID,
ItemType: newItem.ItemType,
IconUrl: newItem.IconURL,
ItemName: newItem.ItemName,
MinItemPower: newItem.MinItemPower,
MaxItemPower: newItem.MaxItemPower,
Rarity: newItem.Rarity,
Origin: newItem.Origin,
Tooltip: newItem.Tooltip,
IsEventItem: newItem.IsEventItem,
}
row, err := p.querier.AddNewItemWithID(p.context, powerItem.AddNewItemWithIDParams(sqlItem))
if err != nil {
return Types.PowerItem{}, err
}
return Types.FromAddNewItemWithIDParams(row), nil
}
func (p *PowerItemService) AddMultipile(newItems map[string]Types.PowerItem, itemType int32) ([]Types.PowerItem, []error) {
var errors []error
var addedItems []Types.PowerItem
for key, value := range newItems {
id := pgtype.UUID{}
id.Set(key)
newItem := powerItem.AddNewItemWithIDParams{
ID: id,
ItemType: itemType,
IconUrl: value.IconURL,
ItemName: value.ItemName,
MinItemPower: value.MinItemPower,
MaxItemPower: value.MaxItemPower,
Rarity: value.Rarity,
Origin: value.Origin,
Tooltip: value.Tooltip,
IsEventItem: value.IsEventItem,
}
row, err := p.querier.AddNewItemWithID(p.context, powerItem.AddNewItemWithIDParams(newItem))
if err != nil {
errors = append(errors, err)
continue
}
addedItems = append(addedItems, Types.FromAddNewItemWithIDParams(row))
}
return addedItems, errors
}

View file

@ -1,19 +1,65 @@
package Types
import (
"github.com/gofrs/uuid"
uuid "github.com/jackc/pgtype/ext/gofrs-uuid"
"isl-api/sql/powerItem"
"github.com/jackc/pgtype"
)
type PowerItem struct {
ID uuid.UUID `json:"id"`
ItemType int32 `json:"itemType"`
IconUrl string `json:"iconUrl"`
ItemName string `json:"itemName"`
MinItemPower int32 `json:"minItemPower"`
MaxItemPower int32 `json:"maxItemPower"`
Rarity int32 `json:"rarity"`
Origin string `json:"origin"`
Tooltip string `json:"tooltip"`
IsEventItem bool `json:"isEventItem"`
ID pgtype.UUID `json:"id"`
ItemType int32 `json:"itemType"`
IconURL string `json:"iconURL"`
ItemName string `json:"itemName"`
MinItemPower int32 `json:"minItemPower"`
MaxItemPower int32 `json:"maxItemPower"`
Rarity int32 `json:"rarity"`
Origin string `json:"origin"`
Tooltip string `json:"tooltip"`
IsEventItem bool `json:"isEventItem"`
}
func FromGetAllItemsRow(sqlItem powerItem.GetAllItemsRow) PowerItem {
return PowerItem{
ID: sqlItem.ID,
ItemType: *sqlItem.Itemtype,
IconURL: *sqlItem.Iconurl,
ItemName: *sqlItem.Itemname,
MinItemPower: *sqlItem.Minitempower,
MaxItemPower: *sqlItem.Maxitempower,
Rarity: *sqlItem.Rarity,
Origin: *sqlItem.Origin,
Tooltip: *sqlItem.Tooltip,
IsEventItem: *sqlItem.Iseventitem,
}
}
func FromGetAllItemsByTypeRow(sqlItem powerItem.GetAllByTypeRow) PowerItem {
return PowerItem{
ID: sqlItem.ID,
ItemType: sqlItem.Itemtype,
IconURL: sqlItem.Iconurl,
ItemName: sqlItem.Itemname,
MinItemPower: sqlItem.Minitempower,
MaxItemPower: sqlItem.Maxitempower,
Rarity: sqlItem.Rarity,
Origin: sqlItem.Origin,
Tooltip: *sqlItem.Tooltip,
IsEventItem: *sqlItem.Iseventitem,
}
}
func FromAddNewItemWithIDParams(sqlItem powerItem.AddNewItemWithIDRow) PowerItem {
return PowerItem{
ID: sqlItem.ID,
ItemType: sqlItem.Itemtype,
IconURL: sqlItem.Iconurl,
ItemName: sqlItem.Itemname,
MinItemPower: sqlItem.Minitempower,
MaxItemPower: sqlItem.Maxitempower,
Rarity: sqlItem.Rarity,
Origin: sqlItem.Origin,
Tooltip: *sqlItem.Tooltip,
IsEventItem: *sqlItem.Iseventitem,
}
}

2
go.mod
View file

@ -3,6 +3,7 @@ module isl-api
go 1.21.6
require (
github.com/google/uuid v1.6.0
github.com/jackc/pgconn v1.14.1
github.com/jackc/pgtype v1.14.1
github.com/jackc/pgx/v4 v4.18.1
@ -14,6 +15,7 @@ require (
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.2 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/julienschmidt/httprouter v1.3.0 // indirect
golang.org/x/crypto v0.6.0 // indirect
golang.org/x/text v0.7.0 // indirect
)

4
go.sum
View file

@ -14,6 +14,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
@ -64,6 +66,8 @@ github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0f
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=

65
main.go
View file

@ -1,41 +1,52 @@
package main
import (
"context"
"fmt"
"isl-api/Controllers"
"isl-api/Services"
"isl-api/sql/powerItem"
"log"
"net/http"
"github.com/jackc/pgx/v4"
"github.com/julienschmidt/httprouter"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", index)
Controllers.SetPowerItemEndpoints(mux, "powerItem")
deps := dependencies{}
deps.initializeDependencies()
err := http.ListenAndServe(":3000", mux)
deps.router.HandlerFunc("GET", "/", index)
err := http.ListenAndServe(":3000", deps.router)
log.Fatal(err)
}
func index(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
// Common code for all requests can go here...
switch r.Method {
case http.MethodGet:
// Handle the GET request...
case http.MethodPost:
// Handle the POST request...
case http.MethodOptions:
w.Header().Set("Allow", "GET, POST, OPTIONS")
w.WriteHeader(http.StatusNoContent)
default:
w.Header().Set("Allow", "GET, POST, OPTIONS")
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
type dependencies struct {
router *httprouter.Router
postgresConnection *pgx.Conn
context context.Context
powerItemQuerier *powerItem.DBQuerier
powerItemService *Services.PowerItemService
powerItemController *Controllers.PowerItemController
}
func (d *dependencies) initializeDependencies() error {
var err error
d.router = httprouter.New()
d.context = context.Background()
d.postgresConnection, err = pgx.Connect(d.context, "postgres://isl:development@localhost:5432/isl")
if err != nil {
return err
}
d.powerItemQuerier = powerItem.NewQuerier(d.postgresConnection)
d.powerItemService = Services.NewPowerItemService(d.powerItemQuerier)
d.powerItemController = Controllers.NewPowerItemController(d.router, d.powerItemService)
return nil
}
func index(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Index")
}

View file

@ -14,6 +14,24 @@ SELECT
FROM
powerItem;
-- GetAllByType finds all items of a specific type.
-- name: GetAllByType :many
SELECT
id,
itemType,
iconURL,
itemName,
minItemPower,
maxItemPower,
rarity,
origin,
tooltip,
isEventItem
FROM
powerItem
WHERE
itemType = pggen.arg('itemType');
-- FindByID finds items by the ID
-- name: FindByID :one
SELECT