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 e49b2b612 fix(consumers): canonicalize delivery order type (#2458)
e49b2b612 is described below

commit e49b2b6128c7b6d371e21363b849cad8ba9ec40c
Author: shown <[email protected]>
AuthorDate: Thu Aug 27 11:55:32 2026 +0800

    fix(consumers): canonicalize delivery order type (#2458)
    
    Signed-off-by: yuluo-yx <[email protected]>
---
 .../pages/instance/__tests__/ConsumerPage.test.tsx | 45 +++++++++++++++++++++-
 web/src/pages/instance/consumer.tsx                |  2 +-
 web/src/utils/resourceCsvImport.test.ts            | 24 ++++++++++++
 web/src/utils/resourceCsvImport.ts                 |  9 ++++-
 4 files changed, 75 insertions(+), 5 deletions(-)

diff --git a/web/src/pages/instance/__tests__/ConsumerPage.test.tsx 
b/web/src/pages/instance/__tests__/ConsumerPage.test.tsx
index 8d62db0f4..da4cca616 100644
--- a/web/src/pages/instance/__tests__/ConsumerPage.test.tsx
+++ b/web/src/pages/instance/__tests__/ConsumerPage.test.tsx
@@ -15,8 +15,8 @@
  * limitations under the License.
  */
 
-import { App } from 'antd';
-import { act, render, screen, waitFor, within } from '@testing-library/react';
+import { App, Modal } from 'antd';
+import { act, fireEvent, render, screen, waitFor, within } from 
'@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import type React from 'react';
 import { MemoryRouter } from 'react-router-dom';
@@ -219,6 +219,47 @@ describe('Consumer page', () => {
     });
   });
 
+  it('submits the canonical global delivery order type', async () => {
+    const user = userEvent.setup();
+    const confirmSpy = vi.spyOn(Modal, 'confirm').mockImplementation((config) 
=> {
+      void config.onOk?.();
+      return { destroy: vi.fn(), update: vi.fn() } as unknown as 
ReturnType<typeof Modal.confirm>;
+    });
+    renderWithProviders(<ConsumerPage />);
+
+    await screen.findByText('remote-cg');
+    const createButton = screen.getByRole('button', { name: '创建 Group' });
+    await waitFor(() => expect(createButton).toBeEnabled());
+    await user.click(createButton);
+    const dialog = await screen.findByRole('dialog');
+    await user.type(within(dialog).getByLabelText('Group 名称'), 
'cg-global-orders');
+
+    const dataTypeSelect = within(dialog).getByRole('combobox', { name: 
'订阅组类型' });
+    fireEvent.mouseDown(dataTypeSelect.parentElement!);
+    await user.click(
+      await screen.findByText('顺序消息', { selector: 
'.ant-select-item-option-content' }),
+    );
+
+    const orderTypeSelect = within(dialog).getByRole('combobox', { name: 
'顺序类型' });
+    fireEvent.mouseDown(orderTypeSelect.parentElement!);
+    await user.click(
+      await screen.findByText('全局顺序', { selector: 
'.ant-select-item-option-content' }),
+    );
+    await user.click(within(dialog).getByRole('button', { name: /创\s*建/ }));
+
+    await waitFor(() =>
+      expect(consumerService.createConsumerGroup).toHaveBeenCalledWith(
+        expect.objectContaining({
+          name: 'cg-global-orders',
+          subscriptionDataType: 'FIFO',
+          deliveryOrderType: 'MESSAGES_ORDER',
+        }),
+      ),
+    );
+    expect(confirmSpy).toHaveBeenCalledTimes(1);
+    confirmSpy.mockRestore();
+  });
+
   it('prefills the group search from the ?group= query parameter', async () => 
{
     renderWithProviders(<ConsumerPage />, 
'/instance/consumer?group=remote-cg');
 
diff --git a/web/src/pages/instance/consumer.tsx 
b/web/src/pages/instance/consumer.tsx
index f5fa93ff1..1da662bf5 100644
--- a/web/src/pages/instance/consumer.tsx
+++ b/web/src/pages/instance/consumer.tsx
@@ -1824,7 +1824,7 @@ const ConsumerPageContent = ({
                     label: '分区顺序',
                   },
                   {
-                    value: 'MESSAGES ORDER',
+                    value: 'MESSAGES_ORDER',
                     label: '全局顺序',
                   },
                 ]}
diff --git a/web/src/utils/resourceCsvImport.test.ts 
b/web/src/utils/resourceCsvImport.test.ts
index 979f38fb9..1f0381cab 100644
--- a/web/src/utils/resourceCsvImport.test.ts
+++ b/web/src/utils/resourceCsvImport.test.ts
@@ -167,4 +167,28 @@ describe('resourceCsvImport', () => {
     );
     expect(validateResourceName('ok-name|100%', 'group')).toBeNull();
   });
+
+  it.each(['MESSAGES_ORDER', 'MESSAGES ORDER'])(
+    'accepts and canonicalizes the global delivery order value %s from CSV',
+    (deliveryOrderType) => {
+      const records = parseCsvTable(
+        [
+          '"Name","Subscription Data Type","Delivery Order Type"',
+          `"cg-global-orders","FIFO","${deliveryOrderType}"`,
+        ].join('\n'),
+      );
+      const validation = validateConsumerGroupCsvImport(records, 'instance-3');
+
+      expect(validation.errors).toEqual([]);
+      expect(validation.rows[0]).toMatchObject({
+        status: 'pending',
+        payload: {
+          name: 'cg-global-orders',
+          subscriptionDataType: 'FIFO',
+          deliveryOrderType: 'MESSAGES_ORDER',
+          instanceId: 'instance-3',
+        },
+      });
+    },
+  );
 });
diff --git a/web/src/utils/resourceCsvImport.ts 
b/web/src/utils/resourceCsvImport.ts
index bda78d46a..c10314fb6 100644
--- a/web/src/utils/resourceCsvImport.ts
+++ b/web/src/utils/resourceCsvImport.ts
@@ -72,7 +72,7 @@ const TOPIC_PERMISSIONS = new Set(['RW', 'RO', 'WO']);
 const GROUP_SUBSCRIPTION_MODES = new Set(['Push', 'Pop']);
 const GROUP_CONSUME_TYPES = new Set(['CLUSTERING', 'BROADCASTING']);
 const GROUP_SUBSCRIPTION_DATA_TYPES = new Set(['NORMAL', 'FIFO', 'DELAY', 
'TRANSACTION']);
-const GROUP_DELIVERY_ORDER_TYPES = new Set(['PARTITON_ORDER', 
'PARTITION_ORDER', 'MESSAGES ORDER']);
+const GROUP_DELIVERY_ORDER_TYPES = new Set(['PARTITON_ORDER', 
'PARTITION_ORDER', 'MESSAGES_ORDER']);
 
 const restoreFormulaSafeCell = (value: string): string =>
   value.replace(FORMULA_SAFE_PREFIX_PATTERN, '');
@@ -82,6 +82,9 @@ const normalizeHeader = (header: string): string => 
restoreFormulaSafeCell(heade
 const normalizeValue = (value: string | undefined): string =>
   restoreFormulaSafeCell(value ?? '').trim();
 
+const normalizeDeliveryOrderType = (value: string): string =>
+  value === 'MESSAGES ORDER' ? 'MESSAGES_ORDER' : value;
+
 const parseInteger = (
   value: string,
   fieldName: string,
@@ -334,7 +337,9 @@ export const validateConsumerGroupCsvImport = (
     );
     const subscriptionDataType =
       normalizeValue(record.values['Subscription Data Type']) || 'NORMAL';
-    const deliveryOrderType = normalizeValue(record.values['Delivery Order 
Type']);
+    const deliveryOrderType = normalizeDeliveryOrderType(
+      normalizeValue(record.values['Delivery Order Type']),
+    );
     const duplicateMessage = duplicateMessages.get(record.lineNumber);
     if (duplicateMessage) rowErrors.push(duplicateMessage);
 

Reply via email to