Skip to content

Commit 5fe1c8f

Browse files
authored
fix: write correct error structure (#611)
1 parent 0917707 commit 5fe1c8f

3 files changed

Lines changed: 115 additions & 3 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ require (
2525
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0
2626
go.opentelemetry.io/otel/sdk v1.39.0
2727
go.opentelemetry.io/otel/sdk/metric v1.39.0
28+
go.opentelemetry.io/otel/trace v1.39.0
2829
golang.org/x/tools v0.38.0
2930
gorm.io/driver/mysql v1.5.7
3031
gorm.io/gorm v1.30.0
@@ -146,7 +147,6 @@ require (
146147
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
147148
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 // indirect
148149
go.opentelemetry.io/otel/metric v1.39.0 // indirect
149-
go.opentelemetry.io/otel/trace v1.39.0 // indirect
150150
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
151151
golang.org/x/mod v0.29.0 // indirect
152152
golang.org/x/oauth2 v0.32.0 // indirect

internal/core/io_tunnel/backwards_invocation/transaction/serverless_handler.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,27 @@ func (h *ServerlessTransactionHandler) Handle(ctx *gin.Context, sessionId string
8383
session, err := session_manager.GetSession(sessionId)
8484
if err != nil {
8585
ctx.Writer.WriteHeader(http.StatusBadRequest)
86-
ctx.Writer.Write([]byte(err.Error()))
87-
writer.Close()
86+
invokePayload, marshalErr := parser.UnmarshalJsonBytes2Map(sessionMessage.Data)
87+
var backwardsRequestId string
88+
if marshalErr == nil {
89+
backwardsRequestId, _ = invokePayload["backwards_request_id"].(string)
90+
}
91+
92+
respData := backwards_invocation.BackwardsInvocationResponseEvent{
93+
BackwardsRequestId: backwardsRequestId,
94+
Event: backwards_invocation.REQUEST_EVENT_ERROR,
95+
Message: "failed to get session info from cache",
96+
Data: map[string]any{
97+
"error_type": "SessionNotFound",
98+
"detail": err.Error(), // 保留“key not found”作为 detail
99+
"session_id": sessionId,
100+
},
101+
}
102+
_, err = ctx.Writer.Write(parser.MarshalJsonBytes(respData))
103+
if err != nil {
104+
log.Error("failed to write response", "error", err)
105+
}
106+
_ = writer.Close()
88107
return
89108
}
90109

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package transaction
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"net/http/httptest"
7+
"testing"
8+
"time"
9+
10+
"github.com/gin-gonic/gin"
11+
"github.com/langgenius/dify-plugin-daemon/internal/core/io_tunnel/backwards_invocation"
12+
"github.com/langgenius/dify-plugin-daemon/pkg/entities/plugin_entities"
13+
"github.com/langgenius/dify-plugin-daemon/pkg/utils/parser"
14+
)
15+
16+
func TestHandle_SessionNotFound_WritesErrorResponse(t *testing.T) {
17+
gin.SetMode(gin.TestMode)
18+
19+
// Arrange: craft a valid PluginUniversalEvent that will trigger the session handler path
20+
// with a backwards_request_id extracted from SessionMessage.Data
21+
const (
22+
testSessionID = "test-session-not-exist"
23+
testBackwardsReqID = "req-123"
24+
)
25+
26+
invokePayload := map[string]any{
27+
"backwards_request_id": testBackwardsReqID,
28+
}
29+
invokePayloadBytes := parser.MarshalJsonBytes(invokePayload)
30+
31+
sessionMsg := plugin_entities.SessionMessage{
32+
Type: plugin_entities.SESSION_MESSAGE_TYPE_INVOKE,
33+
Data: invokePayloadBytes,
34+
}
35+
sessionMsgBytes := parser.MarshalJsonBytes(sessionMsg)
36+
37+
event := plugin_entities.PluginUniversalEvent{
38+
SessionId: testSessionID,
39+
Event: plugin_entities.PLUGIN_EVENT_SESSION,
40+
Data: json.RawMessage(sessionMsgBytes),
41+
}
42+
body := bytes.NewReader(parser.MarshalJsonBytes(event))
43+
44+
recorder := httptest.NewRecorder()
45+
ctx, _ := gin.CreateTestContext(recorder)
46+
ctx.Request = httptest.NewRequest("POST", "/serverless", body)
47+
48+
h := NewServerlessTransactionHandler(2 * time.Second)
49+
50+
// Act: handle the request; since no session exists in memory and the cache client
51+
// is not initialized in tests, session_manager.GetSession will fail and the
52+
// error branch should write a BackwardsInvocationResponseEvent to the response.
53+
h.Handle(ctx, "ignored")
54+
55+
// Assert
56+
var resp backwards_invocation.BackwardsInvocationResponseEvent
57+
if err := json.Unmarshal(recorder.Body.Bytes(), &resp); err != nil {
58+
t.Fatalf("response should be JSON BackwardsInvocationResponseEvent, got error: %v, body: %s", err, recorder.Body.String())
59+
}
60+
61+
if resp.Event != backwards_invocation.REQUEST_EVENT_ERROR {
62+
t.Fatalf("expected event=error, got %q", resp.Event)
63+
}
64+
if resp.Message != "failed to get session info from cache" {
65+
t.Fatalf("expected message 'failed to get session info from cache', got %q", resp.Message)
66+
}
67+
if resp.BackwardsRequestId != testBackwardsReqID {
68+
t.Fatalf("expected backwards_request_id=%q, got %q", testBackwardsReqID, resp.BackwardsRequestId)
69+
}
70+
71+
// Data should include error_type, detail, and session_id
72+
m, ok := resp.Data.(map[string]any)
73+
if !ok {
74+
// json.Unmarshal into interface{} yields map[string]any by default; if not, re-marshal and unmarshal to map
75+
raw := parser.MarshalJsonBytes(resp.Data)
76+
var tmp map[string]any
77+
if err := json.Unmarshal(raw, &tmp); err == nil {
78+
m = tmp
79+
} else {
80+
t.Fatalf("response data should be an object: %v", err)
81+
}
82+
}
83+
84+
if v, _ := m["error_type"].(string); v != "SessionNotFound" {
85+
t.Fatalf("expected data.error_type=SessionNotFound, got %v", m["error_type"])
86+
}
87+
if v, _ := m["session_id"].(string); v != testSessionID {
88+
t.Fatalf("expected data.session_id=%q, got %v", testSessionID, m["session_id"])
89+
}
90+
if v, _ := m["detail"].(string); v == "" {
91+
t.Fatalf("expected non-empty data.detail, got empty")
92+
}
93+
}

0 commit comments

Comments
 (0)