diff --git a/client.go b/client.go index e2ba186..d8d9a00 100644 --- a/client.go +++ b/client.go @@ -342,22 +342,21 @@ func (c *OpenAPIClient) buildURL(op openAPIOperation, params map[string]any) (st appendQueryValue(query, key, value) } } - if op.method == http.MethodGet || (op.method == http.MethodHead && !op.hasRequestBody) { - for key, value := range params { - if key == "path" || key == "body" || key == "query" || key == "header" || key == "cookie" { - continue - } - if containsString(pathKeys, key) { - continue - } - if operationParameterLocation(op, key) == "header" || operationParameterLocation(op, key) == "cookie" { - continue - } - if _, exists := query[key]; exists { - continue - } - appendQueryValue(query, key, value) + for key, value := range params { + if key == "path" || key == "body" || key == "query" || key == "header" || key == "cookie" { + continue } + if containsString(pathKeys, key) { + continue + } + location := operationParameterLocation(op, key) + if location != "query" && !(location == "" && (op.method == http.MethodGet || (op.method == http.MethodHead && !op.hasRequestBody))) { + continue + } + if _, exists := query[key]; exists { + continue + } + appendQueryValue(query, key, value) } if encoded := query.Encode(); encoded != "" { diff --git a/client_test.go b/client_test.go index ecbdda2..751c209 100644 --- a/client_test.go +++ b/client_test.go @@ -257,6 +257,84 @@ paths: } } +func TestOpenAPIClient_Good_UsesTopLevelQueryParametersOnPost(t *testing.T) { + errCh := make(chan error, 1) + mux := http.NewServeMux() + mux.HandleFunc("/submit", func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + errCh <- fmt.Errorf("expected POST, got %s", r.Method) + w.WriteHeader(http.StatusInternalServerError) + return + } + if got := r.URL.Query().Get("verbose"); got != "true" { + errCh <- fmt.Errorf("expected query verbose=true, got %q", got) + w.WriteHeader(http.StatusInternalServerError) + return + } + body, err := io.ReadAll(r.Body) + if err != nil { + errCh <- fmt.Errorf("read body: %v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + if string(body) != `{"name":"Ada"}` { + errCh <- fmt.Errorf("expected JSON body {\"name\":\"Ada\"}, got %q", string(body)) + w.WriteHeader(http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"success":true,"data":{"ok":true}}`)) + }) + + srv := httptest.NewServer(mux) + defer srv.Close() + + specPath := writeTempSpec(t, `openapi: 3.1.0 +info: + title: Test API + version: 1.0.0 +paths: + /submit: + post: + operationId: submit_item + requestBody: + required: true + content: + application/json: + schema: + type: object + parameters: + - name: verbose + in: query +`) + + client := api.NewOpenAPIClient( + api.WithSpec(specPath), + api.WithBaseURL(srv.URL), + ) + + result, err := client.Call("submit_item", map[string]any{ + "verbose": true, + "name": "Ada", + }) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + select { + case err := <-errCh: + t.Fatal(err) + default: + } + + decoded, ok := result.(map[string]any) + if !ok { + t.Fatalf("expected map result, got %T", result) + } + if okValue, ok := decoded["ok"].(bool); !ok || !okValue { + t.Fatalf("expected ok=true, got %#v", decoded["ok"]) + } +} + func TestOpenAPIClient_Good_UsesHeaderAndCookieParameters(t *testing.T) { errCh := make(chan error, 1) mux := http.NewServeMux()