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);