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 e1bf88889 fix(web): persist mock consumer group settings (#6431)
e1bf88889 is described below
commit e1bf88889de5681186040e3f5606264387b9b188
Author: Loyal-Young <[email protected]>
AuthorDate: Thu Oct 15 20:27:06 2026 +0800
fix(web): persist mock consumer group settings (#6431)
* fix(web): persist mock consumer group settings
* fix(web): persist all mock consumer group settings fields
The settings dialog edits five fields, but the mock branch only stored
retryQueueNums and retryMaxTimes, so consumeEnable, consumeMessageOrderly
and consumeBroadcastEnable were lost on reopen. Widen the mock state
intersection type and round-trip the three toggles on both sides, keeping
real mode delegating to metadataApi untouched.
---------
Co-authored-by: lizhimins <[email protected]>
---
web/src/services/consumerService.test.ts | 159 +++++++++++++++++++++++++++++++
web/src/services/consumerService.ts | 38 +++++++-
2 files changed, 194 insertions(+), 3 deletions(-)
diff --git a/web/src/services/consumerService.test.ts
b/web/src/services/consumerService.test.ts
index f4d6c0177..893c3e722 100644
--- a/web/src/services/consumerService.test.ts
+++ b/web/src/services/consumerService.test.ts
@@ -22,6 +22,8 @@ import {
deleteConsumerGroup,
exportConsumerGroups,
getConsumerGroup,
+ getConsumerGroupSettings,
+ updateConsumerGroupSettings,
getConsumerProgress,
getConsumerStack,
getConsumerSubscriptions,
@@ -36,6 +38,8 @@ const { mode, metadataApi } = vi.hoisted(() => ({
mode: { mock: true },
metadataApi: {
getConsumerGroup: vi.fn(),
+ getConsumerGroupSettings: vi.fn(),
+ updateConsumerGroupSettings: vi.fn(),
listConsumerGroupPage: vi.fn(),
listConsumerGroups: vi.fn(),
previewConsumerOffsetReset: vi.fn(),
@@ -49,6 +53,161 @@ vi.mock('../config', () => ({
vi.mock('../api/metadata', () => metadataApi);
describe('consumer service mock data', () => {
+ it('roundTripsSettingsForTheSelectedMockGroupAndInstanceTest', async () => {
+ const name = 'settings-round-trip';
+ await createConsumerGroup({ name, instanceId: 'settings-a', retryMaxTimes:
3 });
+ await createConsumerGroup({ name, instanceId: 'settings-b', retryMaxTimes:
7 });
+ try {
+ expect(await getConsumerGroupSettings(name, 'settings-a')).toEqual({
+ groupName: name,
+ retryQueueNums: 1,
+ retryMaxTimes: 3,
+ });
+ const saved = await updateConsumerGroupSettings({
+ name,
+ instanceId: 'settings-a',
+ retryQueueNums: 4,
+ retryMaxTimes: 0,
+ });
+ expect(saved).toEqual({ groupName: name, retryQueueNums: 4,
retryMaxTimes: 0 });
+ saved.retryMaxTimes = 99;
+ const read = await getConsumerGroupSettings(name, 'settings-a');
+ expect(read).toEqual({ groupName: name, retryQueueNums: 4,
retryMaxTimes: 0 });
+ read.retryQueueNums = 99;
+ expect((await getConsumerGroupSettings(name,
'settings-a')).retryQueueNums).toBe(4);
+ expect((await getConsumerGroupSettings(name,
'settings-b')).retryMaxTimes).toBe(7);
+ expect((await getConsumerGroup(name,
'settings-a')).retryMaxTimes).toBe(0);
+ } finally {
+ await deleteConsumerGroup(name, 'settings-a');
+ await deleteConsumerGroup(name, 'settings-b');
+ }
+ });
+
+ it('roundTripsMockConsumptionTogglesPerGroupAndInstanceTest', async () => {
+ const name = 'settings-toggles';
+ await createConsumerGroup({ name, instanceId: 'toggles-a' });
+ await createConsumerGroup({ name, instanceId: 'toggles-b' });
+ try {
+ const expectedA = {
+ groupName: name,
+ retryQueueNums: 2,
+ retryMaxTimes: 5,
+ consumeEnable: false,
+ consumeMessageOrderly: true,
+ consumeBroadcastEnable: true,
+ };
+ expect(
+ await updateConsumerGroupSettings({
+ name,
+ instanceId: 'toggles-a',
+ retryQueueNums: 2,
+ retryMaxTimes: 5,
+ consumeEnable: false,
+ consumeMessageOrderly: true,
+ consumeBroadcastEnable: true,
+ }),
+ ).toEqual(expectedA);
+ // Re-opening the dialog must show what was saved, including the
explicit `false`.
+ expect(await getConsumerGroupSettings(name,
'toggles-a')).toEqual(expectedA);
+
+ const expectedB = {
+ groupName: name,
+ retryQueueNums: 1,
+ retryMaxTimes: 16,
+ consumeEnable: true,
+ consumeMessageOrderly: false,
+ consumeBroadcastEnable: false,
+ };
+ expect(
+ await updateConsumerGroupSettings({
+ name,
+ instanceId: 'toggles-b',
+ retryQueueNums: 1,
+ retryMaxTimes: 16,
+ consumeEnable: true,
+ consumeMessageOrderly: false,
+ consumeBroadcastEnable: false,
+ }),
+ ).toEqual(expectedB);
+ expect(await getConsumerGroupSettings(name,
'toggles-b')).toEqual(expectedB);
+ // A same-named group under another instance keeps its own toggles.
+ expect((await getConsumerGroupSettings(name,
'toggles-a')).consumeEnable).toBe(false);
+ expect((await getConsumerGroupSettings(name,
'toggles-b')).consumeEnable).toBe(true);
+ expect((await getConsumerGroupSettings(name,
'toggles-a')).consumeMessageOrderly).toBe(true);
+ expect((await getConsumerGroupSettings(name,
'toggles-b')).consumeMessageOrderly).toBe(false);
+ } finally {
+ await deleteConsumerGroup(name, 'toggles-a');
+ await deleteConsumerGroup(name, 'toggles-b');
+ }
+ });
+
+ it('doesNotResurrectSettingsAfterDeletingAndRecreatingAMockGroupTest', async
() => {
+ const name = 'settings-recreated';
+ const instanceId = 'settings-instance';
+ await createConsumerGroup({ name, instanceId });
+ try {
+ await updateConsumerGroupSettings({ name, instanceId, retryQueueNums: 4,
retryMaxTimes: 2 });
+ expect((await getConsumerGroupSettings(name,
instanceId)).retryQueueNums).toBe(4);
+ await deleteConsumerGroup(name, instanceId);
+ await createConsumerGroup({ name, instanceId });
+ expect(await getConsumerGroupSettings(name, instanceId)).toEqual({
+ groupName: name,
+ retryQueueNums: 1,
+ retryMaxTimes: 16,
+ });
+ } finally {
+ await deleteConsumerGroup(name, instanceId);
+ }
+ });
+
+ it('rejectsMockSettingsForAMissingGroupOrWrongInstanceTest', async () => {
+ const name = 'settings-owned';
+ await createConsumerGroup({ name, instanceId: 'owner', retryMaxTimes: 8 });
+ try {
+ await expect(getConsumerGroupSettings(name, 'other')).rejects.toThrow(
+ 'Consumer group not found',
+ );
+ await expect(
+ updateConsumerGroupSettings({
+ name,
+ instanceId: 'other',
+ retryQueueNums: 4,
+ retryMaxTimes: 2,
+ }),
+ ).rejects.toThrow('Consumer group not found');
+ await expect(getConsumerGroupSettings('missing-settings-group',
'owner')).rejects.toThrow(
+ 'Consumer group not found',
+ );
+ expect((await getConsumerGroup(name, 'owner')).retryMaxTimes).toBe(8);
+ } finally {
+ await deleteConsumerGroup(name, 'owner');
+ }
+ });
+
+ it('delegatesRealSettingsCallsWithoutUsingMockStateTest', async () => {
+ mode.mock = false;
+ const settings = { groupName: 'real', retryQueueNums: 2, retryMaxTimes: 5
};
+ const request = {
+ name: 'real',
+ instanceId: 'real-instance',
+ retryQueueNums: 2,
+ retryMaxTimes: 5,
+ };
+ metadataApi.getConsumerGroupSettings.mockResolvedValueOnce(settings);
+ metadataApi.updateConsumerGroupSettings.mockResolvedValueOnce(settings);
+ try {
+ expect(await getConsumerGroupSettings('real',
'real-instance')).toBe(settings);
+ expect(await updateConsumerGroupSettings(request)).toBe(settings);
+ expect(metadataApi.getConsumerGroupSettings).toHaveBeenLastCalledWith(
+ 'real',
+ 'real-instance',
+ );
+
expect(metadataApi.updateConsumerGroupSettings).toHaveBeenLastCalledWith(request);
+ } finally {
+ mode.mock = true;
+ }
+ });
+
it('returns copied consumer group rows', async () => {
const first = await listConsumerGroups({ search: 'cg-order-notify' });
expect(first[0].name).toBe('cg-order-notify');
diff --git a/web/src/services/consumerService.ts
b/web/src/services/consumerService.ts
index 9133e9614..aec166c7e 100644
--- a/web/src/services/consumerService.ts
+++ b/web/src/services/consumerService.ts
@@ -20,7 +20,12 @@ import { mockConsumerGroups, mockQueueProgress,
mockSubscriptions } from '../moc
import { formatOnlineInstances } from '../utils/consumerConnections';
import { buildCsv, type CsvColumn } from '../utils/download';
-const consumerGroupsState = mockConsumerGroups as unknown as ConsumerGroup[];
+const consumerGroupsState = mockConsumerGroups as unknown as (ConsumerGroup & {
+ retryQueueNums?: number;
+ consumeEnable?: boolean;
+ consumeMessageOrderly?: boolean;
+ consumeBroadcastEnable?: boolean;
+})[];
const EXPORT_PAGE_SIZE = 100;
const MAX_EXPORT_PAGES = 100;
const GROUP_EXPORT_COLUMNS: CsvColumn<ConsumerGroup>[] = [
@@ -193,19 +198,46 @@ export async function getConsumerGroupSettings(
name: string,
instanceId: string,
): Promise<ConsumerGroupSettings> {
- if (isMockMode()) return { groupName: name, retryQueueNums: 1,
retryMaxTimes: 16 };
+ if (isMockMode()) {
+ const group = consumerGroupsState.find(
+ (item) => item.name === name && item.instanceId === instanceId,
+ );
+ if (!group) throw new Error(`Consumer group not found: ${name}`);
+ return {
+ groupName: name,
+ retryQueueNums: group.retryQueueNums ?? 1,
+ retryMaxTimes: group.retryMaxTimes ?? 16,
+ consumeEnable: group.consumeEnable,
+ consumeMessageOrderly: group.consumeMessageOrderly,
+ consumeBroadcastEnable: group.consumeBroadcastEnable,
+ };
+ }
return metadataApi.getConsumerGroupSettings(name, instanceId);
}
export async function updateConsumerGroupSettings(
data: Omit<ConsumerGroupSettings, 'groupName'> & { instanceId: string; name:
string },
) {
- if (isMockMode())
+ if (isMockMode()) {
+ const group = consumerGroupsState.find(
+ (item) => item.name === data.name && item.instanceId === data.instanceId,
+ );
+ if (!group) throw new Error(`Consumer group not found: ${data.name}`);
+ group.retryQueueNums = data.retryQueueNums;
+ group.retryMaxTimes = data.retryMaxTimes;
+ group.consumeEnable = data.consumeEnable;
+ group.consumeMessageOrderly = data.consumeMessageOrderly;
+ group.consumeBroadcastEnable = data.consumeBroadcastEnable;
+ group.gmtModified = new Date().toISOString();
return {
groupName: data.name,
retryQueueNums: data.retryQueueNums,
retryMaxTimes: data.retryMaxTimes,
+ consumeEnable: data.consumeEnable,
+ consumeMessageOrderly: data.consumeMessageOrderly,
+ consumeBroadcastEnable: data.consumeBroadcastEnable,
};
+ }
return metadataApi.updateConsumerGroupSettings(data);
}