fix(client): support gateway error format and wiki/open paths

Cherry-picked from co63oc's PR #193:
- Handle gateway {"code":N, "msg":"..."} error format
- Extend success status codes (201, 204)
- Exclude wiki/open paths from .json suffix auto-append

Co-Authored-By: co63oc <4617245+co63oc@users.noreply.github.com>
This commit is contained in:
co63oc 2026-06-10 15:07:33 +08:00 committed by Tiger
parent f9f96cfedc
commit 339900785f
2 changed files with 82 additions and 11 deletions

View File

@ -115,22 +115,35 @@ func (c *Client) Do(method, path string, body interface{}, query url.Values) (*o
}
// Check GitLink error-in-body pattern
// Support both {"status":N, "message":"..."} and gateway {"code":N, "msg":"..."}
var bodyCode float64
var bodyMsg string
if status, ok := raw["status"]; ok {
var statusCode float64
switch v := status.(type) {
case float64:
statusCode = v
bodyCode = v
case int:
statusCode = float64(v)
bodyCode = float64(v)
}
if statusCode != 0 && statusCode != 200 && statusCode != 1 {
msg, _ := raw["message"].(string)
suggestion := suggestFix(int(statusCode))
return output.ErrorEnvelope(int(statusCode), msg, suggestion), &APIError{
StatusCode: int(statusCode),
Code: int(statusCode),
Message: msg,
}
bodyMsg, _ = raw["message"].(string)
} else if code, ok := raw["code"]; ok {
switch v := code.(type) {
case float64:
bodyCode = v
case int:
bodyCode = float64(v)
}
bodyMsg, _ = raw["msg"].(string)
if bodyMsg == "" {
bodyMsg, _ = raw["message"].(string)
}
}
if bodyCode != 0 && bodyCode != 200 && bodyCode != 201 && bodyCode != 204 && bodyCode != 1 {
suggestion := suggestFix(int(bodyCode))
return output.ErrorEnvelope(int(bodyCode), bodyMsg, suggestion), &APIError{
StatusCode: int(bodyCode),
Code: int(bodyCode),
Message: bodyMsg,
}
}
@ -175,6 +188,10 @@ func shouldAppendJSONSuffix(path string) bool {
return false
}
}
// Wiki open API endpoints do not use .json suffix
if len(parts) >= 3 && parts[0] == "wiki" && parts[1] == "open" {
return false
}
return true
}

View File

@ -169,6 +169,45 @@ func TestClientDoStatusError(t *testing.T) {
}
}
func TestClientDoGatewayCodeError(t *testing.T) {
// Gateway returns {"code":N, "msg":"..."} instead of {"status":N, "message":"..."}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"code":400,"msg":"Bad Request"}`))
}))
defer server.Close()
c := &Client{HTTP: server.Client(), BaseURL: server.URL}
env, err := c.Do("GET", "/api/test", nil, nil)
if err == nil {
t.Fatal("expected error for code=400")
}
if env == nil {
t.Fatal("expected envelope for code error")
}
if env.OK {
t.Fatal("expected OK=false for code=400")
}
}
func TestClientDoGatewayCode201Success(t *testing.T) {
// Gateway returns code=201 with JSON string data — should be treated as success
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"code":201,"msg":"","data":"{\"title\":\"test\"}"}`))
}))
defer server.Close()
c := &Client{HTTP: server.Client(), BaseURL: server.URL}
env, err := c.Do("POST", "/api/test", map[string]string{"title": "test"}, nil)
if err != nil {
t.Fatalf("unexpected error for code=201: %v", err)
}
if !env.OK {
t.Fatal("expected OK=true for code=201")
}
}
func TestClientDoStatusZero(t *testing.T) {
// status=0, 200, 1 are treated as success
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -525,3 +564,18 @@ func TestShouldAppendJSONSuffixSkipsExistingJSONPath(t *testing.T) {
t.Fatal("existing .json path should not get another suffix")
}
}
func TestShouldAppendJSONSuffixSkipsWikiOpenPaths(t *testing.T) {
paths := []string{
"/wiki/open/createWiki",
"/wiki/open/getWiki",
"/wiki/open/updateWiki",
"/wiki/open/deleteWiki",
"/wiki/open/wikiPages",
}
for _, p := range paths {
if shouldAppendJSONSuffix(p) {
t.Errorf("wiki/open path %q should not get .json suffix", p)
}
}
}