From c83326fed515419336f33a98251a594e14cea547 Mon Sep 17 00:00:00 2001 From: Julien Silland Date: Thu, 26 Aug 2021 20:20:23 -0700 Subject: [PATCH] Adds support for query parameters in path patterns This completes the resolution of https://github.com/go-openapi/runtime/issues/207 by adding support for extracting static query parameters in path patterns, on top of base path. Signed-off-by: Julien Silland --- client/request.go | 36 +++++++++++++++++++++++++----------- client/request_test.go | 38 +++++++++++++++++++++++++++++++++++--- 2 files changed, 60 insertions(+), 14 deletions(-) diff --git a/client/request.go b/client/request.go index 07ec972d..2ffeb8d1 100644 --- a/client/request.go +++ b/client/request.go @@ -273,22 +273,36 @@ DoneChoosingBodySource: } } - // create http request - var reinstateSlash bool - if r.pathPattern != "" && r.pathPattern != "/" && r.pathPattern[len(r.pathPattern)-1] == '/' { - reinstateSlash = true + // In case the basePath or the request pathPattern include static query parameters, + // parse those out before constructing the final path. The parameters themselves + // will be merged with the ones set by the client, with the priority given first to + // the ones set by the client, then the path pattern, and lastly the base path. + basePathURL, err := url.Parse(basePath) + if err != nil { + return nil, err } + staticQueryParams := basePathURL.Query() - // In case the basePath includes hardcoded query parameters, parse those out before - // constructing the final path. The parameters themselves will be merged with the - // ones set by the client, with the priority given to the latter. - basePathURL, err := url.Parse(basePath) + pathPatternURL, err := url.Parse(r.pathPattern) if err != nil { return nil, err } - basePathQueryParams := basePathURL.Query() + for name, values := range pathPatternURL.Query() { + if _, present := staticQueryParams[name]; present { + staticQueryParams.Del(name) + } + for _, value := range values { + staticQueryParams.Add(name, value) + } + } + + // create http request + var reinstateSlash bool + if pathPatternURL.Path != "" && pathPatternURL.Path != "/" && pathPatternURL.Path[len(pathPatternURL.Path)-1] == '/' { + reinstateSlash = true + } - urlPath := path.Join(basePathURL.Path, r.pathPattern) + urlPath := path.Join(basePathURL.Path, pathPatternURL.Path) for k, v := range r.pathParams { urlPath = strings.Replace(urlPath, "{"+k+"}", url.PathEscape(v), -1) } @@ -305,7 +319,7 @@ DoneChoosingBodySource: // Merge the query parameters extracted from the basePath with the ones set by // the client in this struct. In case of conflict, the client wins. - for k, v := range basePathQueryParams { + for k, v := range staticQueryParams { _, present := originalParams[k] if !present { if err = r.SetQueryParam(k, v...); err != nil { diff --git a/client/request_test.go b/client/request_test.go index 86d0b0ff..c169ff04 100644 --- a/client/request_test.go +++ b/client/request_test.go @@ -517,7 +517,7 @@ func TestBuildRequest_BuildHTTP_EscapedPath(t *testing.T) { } } -func TestBuildRequest_BuildHTTP_BasePathWithParameters(t *testing.T) { +func TestBuildRequest_BuildHTTP_BasePathWithQueryParameters(t *testing.T) { reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetBodyParam(nil) _ = req.SetQueryParam("hello", "world") @@ -534,14 +534,30 @@ func TestBuildRequest_BuildHTTP_BasePathWithParameters(t *testing.T) { } } -func TestBuildRequest_BuildHTTP_BasePathWithConflictingParameters(t *testing.T) { +func TestBuildRequest_BuildHTTP_PathPatternWithQueryParameters(t *testing.T) { reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetBodyParam(nil) _ = req.SetQueryParam("hello", "world") _ = req.SetPathParam("id", "1234") return nil }) - r, _ := newRequest("POST", "/flats/{id}/", reqWrtr) + r, _ := newRequest("POST", "/flats/{id}/?foo=bar", reqWrtr) + + req, err := r.BuildHTTP(runtime.JSONMime, "/basepath", testProducers, nil) + if assert.NoError(t, err) && assert.NotNil(t, req) { + assert.Equal(t, "world", req.URL.Query().Get("hello")) + assert.Equal(t, "bar", req.URL.Query().Get("foo")) + assert.Equal(t, "/basepath/flats/1234/", req.URL.Path) + } +} + +func TestBuildRequest_BuildHTTP_StaticParametersPathPatternPrevails(t *testing.T) { + reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { + _ = req.SetBodyParam(nil) + _ = req.SetPathParam("id", "1234") + return nil + }) + r, _ := newRequest("POST", "/flats/{id}/?hello=world", reqWrtr) req, err := r.BuildHTTP(runtime.JSONMime, "/basepath?hello=kitty", testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { @@ -550,6 +566,22 @@ func TestBuildRequest_BuildHTTP_BasePathWithConflictingParameters(t *testing.T) } } +func TestBuildRequest_BuildHTTP_StaticParametersConflictClientPrevails(t *testing.T) { + reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { + _ = req.SetBodyParam(nil) + _ = req.SetQueryParam("hello", "there") + _ = req.SetPathParam("id", "1234") + return nil + }) + r, _ := newRequest("POST", "/flats/{id}/?hello=world", reqWrtr) + + req, err := r.BuildHTTP(runtime.JSONMime, "/basepath?hello=kitty", testProducers, nil) + if assert.NoError(t, err) && assert.NotNil(t, req) { + assert.Equal(t, "there", req.URL.Query().Get("hello")) + assert.Equal(t, "/basepath/flats/1234/", req.URL.Path) + } +} + type testReqFn func(*testing.T, *http.Request) type testRoundTripper struct {