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 32345ec18 fix(web): render a placeholder for an unknown instance
protocol (#5729)
32345ec18 is described below
commit 32345ec18e66e05d35c3b9a99e8768b1b229c712
Author: 0 <[email protected]>
AuthorDate: Mon Oct 19 19:53:54 2026 +0800
fix(web): render a placeholder for an unknown instance protocol (#5729)
Verified against `rocketmq-studio` at fbdc5f22b before merging: the defect
is still present on the
current trunk and the change merges cleanly.
Maintainer follow-ups on top of the contributor commit:
- test(consumer): align the unknown-protocol placeholder with the page
convention
Each follow-up commit message records the review finding it addresses.
Locally verified: targeted suites green on the integration branch; a full
backend and frontend
build plus test pass runs on the trunk once this batch lands.
---
web/src/api/metadata.ts | 10 ++++---
.../pages/instance/__tests__/ConsumerPage.test.tsx | 32 ++++++++++++++++++++++
web/src/pages/instance/consumer.tsx | 8 +++++-
web/src/services/consumerService.test.ts | 6 ++--
web/src/services/consumerService.ts | 4 +--
5 files changed, 50 insertions(+), 10 deletions(-)
diff --git a/web/src/api/metadata.ts b/web/src/api/metadata.ts
index b11ca24ec..92b1d9309 100644
--- a/web/src/api/metadata.ts
+++ b/web/src/api/metadata.ts
@@ -114,11 +114,13 @@ export interface ConsumerGroup {
export interface ConsumerInstance {
clientId: string;
- protocol: string;
+ /** Absent when the backend cannot tell (Apache connections carry no
protocol marker). */
+ protocol?: string;
address: string;
- subscribedTopics: string[];
- lastHeartbeat: string;
- topicLag: Record<string, number>;
+ /** Populated only when the provider reports per-topic lag for the instance.
*/
+ subscribedTopics?: string[];
+ lastHeartbeat?: string;
+ topicLag?: Record<string, number>;
}
export interface ConsumerThreadStack {
diff --git a/web/src/pages/instance/__tests__/ConsumerPage.test.tsx
b/web/src/pages/instance/__tests__/ConsumerPage.test.tsx
index f4ca5db97..2c8c51b2b 100644
--- a/web/src/pages/instance/__tests__/ConsumerPage.test.tsx
+++ b/web/src/pages/instance/__tests__/ConsumerPage.test.tsx
@@ -1971,6 +1971,38 @@ describe('Consumer page', () => {
expect(await within(row).findByText('42')).toBeInTheDocument();
});
+ it('renders a placeholder for an online instance whose protocol is unknown',
async () => {
+ // The Apache provider derives online instances from a broker connection
set, which
+ // carries no protocol marker — the API legitimately returns the field
absent.
+ vi.mocked(consumerService.listConsumerGroupPage).mockResolvedValue(
+ groupPage([
+ {
+ ...group,
+ instances: [{ clientId: 'client-no-proto', address: '10.0.0.9:1234'
}],
+ },
+ ]),
+ );
+ const user = userEvent.setup({ pointerEventsCheck: 0 });
+ renderWithProviders(<ConsumerPage />);
+
+ await user.click(await screen.findByRole('button', { name: /详情/ }));
+ const dialog = await screen.findByRole('dialog', { name: /remote-cg/ });
+ const instanceRow = await within(dialog).findByText('client-no-proto');
+ const row = instanceRow.closest('tr') as HTMLElement;
+
+ // Locate the protocol cell through its header instead of a hardcoded
column index, so
+ // the assertion keeps working when the instance columns are reordered.
+ const headers = Array.from(row.closest('table')!.querySelectorAll('thead
th')).map((th) =>
+ th.textContent?.trim(),
+ );
+ const protocolIndex = headers.indexOf('协议');
+ expect(protocolIndex).toBeGreaterThan(-1);
+
+ const cells = within(row).getAllByRole('cell');
+ expect(cells[protocolIndex].textContent?.trim()).toBe('-');
+ expect(cells[protocolIndex].querySelector('.ant-tag')).toBeNull();
+ });
+
it('edits retry settings from the detail modal settings tab', async () => {
vi.mocked(consumerService.getConsumerGroupSettings).mockResolvedValue({
groupName: 'remote-cg',
diff --git a/web/src/pages/instance/consumer.tsx
b/web/src/pages/instance/consumer.tsx
index 572e25c3b..0c0170c67 100644
--- a/web/src/pages/instance/consumer.tsx
+++ b/web/src/pages/instance/consumer.tsx
@@ -1229,7 +1229,13 @@ const ConsumerPageContent = ({
dataIndex: 'protocol',
key: 'protocol',
width: 80,
- render: (protocol: string) => {
+ render: (protocol?: string) => {
+ // The Apache provider cannot tell the protocol from a broker
connection set,
+ // so the field is legitimately absent — render a placeholder, not an
empty tag.
+ // Plain '-' matches the other optional columns of this page.
+ if (!protocol) {
+ return '-';
+ }
const config = PROTOCOL_MAP[protocol] || { labelKey: protocol, color:
'default' };
return <Tag color={config.color}>{t(config.labelKey)}</Tag>;
},
diff --git a/web/src/services/consumerService.test.ts
b/web/src/services/consumerService.test.ts
index e42efe3a0..ae9eff14c 100644
--- a/web/src/services/consumerService.test.ts
+++ b/web/src/services/consumerService.test.ts
@@ -214,19 +214,19 @@ describe('consumer service mock data', () => {
first[0].name = 'mutated-group';
first[0].subscribedTopics.push('mutated-topic');
- first[0].instances[0].topicLag['order-create'] = 999999;
+ first[0].instances[0].topicLag!['order-create'] = 999999;
const second = await listConsumerGroups({ search: 'cg-order-notify' });
expect(second[0].name).toBe('cg-order-notify');
expect(second[0].subscribedTopics).not.toContain('mutated-topic');
- expect(second[0].instances[0].topicLag['order-create']).toBe(180);
+ expect(second[0].instances[0].topicLag!['order-create']).toBe(180);
expect(second[0]).not.toBe(first[0]);
expect(second[0].instances[0]).not.toBe(first[0].instances[0]);
});
it('returns copied consumer group details', async () => {
const first = await getConsumerGroup('cg-order-notify');
- first.instances[0].subscribedTopics.push('mutated-topic');
+ first.instances[0].subscribedTopics!.push('mutated-topic');
const second = await getConsumerGroup('cg-order-notify');
expect(second.instances[0].subscribedTopics).not.toContain('mutated-topic');
diff --git a/web/src/services/consumerService.ts
b/web/src/services/consumerService.ts
index 9663345fd..53f57abdb 100644
--- a/web/src/services/consumerService.ts
+++ b/web/src/services/consumerService.ts
@@ -57,8 +57,8 @@ function copyConsumerInstance(
): ConsumerGroup['instances'][number] {
return {
...instance,
- subscribedTopics: [...instance.subscribedTopics],
- topicLag: { ...instance.topicLag },
+ ...(instance.subscribedTopics ? { subscribedTopics:
[...instance.subscribedTopics] } : {}),
+ ...(instance.topicLag ? { topicLag: { ...instance.topicLag } } : {}),
};
}