Skip to content

Commit 86a4da4

Browse files
committed
fix query string for characters
1 parent c280f15 commit 86a4da4

6 files changed

Lines changed: 67 additions & 41 deletions

File tree

api/bruno/characters.bru

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@ meta {
66

77
get {
88
url: {{BASE_URL}}/characters
9-
body: json
9+
body: none
1010
auth: inherit
1111
}
1212

13+
params:query {
14+
~limit: 10
15+
~skip: 0
16+
}
17+
1318
body:json {
1419
{
1520
"limit": 10,
Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,31 @@
11
package charactersapi
22

33
import (
4+
"errors"
45
"net/http"
56

67
"github.com/UltimateForm/gopen-api/internal/core"
78
"github.com/UltimateForm/gopen-api/internal/repository/charrep"
89
)
910

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-
2511
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)
12+
query := req.URL.Query()
13+
limit, limitErr := core.GetIntQuery(query, "limit", 10)
14+
skip, skipErr := core.GetIntQuery(query, "skip", 0)
15+
if queryErr := errors.Join(limitErr, skipErr); queryErr != nil {
16+
core.RespondError(res, req, core.BadRequest(queryErr.Error()))
3117
return
3218
}
3319
characters, err := charrep.ReadCharacters(req.Context(), charrep.Offset{
34-
Limit: offset.Limit,
35-
Skip: offset.Skip,
20+
Limit: limit,
21+
Skip: skip,
3622
})
3723
if err != nil {
3824
core.RespondError(res, req, err)
3925
return
4026
}
4127
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
28+
// NOTE: eh should i be mapping at all? golang type integrity does mean i dont need to worry so much about leakage
4329
for i, char := range characters {
4430
charactersMapped[i] = Character{
4531
Id: char.Id,
@@ -48,5 +34,12 @@ func HandleGetCharacters(res http.ResponseWriter, req *http.Request) {
4834
Debut: char.Debut,
4935
}
5036
}
51-
core.RespondOk(res, CharacterList{Offset: offset, Characters: charactersMapped})
37+
core.RespondOk(
38+
res, CharacterList{
39+
Offset: Offset{
40+
Limit: limit,
41+
Skip: skip,
42+
},
43+
Characters: charactersMapped,
44+
})
5245
}

cmd/api/charactersapi/types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ type Character struct {
77
Debut int `json:"debut"`
88
}
99

10+
type Offset struct {
11+
Limit int `json:"limit"`
12+
Skip int `json:"skip"`
13+
}
14+
1015
type CharacterList struct {
1116
Offset
1217
Characters []Character `json:"characters"`

internal/core/body_parse.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ package core
22

33
import (
44
"encoding/json"
5+
"fmt"
56
"net/http"
7+
"net/url"
8+
"strconv"
69
)
710

811
type RequestBody interface {
@@ -20,3 +23,23 @@ func ParseBody(req *http.Request, data any) error {
2023
}
2124
return nil
2225
}
26+
27+
func GetQuery(query url.Values, key string, def string) (string, bool) {
28+
val := query.Get(key)
29+
if val == "" {
30+
return "", false
31+
}
32+
return val, true
33+
}
34+
35+
func GetIntQuery(query url.Values, key string, def int) (int, error) {
36+
stringVal, exists := GetQuery(query, key, "")
37+
if !exists {
38+
return def, nil
39+
}
40+
numericVal, convErr := strconv.Atoi(stringVal)
41+
if convErr != nil {
42+
return 0, BadRequest(fmt.Sprintf("expected '%v' numeric query parameter but instead found string '%v'", key, stringVal))
43+
}
44+
return numericVal, nil
45+
}

internal/core/body_parse_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,11 @@ func TestParseBodyWithValidationFail(t *testing.T) {
5555
if parseErr == nil {
5656
t.Fatalf("Expected parseErr to not be nil but instead it is %v", parseErr)
5757
}
58-
badRequestErr, isErrorResponse := parseErr.(*ErrorResponse)
58+
badRequestErr, isErrorResponse := parseErr.(ErrorResponse)
5959
if !isErrorResponse {
6060
t.Fatalf("Expected error (%v) to be of type error response but it is not", parseErr)
6161
}
62-
errorRes := &ErrorResponse{
62+
errorRes := ErrorResponse{
6363
Code: 400,
6464
Message: "text is required",
6565
}

internal/core/errors.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,28 @@ type ErrorResponse struct {
1212
Message string `json:"message"`
1313
}
1414

15-
func (e *ErrorResponse) Error() string {
15+
func (e ErrorResponse) Error() string {
1616
return e.Message
1717
}
1818

19-
func BadRequest(msg string) *ErrorResponse {
20-
return &ErrorResponse{Code: 400, Message: msg}
19+
func BadRequest(msg string) ErrorResponse {
20+
return ErrorResponse{Code: 400, Message: msg}
2121
}
2222

23-
func InternalServerError() *ErrorResponse {
24-
return &ErrorResponse{Code: 500, Message: "Internal Server Error"}
23+
func InternalServerError() ErrorResponse {
24+
return ErrorResponse{Code: 500, Message: "Internal Server Error"}
2525
}
2626

27-
func NotFound() *ErrorResponse {
28-
return &ErrorResponse{Code: 404, Message: "Resource Not Found"}
27+
func NotFound() ErrorResponse {
28+
return ErrorResponse{Code: 404, Message: "Resource Not Found"}
2929
}
3030

31-
func Unauthorized() *ErrorResponse {
32-
return &ErrorResponse{Code: 401, Message: "Unauthorized"}
31+
func Unauthorized() ErrorResponse {
32+
return ErrorResponse{Code: 401, Message: "Unauthorized"}
3333
}
3434

35-
func Forbidden() *ErrorResponse {
36-
return &ErrorResponse{Code: 403, Message: "Forbidden"}
35+
func Forbidden() ErrorResponse {
36+
return ErrorResponse{Code: 403, Message: "Forbidden"}
3737
}
3838

3939
func RespondKnownError(res http.ResponseWriter, err ErrorResponse) {
@@ -44,11 +44,11 @@ func RespondKnownError(res http.ResponseWriter, err ErrorResponse) {
4444

4545
func RespondError(res http.ResponseWriter, req *http.Request, err error) {
4646
switch bindErr := err.(type) {
47-
case *ErrorResponse:
48-
RespondKnownError(res, *bindErr)
47+
case ErrorResponse:
48+
RespondKnownError(res, bindErr)
4949
default:
5050
log.Printf("Non HTTP error occured: %v", err)
51-
RespondKnownError(res, *InternalServerError())
51+
RespondKnownError(res, InternalServerError())
5252

5353
}
5454
}

0 commit comments

Comments
 (0)