diff --git a/backend/ddl.sql b/backend/ddl.sql index 74962a8..616da60 100644 --- a/backend/ddl.sql +++ b/backend/ddl.sql @@ -23,5 +23,4 @@ create table note ( content text, timestamp string, player integer references player(id) -); -create unique index idx_note_content on note(timestamp, player); \ No newline at end of file +); \ No newline at end of file diff --git a/backend/main.go b/backend/main.go index bd786e2..618a15e 100644 --- a/backend/main.go +++ b/backend/main.go @@ -6,6 +6,8 @@ import ( "io" "log" "os" + + _ "embed" ) var Error *log.Logger @@ -35,6 +37,15 @@ var ps PlayerService var ns NoteService var as AssociationService +//go:embed selectPlayer.sql +var selectPlayer string + +//go:embed selectAssociation.sql +var selectAssociation string + +//go:embed selectNotes.sql +var selectNotes string + func main() { db = DB{ path: "data/db.db", @@ -54,6 +65,8 @@ func main() { app := fiber.New() app.Post("/note/new", CreateNote) + app.Get("/player/:name", GetPlayer) + app.Get("/player", GetPlayers) log.Fatal(app.Listen(":3000")) } @@ -89,3 +102,37 @@ func CreateNote(c fiber.Ctx) error { res.Message = "OK" return c.Status(200).JSON(res) } + +func GetPlayer(c fiber.Ctx) error { + name := c.Params("name") + log.Printf("Getting player %s", name) + player, err := ps.GetAllPlayerInfo(name, 10) + if err != nil { + Error.Printf("Failed getting player: %v", err) + return c.Status(500).JSON(Response{ + Success: false, + Message: err.Error(), + }) + } + + return c.Status(200).JSON(Response{ + Success: true, + Data: player, + }) +} + +func GetPlayers(c fiber.Ctx) error { + players, err := ps.Query(PlayerServiceQuery{}) + if err != nil { + Error.Printf("Failed getting players: %v", err) + return c.Status(500).JSON(Response{ + Success: false, + Message: err.Error(), + }) + } + + return c.Status(200).JSON(Response{ + Success: true, + Data: players, + }) +} diff --git a/backend/playerService.go b/backend/playerService.go index 8051a5e..02913a0 100644 --- a/backend/playerService.go +++ b/backend/playerService.go @@ -81,3 +81,69 @@ func (ps *PlayerService) GetOrCreate(name string, guild Guild) (Player, error) { } return player, nil } + +type ( + FullPlayer struct { + ID int64 `json:"id"` + Name string `json:"name"` + Guild struct { + ID int64 `json:"id"` + Name string `json:"name"` + } `json:"guild"` + Associations []FullPlayerAssociation `json:"associations"` + Notes []FullPlayerNote `json:"notes"` + } + + FullPlayerAssociation struct { + ID int64 `json:"id"` + Name string `json:"name"` + Note string `json:"note"` + } + + FullPlayerNote struct { + ID int64 `json:"id"` + Content string `json:"content"` + Timestamp string `json:"timestamp"` + } +) + +func (ps *PlayerService) GetAllPlayerInfo(name string, nnotes int) (FullPlayer, error) { + res := FullPlayer{} + + err := ps.db.readConn.QueryRow(selectPlayer, name).Scan(&res.ID, &res.Name, &res.Guild.ID, &res.Guild.Name) + if err != nil { + return res, fmt.Errorf("failed getting player info: %v", err) + } + + rows, err := ps.db.readConn.Query(selectAssociation, res.ID) + if err != nil { + return res, fmt.Errorf("failed getting player associations: %v", err) + } + + for rows.Next() { + assoc := FullPlayerAssociation{} + err := rows.Scan(&assoc.ID, &assoc.Name, &assoc.Note) + if err != nil { + return res, fmt.Errorf("failed scanning association: %v", err) + } + res.Associations = append(res.Associations, assoc) + } + rows.Close() + + rows, err = ps.db.readConn.Query(selectNotes, res.ID, nnotes) + if err != nil { + return res, fmt.Errorf("failed getting player notes: %v", err) + } + + for rows.Next() { + note := FullPlayerNote{} + err := rows.Scan(¬e.ID, ¬e.Content, ¬e.Timestamp) + if err != nil { + return res, fmt.Errorf("failed scanning note: %v", err) + } + res.Notes = append(res.Notes, note) + } + rows.Close() + + return res, nil +} diff --git a/backend/selectAssociation.sql b/backend/selectAssociation.sql new file mode 100644 index 0000000..39a5bf1 --- /dev/null +++ b/backend/selectAssociation.sql @@ -0,0 +1,28 @@ + -- 3. Get all associations (N rows) +with + AllAssociations as ( + select + lhs as PlayerID, + rhs as AssociateID, + note + from + association + where + lhs = $1 + union + select + rhs as PlayerID, + lhs as AssociateID, + note + from + association + where + rhs = $1 + ) +select + p.id as AssociateID, + p.name as AssociateName, + coalesce(a.note, '') as AssociationNote +from + AllAssociations a + join player p on a.AssociateID = p.id; \ No newline at end of file diff --git a/backend/selectNotes.sql b/backend/selectNotes.sql new file mode 100644 index 0000000..a4b7cf0 --- /dev/null +++ b/backend/selectNotes.sql @@ -0,0 +1,22 @@ + -- 2. Get N most recent notes (10 rows) +select + id as NoteID, + content as NoteContent, + timestamp as NoteTimestamp +from + ( + select + n.*, + ROW_NUMBER() OVER ( + order by + timestamp desc + ) as rn + from + note n + where + n.player = $1 + ) ranked +where + rn <= $2 +order by + timestamp desc; \ No newline at end of file diff --git a/backend/selectPlayer.sql b/backend/selectPlayer.sql new file mode 100644 index 0000000..a0ae005 --- /dev/null +++ b/backend/selectPlayer.sql @@ -0,0 +1,11 @@ + -- 1. Get player and guild info (1 row) +select + p.id as PlayerID, + p.name as PlayerName, + g.id as GuildID, + g.name as GuildName +from + player p + join guild g on p.guild = g.id +where + p.name = $1; \ No newline at end of file diff --git a/backend/types.go b/backend/types.go index b9b540b..e0f2ba2 100644 --- a/backend/types.go +++ b/backend/types.go @@ -8,9 +8,9 @@ type ( Name string `json:"name"` } Player struct { - ID int64 - Name string `json:"name"` - Guild Guild `json:"guild"` + ID int64 + Name string `json:"name"` + Guild Guild `json:"guild,omitempty"` } Association struct { ID int64