Skip to content

Commit 4df2e0e

Browse files
committed
characters list endpoint
1 parent 2d8901c commit 4df2e0e

9 files changed

Lines changed: 169 additions & 19 deletions

File tree

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
meta {
2-
name: character
2+
name: characters
33
type: http
44
seq: 2
55
}
66

77
get {
88
url: {{BASE_URL}}/characters
9-
body: none
9+
body: json
1010
auth: inherit
1111
}
12+
13+
body:json {
14+
{
15+
"limit": 10,
16+
"skip": 0
17+
}
18+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package charactersapi
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/UltimateForm/gopen-api/internal/core"
7+
"github.com/UltimateForm/gopen-api/internal/repository/charrep"
8+
)
9+
10+
type Offset struct {
11+
Limit int `json:"limit"`
12+
Skip int `json:"skip"`
13+
}
14+
15+
func (src Offset) Validate() error {
16+
if src.Limit <= 0 {
17+
return core.BadRequest("limit must be a positive higher than 0 integer")
18+
}
19+
if src.Skip < 0 {
20+
return core.BadRequest("skip be a positive integer")
21+
}
22+
return nil
23+
}
24+
25+
func HandleGetCharacters(res http.ResponseWriter, req *http.Request) {
26+
var offset Offset
27+
// TODO: use query parameters silly
28+
err := core.ParseBody(req, &offset)
29+
if err != nil {
30+
core.RespondError(res, req, err)
31+
return
32+
}
33+
characters, err := charrep.ReadCharacters(req.Context(), charrep.Offset{
34+
Limit: offset.Limit,
35+
Skip: offset.Skip,
36+
})
37+
if err != nil {
38+
core.RespondError(res, req, err)
39+
return
40+
}
41+
charactersMapped := make([]Character, len(characters))
42+
// NOTE: eh should i be mapping at all, golang type integrity does mean i dont need to worry so much about leakage
43+
for i, char := range characters {
44+
charactersMapped[i] = Character{
45+
Id: char.Id,
46+
Name: char.Name,
47+
Description: char.Description,
48+
Debut: char.Debut,
49+
}
50+
}
51+
core.RespondOk(res, CharacterList{Offset: offset, Characters: charactersMapped})
52+
}

cmd/api/charactersapi/types.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package charactersapi
2+
3+
type Character struct {
4+
Id string `json:"id"`
5+
Name string `json:"name"`
6+
Description string `json:"description"`
7+
Debut int `json:"debut"`
8+
}
9+
10+
type CharacterList struct {
11+
Offset
12+
Characters []Character `json:"characters"`
13+
}

cmd/api/get_one_character.go

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package api
1+
package loginapi
22

