Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,12 +311,15 @@ http.ListenAndServe(":8080", nil)
$ curl localhost:8080/pets/update -d '{"name":"Lola"}'
{"name":"Lola 3000"}
```
If you have empty request or response body or you want to ignore them, use `fetch.Empty`:
#### Ignoring request or response
If you have an empty request or response body or you want to ignore them, use `fetch.Empty`:
```go
http.HandleFunc("/default-pet", fetch.ToHandlerFunc(func(_ fetch.Empty) (Pet, error) {
return Pet{Name: "Teddy"}, nil
}))
```
Alternatively, you can use `fetch.ToHandlerFuncEmptyIn` and `fetch.ToHandlerFuncEmptyOut` functions.
#### Wrappers
If you need to access http request attributes wrap the input with `fetch.Request`:
```go
type Pet struct {
Expand All @@ -330,7 +333,7 @@ http.HandleFunc("/pets", fetch.ToHandlerFunc(func(req fetch.Request[Pet]) (*fetc
return nil, nil
}))
```
If you have go1.23 and above you can access the wildcards as well.
If you have `go1.23` and above you can access the wildcards as well.
```go
http.HandleFunc("GET /pets/{id}", fetch.ToHandlerFunc(func(in fetch.Request[fetch.Empty]) (*fetch.Empty, error) {
fmt.Println("id from url:", in.PathValues["id"])
Expand Down
19 changes: 19 additions & 0 deletions to_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ func (cfg HandlerConfig) respondError(w http.ResponseWriter, err error) {
// In type as a request body and Out type as a response body.
type ApplyFunc[In any, Out any] func(in In) (Out, error)

// ConsumeFunc serves as ApplyFunc ignoring HTTP response
type ConsumeFunc[In any] func(in In) error

// SupplyFunc serves as ApplyFunc ignoring HTTP request
type SupplyFunc[Out any] func() (Out, error)

/*
ToHandlerFunc converts ApplyFunc into http.HandlerFunc,
which can be used later in http.ServeMux#HandleFunc.
Expand Down Expand Up @@ -110,6 +116,19 @@ func ToHandlerFunc[In any, Out any](apply ApplyFunc[In, Out]) http.HandlerFunc {
}
}

func ToHandlerFuncEmptyOut[In any](consume ConsumeFunc[In]) http.HandlerFunc {
return ToHandlerFunc[In, Empty](func(in In) (Empty, error) {
err := consume(in)
return Empty{}, err
})
}

func ToHandlerFuncEmptyIn[Out any](supply SupplyFunc[Out]) http.HandlerFunc {
return ToHandlerFunc[Empty, Out](func(_ Empty) (Out, error) {
return supply()
})
}

func readAndParseBody(r *http.Request, in any) error {
reqBody, err := io.ReadAll(r.Body)
if err != nil {
Expand Down
67 changes: 42 additions & 25 deletions to_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func TestToHandlerFunc_EmptyIn(t *testing.T) {
assert(t, err, nil)
f(mw, r)
assert(t, mw.status, 200)
assert(t, string(mw.body), `{"name":"Lola"}`)
assert(t, mw.body, `{"name":"Lola"}`)
}

func TestToHandlerFunc_EmptyOut(t *testing.T) {
Expand Down Expand Up @@ -61,11 +61,9 @@ func TestToHandlerFunc_J(t *testing.T) {
return M{"status": "ok"}, nil
})
mw := newMockWriter()
mux := http.NewServeMux()
mux.HandleFunc("/j", f)
r, err := http.NewRequest("POST", "/j", bytes.NewBuffer([]byte(`{"name":"Lola"}`)))
assert(t, err, nil)
mux.ServeHTTP(mw, r)
f.ServeHTTP(mw, r)
assert(t, mw.status, 200)
assert(t, string(mw.body), `{"status":"ok"}`)
}
Expand All @@ -78,12 +76,10 @@ func TestToHandlerFunc_Header(t *testing.T) {
return Empty{}, nil
})
mw := newMockWriter()
mux := http.NewServeMux()
mux.HandleFunc("/pets", f)
r, err := http.NewRequest("POST", "/pets", bytes.NewBuffer([]byte(`{}`)))
r.Header.Set("Content", "mycontent")
assert(t, err, nil)
mux.ServeHTTP(mw, r)
f.ServeHTTP(mw, r)
assert(t, mw.status, 200)
}

Expand All @@ -95,11 +91,9 @@ func TestToHandlerFunc_UrlParameter(t *testing.T) {
return Empty{}, nil
})
mw := newMockWriter()
mux := http.NewServeMux()
mux.HandleFunc("/pets", f)
r, err := http.NewRequest("POST", "/pets?name=Lola", bytes.NewBuffer([]byte(``)))
assert(t, err, nil)
mux.ServeHTTP(mw, r)
f.ServeHTTP(mw, r)
assert(t, mw.status, 200)
}

Expand All @@ -112,11 +106,9 @@ func TestToHandlerFunc_ParseErrors(t *testing.T) {
return Empty{}, nil
})
mw := newMockWriter()
mux := http.NewServeMux()
mux.HandleFunc("/pets", f)
r, err := http.NewRequest("POST", "/pets", bytes.NewBuffer([]byte(``)))
assert(t, err, nil)
mux.ServeHTTP(mw, r)
f.ServeHTTP(mw, r)
if mw.status != 400 || mw.body != `{"error":"parse request body: body is empty"}` {
t.Errorf("Wrong writer: %+v", mw)
}
Expand All @@ -129,11 +121,9 @@ func TestToHandlerFunc_Context(t *testing.T) {
return Empty{}, nil
})
mw := newMockWriter()
mux := http.NewServeMux()
mux.HandleFunc("/pets", f)
r, err := http.NewRequest("POST", "/pets", bytes.NewBuffer([]byte(`{"name":"Lola"}`)))
assert(t, err, nil)
mux.ServeHTTP(mw, r)
f.ServeHTTP(mw, r)
assert(t, mw.status, 200)
}

Expand All @@ -142,11 +132,9 @@ func TestToHandlerFunc_ErrorStatus(t *testing.T) {
return nil, &Error{Status: 403}
})
mw := newMockWriter()
mux := http.NewServeMux()
mux.HandleFunc("/pets", f)
r, err := http.NewRequest("POST", "/pets", bytes.NewBuffer([]byte(`{"name":"Lola"}`)))
assert(t, err, nil)
mux.ServeHTTP(mw, r)
f.ServeHTTP(mw, r)
assert(t, mw.status, 403)
}

Expand All @@ -158,11 +146,9 @@ func TestToHandlerFunc_Response(t *testing.T) {
return Response[*Pet]{Status: 201, Body: &Pet{Name: "Lola"}}, nil
})
mw := newMockWriter()
mux := http.NewServeMux()
mux.HandleFunc("/pets", f)
r, err := http.NewRequest("POST", "/pets", bytes.NewBuffer([]byte(``)))
assert(t, err, nil)
mux.ServeHTTP(mw, r)
f.ServeHTTP(mw, r)
if mw.status != 201 || string(mw.body) != `{"name":"Lola"}` {
t.Errorf("wrong writer: %+v", mw)
}
Expand All @@ -183,11 +169,9 @@ func TestToHandlerFunc_Middleware(t *testing.T) {
return Empty{}, nil
})
mw := newMockWriter()
mux := http.NewServeMux()
mux.HandleFunc("/pets", f)
r, err := http.NewRequest("POST", "/pets", bytes.NewBuffer([]byte(`{}`)))
assert(t, err, nil)
mux.ServeHTTP(mw, r)
f.ServeHTTP(mw, r)
assert(t, mw.status, 422)
}

Expand Down Expand Up @@ -215,3 +199,36 @@ func TestToHandlerFunc_ExtractPathValues_GoLess23(t *testing.T) {
}
}
}

func TestToHandlerFuncEmptyIn(t *testing.T) {
type Pet struct {
Name string
}
f := ToHandlerFuncEmptyIn(func() (Response[*Pet], error) {
return Response[*Pet]{Status: 201, Body: &Pet{Name: "Lola"}}, nil
})
mw := newMockWriter()
f.ServeHTTP(mw, nil)
if mw.status != 201 || string(mw.body) != `{"name":"Lola"}` {
t.Errorf("wrong writer: %+v", mw)
}
}

func TestToHandlerFuncEmptyOut(t *testing.T) {
type Pet struct {
Name string
}
f := ToHandlerFuncEmptyOut(func(in Request[Pet]) error {
if in.Body.Name != "Lola" {
t.Fatalf("expected body %+v", in.Body)
}
return nil
})
mw := newMockWriter()
r, err := http.NewRequest("POST", "/pets", bytes.NewBuffer([]byte(`{"name":"Lola"}`)))
assert(t, err, nil)
f.ServeHTTP(mw, r)
if mw.status != 200 || string(mw.body) != `` {
t.Errorf("wrong writer: %+v", mw)
}
}
Loading