Skip to content

Commit fcaa664

Browse files
Add JSONInputDecoder for array input support
Signed-off-by: Peter Broadhurst <peter.broadhurst@kaleido.io>
1 parent 0d16a02 commit fcaa664

File tree

3 files changed

+55
-2
lines changed

3 files changed

+55
-2
lines changed

pkg/ffapi/apiserver_test.go

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package ffapi
1818

1919
import (
2020
"context"
21+
"encoding/json"
2122
"fmt"
2223
"io"
2324
"net/http"
@@ -112,6 +113,29 @@ var utAPIRoute2 = &Route{
112113
},
113114
}
114115

116+
type testInputStruct struct {
117+
Input1 string `json:"input1,omitempty"`
118+
}
119+
120+
var utAPIRoute3 = &Route{
121+
Name: "utAPIRoute3",
122+
Path: "ut/utresource/{resourceid}/postbatch",
123+
Method: http.MethodPost,
124+
Description: "post an array to check arrays go through ok",
125+
JSONInputDecoder: func(req *http.Request, body io.Reader) (interface{}, error) {
126+
var arrayInput []*testInputStruct
127+
err := json.NewDecoder(body).Decode(&arrayInput)
128+
return arrayInput, err
129+
},
130+
JSONInputValue: func() interface{} { return []*testInputStruct{} },
131+
JSONOutputValue: func() interface{} { return []*testInputStruct{} },
132+
Extensions: &APIServerRouteExt[*utManager]{
133+
JSONHandler: func(a *APIRequest, um *utManager) (output interface{}, err error) {
134+
return a.Input.([]*testInputStruct), nil
135+
},
136+
},
137+
}
138+
115139
func initUTConfig() (config.Section, config.Section, config.Section) {
116140
config.RootConfigReset()
117141
apiConfig := config.RootSection("ut.api")
@@ -129,7 +153,7 @@ func newTestAPIServer(t *testing.T, start bool) (*utManager, *apiServer[*utManag
129153
um := &utManager{t: t}
130154
as := NewAPIServer(ctx, APIServerOptions[*utManager]{
131155
MetricsRegistry: metric.NewPrometheusMetricsRegistry("ut"),
132-
Routes: []*Route{utAPIRoute1, utAPIRoute2},
156+
Routes: []*Route{utAPIRoute1, utAPIRoute2, utAPIRoute3},
133157
EnrichRequest: func(r *APIRequest) (*utManager, error) {
134158
// This could be some dynamic object based on extra processing in the request,
135159
// but the most common case is you just have a "manager" that you inject into each
@@ -176,6 +200,30 @@ func TestAPIServerInvokeAPIRouteStream(t *testing.T) {
176200
assert.Equal(t, "a stream!", string(res.Body()))
177201
}
178202

203+
func TestAPIServerInvokeAPIPostEmptyArray(t *testing.T) {
204+
_, as, done := newTestAPIServer(t, true)
205+
defer done()
206+
207+
<-as.Started()
208+
209+
var o []*testInputStruct
210+
res, err := resty.New().R().
211+
SetBody([]*testInputStruct{}).
212+
SetResult(&o).
213+
Post(fmt.Sprintf("%s/api/v1/ut/utresource/id12345/postbatch", as.APIPublicURL()))
214+
assert.NoError(t, err)
215+
assert.Equal(t, 200, res.StatusCode())
216+
assert.Equal(t, []*testInputStruct{}, o)
217+
218+
res, err = resty.New().R().
219+
SetBody([]*testInputStruct{{Input1: "in1"}}).
220+
SetResult(&o).
221+
Post(fmt.Sprintf("%s/api/v1/ut/utresource/id12345/postbatch", as.APIPublicURL()))
222+
assert.NoError(t, err)
223+
assert.Equal(t, 200, res.StatusCode())
224+
assert.Equal(t, []*testInputStruct{{Input1: "in1"}}, o)
225+
}
226+
179227
func TestAPIServerInvokeAPIRouteLiveness(t *testing.T) {
180228
_, as, done := newTestAPIServer(t, true)
181229
defer done()

pkg/ffapi/handler.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,9 @@ func (hs *HandlerFactory) RouteHandler(route *Route) http.HandlerFunc {
216216
req.Header.Set("Content-Type", "application/json; charset=utf8")
217217
fallthrough
218218
case strings.HasPrefix(strings.ToLower(contentType), "application/json"):
219-
if jsonInput != nil {
219+
if route.JSONInputDecoder != nil {
220+
jsonInput, err = route.JSONInputDecoder(req, req.Body)
221+
} else if jsonInput != nil {
220222
d := json.NewDecoder(req.Body)
221223
d.UseNumber()
222224
err = d.Decode(&jsonInput)

pkg/ffapi/routes.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package ffapi
1919
import (
2020
"context"
2121
"io"
22+
"net/http"
2223

2324
"github.com/getkin/kin-openapi/openapi3"
2425
"github.com/hyperledger/firefly-common/pkg/config"
@@ -52,6 +53,8 @@ type Route struct {
5253
PreTranslatedDescription string
5354
// FilterFactory models the filter fields that can be specified on the API, and will automatically be parsed
5455
FilterFactory QueryFactory
56+
// JSONInputDecoder is a function that does the decoding completely - needed (as this was written pre-generics) for handling arrays
57+
JSONInputDecoder func(req *http.Request, body io.Reader) (interface{}, error)
5558
// JSONInputValue is a function that returns a pointer to a structure to take JSON input
5659
JSONInputValue func() interface{}
5760
// JSONInputMask are fields that aren't available for users to supply on input

0 commit comments

Comments
 (0)