Compare commits
1 Commits
master
...
fix/api-he
| Author | SHA1 | Date |
|---|---|---|
|
|
33387ae042 |
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"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 err != nil {
|
||||||
if apiErr, ok := err.(*client.APIError); ok {
|
if apiErr, ok := err.(*client.APIError); ok {
|
||||||
errEnv := output.ErrorEnvelope(apiErr.Code, apiErr.Message, "")
|
errEnv := output.ErrorEnvelope(apiErr.Code, apiErr.Message, "")
|
||||||
|
|
@ -78,6 +84,27 @@ func runAPI(c *cobra.Command, args []string) error {
|
||||||
return output.Print(env, resolveFormat())
|
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) {
|
func readJSONBody(c *cobra.Command) (interface{}, error) {
|
||||||
bodyStr, _ := c.Flags().GetString("body")
|
bodyStr, _ := c.Flags().GetString("body")
|
||||||
bodyFile, _ := c.Flags().GetString("body-file")
|
bodyFile, _ := c.Flags().GetString("body-file")
|
||||||
|
|
|
||||||
|
|
@ -88,3 +88,38 @@ func TestReadJSONBodyWithoutSource(t *testing.T) {
|
||||||
t.Fatalf("body = %v, want nil", body)
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,10 @@ func New() (*Client, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Do(method, path string, body interface{}, query url.Values) (*output.Envelope, 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)
|
path = normalizeAPIPath(c.BaseURL, path)
|
||||||
|
|
||||||
// Append .json suffix if not already present (GitLink API convention)
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
for key, values := range headers {
|
||||||
|
for _, value := range values {
|
||||||
|
req.Header.Add(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if c.Debug {
|
if c.Debug {
|
||||||
fmt.Printf("→ %s %s\n", method, fullURL)
|
fmt.Printf("→ %s %s\n", method, fullURL)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func TestNormalizeAPIPathStripsDuplicateAPIPrefix(t *testing.T) {
|
func TestNormalizeAPIPathStripsDuplicateAPIPrefix(t *testing.T) {
|
||||||
got := normalizeAPIPath("https://www.gitlink.org.cn/api", "/api/v1/repos/Gitlink/gitlink-cli/contents/README.md")
|
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)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue