Repository: go-openapi/runtime
Pull request #212 (2021-08-27 03:34:28)

Adds support for static 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 <julien@soliton.io>


Patch:
diff --git a/client/request.go b/client/request.go
index 07ec972..2ffeb8d 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 {


Test patch:
diff --git a/client/request_test.go b/client/request_test.go
index 86d0b0f..c169ff0 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 {