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 5ff8cc337 fix(cluster): align the NameServer update body with the DTO
(#5882)
5ff8cc337 is described below
commit 5ff8cc33741b1f471af4010c3486da5c4b458b62
Author: Wang1rrr <[email protected]>
AuthorDate: Mon Oct 12 16:15:38 2026 +0800
fix(cluster): align the NameServer update body with the DTO (#5882)
The frontend and the backend disagreed about what `POST
/api/nameservers/update` accepts, and only mock mode honoured the frontend's
version.
- #5882 `UpdateNameServerDTO.java:29-37` carries exactly `clusterId`,
`addr` and `version` — there is no `newAddr` anywhere in the contract — yet
`web/src/api/cluster.ts:324-329` declared the field and forwarded the whole
object, and `web/src/services/clusterService.ts:410-428` implemented a
mock-mode rename complete with a duplicate-address check, so mock mode really
did change a NameServer's address while a live backend silently dropped the
unknown property (Boot does not fail on un [...]
Tests: `cluster.test.ts` goes 12 → 13 with an `it.each` over `version`
undefined and `'5.4.0'` that asserts the exact JSON body placed on the wire, so
reintroducing a pass-through adds `newAddr` and fails, and
`clusterService.test.ts` goes 7 → 8 asserting the mock leaves `nameServers`
equal to the original, so restoring the mock rename fails.
---
docs/api-spec.md | 2 +-
web/src/api/cluster.test.ts | 28 ++++++++++++++++++---
web/src/api/cluster.ts | 5 ++--
web/src/services/clusterService.test.ts | 43 ++++++++++++++++++---------------
web/src/services/clusterService.ts | 9 +------
5 files changed, 53 insertions(+), 34 deletions(-)
diff --git a/docs/api-spec.md b/docs/api-spec.md
index c411ab0a2..e5b0c7fd2 100644
--- a/docs/api-spec.md
+++ b/docs/api-spec.md
@@ -575,7 +575,7 @@ POST /api/nameservers/update
|------|------|------|------|
| `clusterId` | `string` | 是 | 所属集群 ID |
| `addr` | `string` | 是 | 原地址 |
-| `newAddr` | `string` | 否 | 新地址(不传则不修改) |
+| `version` | `string` | 否 | NameServer 版本(不传则不修改) |
**Response `data`:** `NameServerInfo`
diff --git a/web/src/api/cluster.test.ts b/web/src/api/cluster.test.ts
index f0e8dbef5..5c3fa4e65 100644
--- a/web/src/api/cluster.test.ts
+++ b/web/src/api/cluster.test.ts
@@ -37,6 +37,9 @@ import {
upgradeNameServer,
} from './cluster';
import type { K8sCertInfo } from './cluster';
+import { updateNameServer as updateNameServerService } from
'../services/clusterService';
+
+vi.mock('../services/dataMode', () => ({ isMockMode: () => false }));
const mock = new MockAdapter(client);
@@ -173,7 +176,7 @@ describe('K8s certificate API', () => {
['/nameservers/restart', target],
['/nameservers/upgrade', { ...target, version: '5.4.0' }],
['/nameservers/create', target],
- ['/nameservers/update', { ...target, newAddr: '127.0.0.2:9876' }],
+ ['/nameservers/update', { ...target, version: '5.4.0' }],
['/nameservers/delete', target],
] as const;
requests.forEach(([url, body]) => {
@@ -186,12 +189,29 @@ describe('K8s certificate API', () => {
await expect(restartNameServer(target)).resolves.toBeUndefined();
await expect(upgradeNameServer({ ...target, version: '5.4.0'
})).resolves.toBeUndefined();
await expect(createNameServer(target)).resolves.toBeUndefined();
- await expect(
- updateNameServer({ ...target, newAddr: '127.0.0.2:9876' }),
- ).resolves.toBeUndefined();
+ await expect(updateNameServer({ ...target, version: '5.4.0'
})).resolves.toBeUndefined();
await expect(deleteNameServer(target)).resolves.toBeUndefined();
});
+ it.each([
+ ['API', updateNameServer],
+ ['live service', updateNameServerService],
+ ] as const)('sends only the NameServer update DTO fields through the %s',
async (_, update) => {
+ const target = { clusterId: 'cluster-1', addr: '127.0.0.1:9876' };
+ mock.onPost('/nameservers/update').reply(200, { code: 200, data: null });
+
+ for (const version of [undefined, '5.4.0']) {
+ const request = { ...target, version, newAddr: '127.0.0.2:9876' };
+ await update(request);
+
+ const lastRequest = mock.history.post[mock.history.post.length - 1];
+ expect(JSON.parse(lastRequest.data)).toEqual({
+ ...target,
+ ...(version === undefined ? {} : { version }),
+ });
+ }
+ });
+
it('loads NameServer configuration drift for the selected cluster', async ()
=> {
const result = {
cluster: 'cluster-1',
diff --git a/web/src/api/cluster.ts b/web/src/api/cluster.ts
index e83f09782..28a8907cc 100644
--- a/web/src/api/cluster.ts
+++ b/web/src/api/cluster.ts
@@ -324,9 +324,10 @@ export async function createNameServer(data: { clusterId:
string; addr: string }
export async function updateNameServer(data: {
clusterId: string;
addr: string;
- newAddr?: string;
+ version?: string;
}) {
- await client.post('/nameservers/update', data);
+ const { clusterId, addr, version } = data;
+ await client.post('/nameservers/update', { clusterId, addr, version });
}
export async function getNameServerConfigDiff(clusterId: string, instanceId?:
string) {
diff --git a/web/src/services/clusterService.test.ts
b/web/src/services/clusterService.test.ts
index 614915b77..fd2af0972 100644
--- a/web/src/services/clusterService.test.ts
+++ b/web/src/services/clusterService.test.ts
@@ -23,7 +23,9 @@ vi.mock('../config', () => ({
}));
import {
+ createNameServer,
createK8sCert,
+ deleteNameServer,
deleteK8sCert,
getCluster,
getNameServerConfigDiff,
@@ -138,36 +140,39 @@ describe('clusterService mock clusters', () => {
}
});
- it('rejects NameServer edits that would duplicate an address in the same
cluster', async () => {
+ it('accepts the update DTO without renaming a mock NameServer', async () => {
const clusterId = 'cluster-prod';
+ const addr = 'nameserver-update.example:9876';
+ const newAddr = 'nameserver-renamed.example:9876';
+ await createNameServer({ clusterId, addr });
const original = await getCluster(clusterId);
- const [first, second] = original.nameServers;
try {
- await expect(
- updateNameServer({
- clusterId,
- addr: first.addr,
- newAddr: second.addr,
- }),
- ).rejects.toThrow(`NameServer already exists: ${second.addr}`);
-
- const fresh = await getCluster(clusterId);
- expect(fresh.nameServers.map((item) => item.addr)).toEqual(
- original.nameServers.map((item) => item.addr),
- );
+ for (const version of [undefined, '5.4.0']) {
+ const request = { clusterId, addr, version, newAddr };
+ await expect(updateNameServer(request)).resolves.toBeUndefined();
+ expect((await
getCluster(clusterId)).nameServers).toEqual(original.nameServers);
+ }
} finally {
const current = await getCluster(clusterId);
- for (let index = 0; index < original.nameServers.length; index += 1) {
- const currentAddr = current.nameServers[index]?.addr;
- const originalAddr = original.nameServers[index].addr;
- if (currentAddr && currentAddr !== originalAddr) {
- await updateNameServer({ clusterId, addr: currentAddr, newAddr:
originalAddr });
+ for (const candidate of [addr, newAddr]) {
+ if (current.nameServers.some((item) => item.addr === candidate)) {
+ await deleteNameServer({ clusterId, addr: candidate });
}
}
}
});
+ it('rejects a NameServer update when its target is absent from the selected
cluster', async () => {
+ const clusterId = 'cluster-prod';
+ const before = await getCluster(clusterId);
+
+ await expect(
+ updateNameServer({ clusterId, addr: 'missing.example:9876', version:
'5.4.0' }),
+ ).rejects.toThrow('NameServer not found: missing.example:9876');
+ expect((await
getCluster(clusterId)).nameServers).toEqual(before.nameServers);
+ });
+
it('copies certificate SAN arrays before writing them into the mock store',
async () => {
const san = ['proxy.example.com'];
const created = await createK8sCert({
diff --git a/web/src/services/clusterService.ts
b/web/src/services/clusterService.ts
index 0f46640eb..e8f55f4fe 100644
--- a/web/src/services/clusterService.ts
+++ b/web/src/services/clusterService.ts
@@ -410,19 +410,12 @@ export async function createNameServer(data: { clusterId:
string; addr: string }
export async function updateNameServer(data: {
clusterId: string;
addr: string;
- newAddr?: string;
+ version?: string;
}): Promise<void> {
if (isMockMode()) {
const nameServers = getMockCluster(data.clusterId).nameServers;
const nameServer = nameServers.find((item) => item.addr === data.addr);
if (!nameServer) throw new Error(`NameServer not found: ${data.addr}`);
- if (data.newAddr && data.newAddr !== data.addr) {
- const duplicate = nameServers.some(
- (item) => item !== nameServer && item.addr === data.newAddr,
- );
- if (duplicate) throw new Error(`NameServer already exists:
${data.newAddr}`);
- }
- if (data.newAddr) nameServer.addr = data.newAddr;
return;
}
return clusterApi.updateNameServer(data);