From 33387ae042b6c5249c6e893c01b9db992c6149d9 Mon Sep 17 00:00:00 2001 From: fsafasff <2624934035@qq.com> Date: Fri, 29 May 2026 17:38:03 +0800 Subject: [PATCH] fix: apply raw api header flags --- cmd/api/api.go | 29 +++++++++++++++++++++++++++- cmd/api/api_test.go | 35 ++++++++++++++++++++++++++++++++++ internal/client/client.go | 9 +++++++++ internal/client/client_test.go | 27 +++++++++++++++++++++++++- 4 files changed, 98 insertions(+), 2 deletions(-) diff --git a/cmd/api/api.go b/cmd/api/api.go index d13cf52..9dd7ad1 100644 --- a/cmd/api/api.go +++ b/cmd/api/api.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "io" + "net/http" "net/url" "os" "strings" @@ -66,7 +67,12 @@ func runAPI(c *cobra.Command, args []string) error { } } - env, err := cli.Do(method, path, body, query) + headers, err := parseHeaders(c) + if err != nil { + return err + } + + env, err := cli.DoWithHeaders(method, path, body, query, headers) if err != nil { if apiErr, ok := err.(*client.APIError); ok { errEnv := output.ErrorEnvelope(apiErr.Code, apiErr.Message, "") @@ -78,6 +84,27 @@ func runAPI(c *cobra.Command, args []string) error { return output.Print(env, resolveFormat()) } +func parseHeaders(c *cobra.Command) (http.Header, error) { + values, _ := c.Flags().GetStringSlice("header") + if len(values) == 0 { + return nil, nil + } + + headers := http.Header{} + for _, value := range values { + key, headerValue, ok := strings.Cut(value, ":") + if !ok { + return nil, fmt.Errorf("invalid header %q: expected key:value", value) + } + key = strings.TrimSpace(key) + if key == "" { + return nil, fmt.Errorf("invalid header %q: header key is empty", value) + } + headers.Add(key, strings.TrimSpace(headerValue)) + } + return headers, nil +} + func readJSONBody(c *cobra.Command) (interface{}, error) { bodyStr, _ := c.Flags().GetString("body") bodyFile, _ := c.Flags().GetString("body-file") diff --git a/cmd/api/api_test.go b/cmd/api/api_test.go index df9a104..821dfcb 100644 --- a/cmd/api/api_test.go +++ b/cmd/api/api_test.go @@ -88,3 +88,38 @@ func TestReadJSONBodyWithoutSource(t *testing.T) { t.Fatalf("body = %v, want nil", body) } } + +func TestParseHeaders(t *testing.T) { + cmd := NewAPICmd() + cmd.Flags().Set("header", "X-Test: value") + cmd.Flags().Set("header", "X-Trace: one:two") + + headers, err := parseHeaders(cmd) + if err != nil { + t.Fatalf("parseHeaders returned error: %v", err) + } + if got := headers.Get("X-Test"); got != "value" { + t.Fatalf("X-Test = %q, want value", got) + } + if got := headers.Get("X-Trace"); got != "one:two" { + t.Fatalf("X-Trace = %q, want one:two", got) + } +} + +func TestParseHeadersRejectsInvalidValue(t *testing.T) { + cmd := NewAPICmd() + cmd.Flags().Set("header", "missing-colon") + + if _, err := parseHeaders(cmd); err == nil { + t.Fatal("expected invalid header to return an error") + } +} + +func TestParseHeadersRejectsEmptyKey(t *testing.T) { + cmd := NewAPICmd() + cmd.Flags().Set("header", ": value") + + if _, err := parseHeaders(cmd); err == nil { + t.Fatal("expected empty header key to return an error") + } +} diff --git a/internal/client/client.go b/internal/client/client.go index 831caa7..52aab17 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -42,6 +42,10 @@ func New() (*Client, error) { } func (c *Client) Do(method, path string, body interface{}, query url.Values) (*output.Envelope, error) { + return c.DoWithHeaders(method, path, body, query, nil) +} + +func (c *Client) DoWithHeaders(method, path string, body interface{}, query url.Values, headers http.Header) (*output.Envelope, error) { path = normalizeAPIPath(c.BaseURL, path) // Append .json suffix if not already present (GitLink API convention) @@ -78,6 +82,11 @@ func (c *Client) Do(method, path string, body interface{}, query url.Values) (*o if err != nil { return nil, err } + for key, values := range headers { + for _, value := range values { + req.Header.Add(key, value) + } + } if c.Debug { fmt.Printf("→ %s %s\n", method, fullURL) diff --git a/internal/client/client_test.go b/internal/client/client_test.go index 87fb91d..2dcc851 100644 --- a/internal/client/client_test.go +++ b/internal/client/client_test.go @@ -1,6 +1,10 @@ package client -import "testing" +import ( + "net/http" + "net/http/httptest" + "testing" +) func TestNormalizeAPIPathStripsDuplicateAPIPrefix(t *testing.T) { got := normalizeAPIPath("https://www.gitlink.org.cn/api", "/api/v1/repos/Gitlink/gitlink-cli/contents/README.md") @@ -25,3 +29,24 @@ func TestNormalizeAPIPathKeepsAPIPrefixForNonAPIBaseURL(t *testing.T) { t.Fatalf("normalizeAPIPath() = %q, want %q", got, want) } } + +func TestDoWithHeadersAddsRequestHeaders(t *testing.T) { + var gotHeader string + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + gotHeader = r.Header.Get("X-Test") + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{"status":1,"data":{"ok":true}}`)) + })) + defer server.Close() + + cli := &Client{HTTP: server.Client(), BaseURL: server.URL} + headers := http.Header{} + headers.Set("X-Test", "value") + + if _, err := cli.DoWithHeaders("GET", "/users/me", nil, nil, headers); err != nil { + t.Fatalf("DoWithHeaders returned error: %v", err) + } + if gotHeader != "value" { + t.Fatalf("X-Test header = %q, want value", gotHeader) + } +}