This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new 21676fc85 fix(studio): return SSE refusal for missing AI message body 
(#5088)
21676fc85 is described below

commit 21676fc852bcde58595d66c25508d08495264bc7
Author: coder999o <[email protected]>
AuthorDate: Thu Oct 1 17:28:18 2026 +0800

    fix(studio): return SSE refusal for missing AI message body (#5088)
---
 .../ops/ai/conversation/AiConversationController.java   |  7 ++++++-
 .../ops/ai/conversation/AiStreamControllerTest.java     | 17 +++++++++++++++++
 2 files changed, 23 insertions(+), 1 deletion(-)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/ops/ai/conversation/AiConversationController.java
 
b/server/src/main/java/org/apache/rocketmq/studio/ops/ai/conversation/AiConversationController.java
index b43d87f28..d31b2ec4a 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/ops/ai/conversation/AiConversationController.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/ops/ai/conversation/AiConversationController.java
@@ -132,6 +132,7 @@ public class AiConversationController {
 
     /** Fallback for a constraint that carries no message; the wording {@code 
GlobalExceptionHandler} uses. */
     private static final String INVALID_REQUEST_MESSAGE = "Invalid request";
+    private static final String MESSAGE_REQUIRED_MESSAGE = "message is 
required";
 
     private final AiConversationService conversationService;
     private final AiRunService runService;
@@ -236,12 +237,16 @@ public class AiConversationController {
      */
     @PostMapping(value = "/conversations/{id}/messages", produces = 
MediaType.TEXT_EVENT_STREAM_VALUE)
     public SseEmitter sendMessage(@PathVariable("id") Long conversationId,
-                                  @Valid @RequestBody AiMessageDTO request,
+                                  @Valid @RequestBody(required = false) 
AiMessageDTO request,
                                   BindingResult bindingResult,
                                   HttpServletResponse response) {
         // Both outcomes of this endpoint are event streams — an admitted run 
and a refusal alike — so the
         // headers go on first. A refusal is converted rather than thrown: see 
refusalStream.
         streamingHeaders(response);
+        if (request == null) {
+            return runService.refusalStream(HttpStatus.BAD_REQUEST.value(), 
AiRunService.REFUSED_INVALID_CODE,
+                    MESSAGE_REQUIRED_MESSAGE, null);
+        }
         if (bindingResult.hasErrors()) {
             // A BindingResult directly after the @Valid body makes Spring 
hand the violations here
             // instead of throwing MethodArgumentNotValidException. That is 
the only way to answer them
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/ops/ai/conversation/AiStreamControllerTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/ops/ai/conversation/AiStreamControllerTest.java
index c96a1e755..b46d81a9b 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/ops/ai/conversation/AiStreamControllerTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/ops/ai/conversation/AiStreamControllerTest.java
@@ -276,6 +276,23 @@ class AiStreamControllerTest extends WebMvcAuthTestSupport 
{
         verify(runService).refusalStream(unconfigured);
     }
 
+    @Test
+    void sendMessageShouldRejectAMissingBodyInsideTheStreamTest() throws 
Exception {
+        when(runService.refusalStream(eq(400), 
eq(AiRunService.REFUSED_INVALID_CODE),
+                eq("message is required"), isNull()))
+                .thenReturn(new 
AiRunTestSupport.RecordingSseEmitter(CLI_TIMEOUT_MILLIS));
+
+        mockMvc.perform(post("/api/ai/conversations/{id}/messages", 
CONVERSATION_ID)
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .accept(MediaType.TEXT_EVENT_STREAM))
+                .andExpect(status().isOk())
+                
.andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_EVENT_STREAM))
+                .andExpect(header().string(ACCEL_BUFFERING_HEADER, "no"));
+
+        verify(runService).refusalStream(400, 
AiRunService.REFUSED_INVALID_CODE, "message is required", null);
+        verify(runService, never()).sendMessage(any(), any());
+    }
+
     @Test
     void sendMessageShouldRejectABlankMessageInsideTheStreamTest() throws 
Exception {
         // The endpoint takes a BindingResult next to the @Valid body, so a 
rejected body is answered in

Reply via email to