33
import (
44
"net/http"
@@ -16,14 +16,14 @@ type LoginResponse struct {
1616
Token string `json:"token"`
1717
}
1818

19-
func (src *LoginRequest) Validate() error {
19+
func (src LoginRequest) Validate() error {
2020
if src.Email == "" || src.Password == "" {
2121
return core.BadRequest("missing either email or password field")
2222
}
2323
return nil
2424
}
2525

26-
func authHandler(res http.ResponseWriter, req *http.Request) {
26+
func HandlePostLogin(res http.ResponseWriter, req *http.Request) {
2727
var reqData LoginRequest
2828
bodyErr := core.ParseBody(req, &reqData)
2929
if bodyErr != nil {

cmd/api/main.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"log"
55
"net/http"
66

7+
"github.com/UltimateForm/gopen-api/cmd/api/charactersapi"
8+
"github.com/UltimateForm/gopen-api/cmd/api/loginapi"
79
"github.com/UltimateForm/gopen-api/internal/core"
810
"github.com/go-chi/chi/v5"
911
"github.com/go-chi/chi/v5/middleware"
@@ -13,10 +15,10 @@ func Start() http.Handler {
1315
router := chi.NewRouter()
1416
router.Use(middleware.Logger)
1517
router.Use(core.ErrorHandlingMiddleware)
16-
router.Post("/login", authHandler)
18+
router.Post("/login", loginapi.HandlePostLogin)
1719
router.Route("/characters", func(r chi.Router) {
1820
r.Use(core.AuthMiddleware)
19-
r.Get("/", characterHandler)
21+
r.Get("/", charactersapi.HandleGetCharacters)
2022
})
2123
log.Println("ROUTER CREATED")
2224
return router
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package charrep
2+
3+
import (
4+
"context"
5+
"log"
6+
7+
"github.com/UltimateForm/gopen-api/internal/repository"
8+
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
9+
)
10+
11+
func txReadCharacters(ctx context.Context, offset Offset) neo4j.ManagedTransactionWorkT[[]Character] {
12+
query := repository.NewQuery(offset,
13+
`
14+
MATCH (character:Character)
15+
RETURN character
16+
ORDER BY character.debut
17+
SKIP $skip
18+
LIMIT $limit
19+
`)
20+
return func(tx neo4j.ManagedTransaction) ([]Character, error) {
21+
res, err := query.Execute(tx, ctx)
22+
if err != nil {
23+
return nil, err
24+
}
25+
characters := make([]Character, offset.Limit)
26+
i := 0
27+
for rec, yieldErr := range res.Records(ctx) {
28+
if yieldErr != nil {
29+
log.Printf("Error while iterating DB records, %v", yieldErr)
30+
i++
31+
continue
32+
}
33+
rowMap, rowMapOk := rec.AsMap()["character"]
34+
rowCharacter, rowCharacterOk := rowMap.(neo4j.Node)
35+
if !rowMapOk || !rowCharacterOk {
36+
log.Printf("Bad row from db %+v\n", rec)
37+
i++
38+
continue
39+
}
40+
propsMap := rowCharacter.GetProperties()
41+
name := propsMap["name"]
42+
id := propsMap["id"]
43+
description := propsMap["description"]
44+
debut := propsMap["debut"]
45+
nameStr := name.(string)
46+
idStr := id.(string)
47+
debutNum := int(debut.(float64))
48+
descrStr := description.(string)
49+
characters[i] = Character{
50+
Name: nameStr,
51+
Id: idStr,
52+
Debut: debutNum,
53+
Description: descrStr,
54+
}
55+
i++
56+
}
57+
return characters, nil
58+
}
59+
}
60+
61+
func ReadCharacters(ctx context.Context, offset Offset) ([]Character, error) {
62+
return repository.ExecuteReadWithDriver(ctx, txReadCharacters(ctx, offset))
63+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package charrep
2+
3+
type Offset struct {
4+
Limit int
5+
Skip int
6+
}
7+
8+
func (src Offset) ToParams() map[string]any {
9+
return map[string]any{
10+
"limit": src.Limit,
11+
"skip": src.Skip,
12+
}
13+
}
14+
15+
type Character struct {
16+
Id string
17+
Name string
18+
Description string
19+
Debut int
20+
}

internal/repository/main.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import (
99
)
1010

1111
func ExecuteReadWithDriver[T any](ctx context.Context, tx neo4j.ManagedTransactionWorkT[T]) (T, error) {
12-
readCtx, cancel := context.WithDeadline(ctx, time.Now().Add(time.Second*10))
13-
defer cancel()
14-
session := db.Driver.NewSession(readCtx, neo4j.SessionConfig{})
15-
defer session.Close(readCtx)
16-
return neo4j.ExecuteRead(readCtx, session, tx)
12+
session := db.Driver.NewSession(ctx, neo4j.SessionConfig{})
13+
defer session.Close(ctx)
14+
return neo4j.ExecuteRead(ctx, session, tx, func(config *neo4j.TransactionConfig) {
15+
config.Timeout = time.Second * 10
16+
})
1717
}
1818

1919
func NewQuery(data IQueryParams, query string) QueryRunner {

0 commit comments

Comments
 (0)