Compare commits

...

4 Commits

Author SHA1 Message Date
Crazylychee
07793d8aae [ISSUE #329] Add Frontend Proxy Component Support (#336) 2025-07-05 20:53:57 +08:00
Crazylychee
4b9ed97f8f [ISSUE #332] Add configuration options for login information, supporting ACL or file storage (#333) 2025-07-05 20:51:42 +08:00
Crazylychee
ff73529a75 [ISSUE #330] Format code and update the doc (#334) 2025-07-05 20:50:36 +08:00
Crazylychee
706082c62f [ISSUE #331] Fix failing tests (#335) 2025-07-05 20:50:08 +08:00
129 changed files with 2675 additions and 2350 deletions

View File

@@ -6,19 +6,14 @@ This project was bootstrapped with [Create React App](https://github.com/faceboo
In the project directory, you can run: In the project directory, you can run:
### `npm start` ### `npm run start`
Runs the app in the development mode.\ Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser. Open [http://localhost:3003](http://localhost:3000) to view it in your browser.
The page will reload when you make changes.\ The page will reload when you make changes.\
You may also see any lint errors in the console. You may also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build` ### `npm run build`
Builds the app for production to the `build` folder.\ Builds the app for production to the `build` folder.\

View File

@@ -77,12 +77,12 @@ const remoteApi = {
listUsers: async (brokerAddress) => { listUsers: async (brokerAddress) => {
const params = new URLSearchParams(); const params = new URLSearchParams();
if (brokerAddress) params.append('brokerAddress', brokerAddress); if (brokerAddress) params.append('brokerAddress', brokerAddress);
const response = await remoteApi._fetch(remoteApi.buildUrl(`/acl/listUsers?${params.toString()}`)); const response = await remoteApi._fetch(remoteApi.buildUrl(`/acl/acls.query?${params.toString()}`));
return await response.json(); return await response.json();
}, },
createUser: async (brokerAddress, userInfo) => { createUser: async (brokerAddress, userInfo) => {
const response = await remoteApi._fetch(remoteApi.buildUrl('/acl/createUser'), { const response = await remoteApi._fetch(remoteApi.buildUrl('/acl/createUser.do'), {
method: 'POST', method: 'POST',
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
body: JSON.stringify({brokerAddress, userInfo}) body: JSON.stringify({brokerAddress, userInfo})
@@ -91,7 +91,7 @@ const remoteApi = {
}, },
updateUser: async (brokerAddress, userInfo) => { updateUser: async (brokerAddress, userInfo) => {
const response = await remoteApi._fetch(remoteApi.buildUrl('/acl/updateUser'), { const response = await remoteApi._fetch(remoteApi.buildUrl('/acl/updateUser.do'), {
method: 'POST', method: 'POST',
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
body: JSON.stringify({brokerAddress, userInfo}) body: JSON.stringify({brokerAddress, userInfo})
@@ -103,7 +103,7 @@ const remoteApi = {
const params = new URLSearchParams(); const params = new URLSearchParams();
if (brokerAddress) params.append('brokerAddress', brokerAddress); if (brokerAddress) params.append('brokerAddress', brokerAddress);
params.append('username', username); params.append('username', username);
const response = await remoteApi._fetch(remoteApi.buildUrl(`/acl/deleteUser?${params.toString()}`), { const response = await remoteApi._fetch(remoteApi.buildUrl(`/acl/deleteUser.do?${params.toString()}`), {
method: 'DELETE' method: 'DELETE'
}); });
return await response.json(); return await response.json();
@@ -114,12 +114,12 @@ const remoteApi = {
const params = new URLSearchParams(); const params = new URLSearchParams();
if (brokerAddress) params.append('brokerAddress', brokerAddress); if (brokerAddress) params.append('brokerAddress', brokerAddress);
if (searchParam) params.append('searchParam', searchParam); if (searchParam) params.append('searchParam', searchParam);
const response = await remoteApi._fetch(remoteApi.buildUrl(`/acl/listAcls?${params.toString()}`)); const response = await remoteApi._fetch(remoteApi.buildUrl(`/acl/acls.query?${params.toString()}`));
return await response.json(); return await response.json();
}, },
createAcl: async (brokerAddress, subject, policies) => { createAcl: async (brokerAddress, subject, policies) => {
const response = await remoteApi._fetch(remoteApi.buildUrl('/acl/createAcl'), { const response = await remoteApi._fetch(remoteApi.buildUrl('/acl/createAcl.do'), {
method: 'POST', method: 'POST',
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
body: JSON.stringify({brokerAddress, subject, policies}) body: JSON.stringify({brokerAddress, subject, policies})
@@ -128,7 +128,7 @@ const remoteApi = {
}, },
updateAcl: async (brokerAddress, subject, policies) => { updateAcl: async (brokerAddress, subject, policies) => {
const response = await remoteApi._fetch(remoteApi.buildUrl('/acl/updateAcl'), { const response = await remoteApi._fetch(remoteApi.buildUrl('/acl/updateAcl.do'), {
method: 'POST', method: 'POST',
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
body: JSON.stringify({brokerAddress, subject, policies}) body: JSON.stringify({brokerAddress, subject, policies})
@@ -141,7 +141,7 @@ const remoteApi = {
if (brokerAddress) params.append('brokerAddress', brokerAddress); if (brokerAddress) params.append('brokerAddress', brokerAddress);
params.append('subject', subject); params.append('subject', subject);
if (resource) params.append('resource', resource); if (resource) params.append('resource', resource);
const response = await remoteApi._fetch(remoteApi.buildUrl(`/acl/deleteAcl?${params.toString()}`), { const response = await remoteApi._fetch(remoteApi.buildUrl(`/acl/deleteAcl.do?${params.toString()}`), {
method: 'DELETE' method: 'DELETE'
}); });
return await response.json(); return await response.json();
@@ -376,9 +376,9 @@ const remoteApi = {
} }
}, },
queryConsumerGroupList: async (skipSysGroup = false) => { queryConsumerGroupList: async (skipSysGroup, address) => {
try { try {
const response = await remoteApi._fetch(remoteApi.buildUrl(`/consumer/groupList.query?skipSysGroup=${skipSysGroup}`)); const response = await remoteApi._fetch(remoteApi.buildUrl(`/consumer/groupList.query?skipSysGroup=${skipSysGroup}&address=${address}`));
const data = await response.json(); const data = await response.json();
return data; return data;
} catch (error) { } catch (error) {

View File

@@ -16,7 +16,7 @@
*/ */
import React from 'react'; import React from 'react';
import { Form, Input, Typography, Modal } from 'antd'; import {Form, Input, Typography} from 'antd';
import moment from 'moment'; import moment from 'moment';
import {useLanguage} from '../i18n/LanguageContext'; // 根据实际路径调整 import {useLanguage} from '../i18n/LanguageContext'; // 根据实际路径调整

View File

@@ -16,9 +16,9 @@
*/ */
import React from 'react'; import React from 'react';
import { Modal, Button, Typography, Descriptions, Tag, Spin, notification } from 'antd'; import {Button, Descriptions, Modal, notification, Spin, Tag, Typography} from 'antd';
import moment from 'moment'; import moment from 'moment';
import { ExclamationCircleOutlined, SyncOutlined } from '@ant-design/icons'; import {SyncOutlined} from '@ant-design/icons';
import {useLanguage} from '../i18n/LanguageContext'; import {useLanguage} from '../i18n/LanguageContext';
import {remoteApi} from '../api/remoteApi/remoteApi'; // 确保这个路径正确 import {remoteApi} from '../api/remoteApi/remoteApi'; // 确保这个路径正确
@@ -96,10 +96,14 @@ const MessageDetailViewDialog = ({ visible, onCancel, messageId, topic, onResend
{messageDetail ? ( // 确保 messageDetail 存在时才渲染内容 {messageDetail ? ( // 确保 messageDetail 存在时才渲染内容
<> <>
{/* 消息信息部分 */} {/* 消息信息部分 */}
<Descriptions title={<Text strong>{t.MESSAGE_INFO}</Text>} bordered column={2} size="small" style={{ marginBottom: 20 }}> <Descriptions title={<Text strong>{t.MESSAGE_INFO}</Text>} bordered column={2} size="small"
<Descriptions.Item label="Topic" span={2}><Text copyable>{messageDetail.messageView.topic}</Text></Descriptions.Item> style={{marginBottom: 20}}>
<Descriptions.Item label="Message ID" span={2}><Text copyable>{messageDetail.messageView.msgId}</Text></Descriptions.Item> <Descriptions.Item label="Topic" span={2}><Text
<Descriptions.Item label="StoreHost">{messageDetail.messageView.storeHost}</Descriptions.Item> copyable>{messageDetail.messageView.topic}</Text></Descriptions.Item>
<Descriptions.Item label="Message ID" span={2}><Text
copyable>{messageDetail.messageView.msgId}</Text></Descriptions.Item>
<Descriptions.Item
label="StoreHost">{messageDetail.messageView.storeHost}</Descriptions.Item>
<Descriptions.Item label="BornHost">{messageDetail.messageView.bornHost}</Descriptions.Item> <Descriptions.Item label="BornHost">{messageDetail.messageView.bornHost}</Descriptions.Item>
<Descriptions.Item label="StoreTime"> <Descriptions.Item label="StoreTime">
{moment(messageDetail.messageView.storeTimestamp).format("YYYY-MM-DD HH:mm:ss")} {moment(messageDetail.messageView.storeTimestamp).format("YYYY-MM-DD HH:mm:ss")}
@@ -108,26 +112,33 @@ const MessageDetailViewDialog = ({ visible, onCancel, messageId, topic, onResend
{moment(messageDetail.messageView.bornTimestamp).format("YYYY-MM-DD HH:mm:ss")} {moment(messageDetail.messageView.bornTimestamp).format("YYYY-MM-DD HH:mm:ss")}
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label="Queue ID">{messageDetail.messageView.queueId}</Descriptions.Item> <Descriptions.Item label="Queue ID">{messageDetail.messageView.queueId}</Descriptions.Item>
<Descriptions.Item label="Queue Offset">{messageDetail.messageView.queueOffset}</Descriptions.Item> <Descriptions.Item
<Descriptions.Item label="StoreSize">{messageDetail.messageView.storeSize} bytes</Descriptions.Item> label="Queue Offset">{messageDetail.messageView.queueOffset}</Descriptions.Item>
<Descriptions.Item label="ReconsumeTimes">{messageDetail.messageView.reconsumeTimes}</Descriptions.Item> <Descriptions.Item
label="StoreSize">{messageDetail.messageView.storeSize} bytes</Descriptions.Item>
<Descriptions.Item
label="ReconsumeTimes">{messageDetail.messageView.reconsumeTimes}</Descriptions.Item>
<Descriptions.Item label="BodyCRC">{messageDetail.messageView.bodyCRC}</Descriptions.Item> <Descriptions.Item label="BodyCRC">{messageDetail.messageView.bodyCRC}</Descriptions.Item>
<Descriptions.Item label="SysFlag">{messageDetail.messageView.sysFlag}</Descriptions.Item> <Descriptions.Item label="SysFlag">{messageDetail.messageView.sysFlag}</Descriptions.Item>
<Descriptions.Item label="Flag">{messageDetail.messageView.flag}</Descriptions.Item> <Descriptions.Item label="Flag">{messageDetail.messageView.flag}</Descriptions.Item>
<Descriptions.Item label="PreparedTransactionOffset">{messageDetail.messageView.preparedTransactionOffset}</Descriptions.Item> <Descriptions.Item
label="PreparedTransactionOffset">{messageDetail.messageView.preparedTransactionOffset}</Descriptions.Item>
</Descriptions> </Descriptions>
{/* 消息属性部分 */} {/* 消息属性部分 */}
{Object.keys(messageDetail.messageView.properties).length > 0 && ( {Object.keys(messageDetail.messageView.properties).length > 0 && (
<Descriptions title={<Text strong>{t.MESSAGE_PROPERTIES}</Text>} bordered column={1} size="small" style={{ marginBottom: 20 }}> <Descriptions title={<Text strong>{t.MESSAGE_PROPERTIES}</Text>} bordered column={1}
size="small" style={{marginBottom: 20}}>
{Object.entries(messageDetail.messageView.properties).map(([key, value]) => ( {Object.entries(messageDetail.messageView.properties).map(([key, value]) => (
<Descriptions.Item label={key} key={key}><Text copyable>{value}</Text></Descriptions.Item> <Descriptions.Item label={key} key={key}><Text
copyable>{value}</Text></Descriptions.Item>
))} ))}
</Descriptions> </Descriptions>
)} )}
{/* 消息体部分 */} {/* 消息体部分 */}
<Descriptions title={<Text strong>{t.MESSAGE_BODY}</Text>} bordered column={1} size="small" style={{ marginBottom: 20 }}> <Descriptions title={<Text strong>{t.MESSAGE_BODY}</Text>} bordered column={1} size="small"
style={{marginBottom: 20}}>
<Descriptions.Item> <Descriptions.Item>
<Paragraph <Paragraph
copyable copyable
@@ -148,7 +159,8 @@ const MessageDetailViewDialog = ({ visible, onCancel, messageId, topic, onResend
<Text strong>{t.MESSAGE_TRACKING}</Text> <Text strong>{t.MESSAGE_TRACKING}</Text>
<div style={{marginTop: 10}}> <div style={{marginTop: 10}}>
{messageDetail.messageTrackList.map((track, index) => ( {messageDetail.messageTrackList.map((track, index) => (
<Descriptions bordered column={1} size="small" key={index} style={{ marginBottom: 15 }}> <Descriptions bordered column={1} size="small" key={index}
style={{marginBottom: 15}}>
<Descriptions.Item label={t.CONSUMER_GROUP}> <Descriptions.Item label={t.CONSUMER_GROUP}>
{track.consumerGroup} {track.consumerGroup}
</Descriptions.Item> </Descriptions.Item>
@@ -181,7 +193,10 @@ const MessageDetailViewDialog = ({ visible, onCancel, messageId, topic, onResend
ellipsis={{ ellipsis={{
rows: 2, // 默认显示2行 rows: 2, // 默认显示2行
expandable: true, expandable: true,
symbol: <Text style={{ color: '#1890ff', cursor: 'pointer' }}>{t.READ_MORE}</Text>, // 蓝色展开文本 symbol: <Text style={{
color: '#1890ff',
cursor: 'pointer'
}}>{t.READ_MORE}</Text>, // 蓝色展开文本
}} }}
> >
{track.exceptionDesc} {track.exceptionDesc}
@@ -198,7 +213,8 @@ const MessageDetailViewDialog = ({ visible, onCancel, messageId, topic, onResend
</> </>
) : ( ) : (
// 当 messageDetail 为 null 时,可以显示一个占位符或者不显示内容 // 当 messageDetail 为 null 时,可以显示一个占位符或者不显示内容
!loading && !error && <Paragraph style={{ textAlign: 'center' }}>{t.NO_MESSAGE_DETAIL_AVAILABLE}</Paragraph> !loading && !error &&
<Paragraph style={{textAlign: 'center'}}>{t.NO_MESSAGE_DETAIL_AVAILABLE}</Paragraph>
)} )}
</Spin> </Spin>
</Modal> </Modal>

View File

@@ -16,7 +16,7 @@
*/ */
import React, {useEffect, useRef} from 'react'; import React, {useEffect, useRef} from 'react';
import { Form, Input, Typography, Collapse, Table, Tag } from 'antd'; import {Collapse, Form, Input, Table, Tag, Typography} from 'antd';
import moment from 'moment'; import moment from 'moment';
import {useLanguage} from '../i18n/LanguageContext'; import {useLanguage} from '../i18n/LanguageContext';
import Paragraph from "antd/es/skeleton/Paragraph"; import Paragraph from "antd/es/skeleton/Paragraph";
@@ -125,6 +125,7 @@ const MessageTraceDetailViewDialog = ({ ngDialogData }) => {
} }
return `Cost Time: ${formatCostTimeStr(costTime)}<br/>` return `Cost Time: ${formatCostTimeStr(costTime)}<br/>`
} }
function buildTimeStamp(timestamp) { function buildTimeStamp(timestamp) {
if (timestamp < 0) { if (timestamp < 0) {
return 'N/A'; return 'N/A';
@@ -323,50 +324,130 @@ const MessageTraceDetailViewDialog = ({ ngDialogData }) => {
// ... (rest of your existing component code) // ... (rest of your existing component code)
const transactionColumns = [ const transactionColumns = [
{ title: t.TIMESTAMP, dataIndex: 'beginTimestamp', key: 'beginTimestamp', align: 'center', render: (text) => moment(text).format('YYYY-MM-DD HH:mm:ss.SSS') }, {
{ title: t.TRANSACTION_STATE, dataIndex: 'transactionState', key: 'transactionState', align: 'center', render: (text) => <Tag color={text === 'COMMIT_MESSAGE' ? 'green' : (text === 'ROLLBACK_MESSAGE' ? 'red' : 'default')}>{text}</Tag> }, title: t.TIMESTAMP,
{ title: t.FROM_TRANSACTION_CHECK, dataIndex: 'fromTransactionCheck', key: 'fromTransactionCheck', align: 'center', render: (text) => (text ? <Tag color="blue">{t.YES}</Tag> : <Tag color="purple">{t.NO}</Tag>) }, dataIndex: 'beginTimestamp',
key: 'beginTimestamp',
align: 'center',
render: (text) => moment(text).format('YYYY-MM-DD HH:mm:ss.SSS')
},
{
title: t.TRANSACTION_STATE,
dataIndex: 'transactionState',
key: 'transactionState',
align: 'center',
render: (text) => <Tag
color={text === 'COMMIT_MESSAGE' ? 'green' : (text === 'ROLLBACK_MESSAGE' ? 'red' : 'default')}>{text}</Tag>
},
{
title: t.FROM_TRANSACTION_CHECK,
dataIndex: 'fromTransactionCheck',
key: 'fromTransactionCheck',
align: 'center',
render: (text) => (text ? <Tag color="blue">{t.YES}</Tag> : <Tag color="purple">{t.NO}</Tag>)
},
{title: t.CLIENT_HOST, dataIndex: 'clientHost', key: 'clientHost', align: 'center'}, {title: t.CLIENT_HOST, dataIndex: 'clientHost', key: 'clientHost', align: 'center'},
{title: t.STORE_HOST, dataIndex: 'storeHost', key: 'storeHost', align: 'center'}, {title: t.STORE_HOST, dataIndex: 'storeHost', key: 'storeHost', align: 'center'},
]; ];
const consumeColumns = [ const consumeColumns = [
{ title: t.BEGIN_TIMESTAMP, dataIndex: 'beginTimestamp', key: 'beginTimestamp', align: 'center', render: (text) => text < 0 ? 'N/A' : moment(text).format('YYYY-MM-DD HH:mm:ss.SSS') }, {
{ title: t.END_TIMESTAMP, dataIndex: 'endTimestamp', key: 'endTimestamp', align: 'center', render: (text) => text < 0 ? 'N/A' : moment(text).format('YYYY-MM-DD HH:mm:ss.SSS') }, title: t.BEGIN_TIMESTAMP,
{ title: t.COST_TIME, dataIndex: 'costTime', key: 'costTime', align: 'center', render: (text) => text < 0 ? 'N/A' : `${text === 0 ? '<1' : text}ms` }, dataIndex: 'beginTimestamp',
{ title: t.STATUS, dataIndex: 'status', key: 'status', align: 'center', render: (text) => <Tag color={text === 'SUCCESS' ? 'green' : (text === 'FAILED' ? 'red' : 'default')}>{text}</Tag> }, key: 'beginTimestamp',
{ title: t.RETRY_TIMES, dataIndex: 'retryTimes', key: 'retryTimes', align: 'center', render: (text) => text < 0 ? 'N/A' : text }, align: 'center',
render: (text) => text < 0 ? 'N/A' : moment(text).format('YYYY-MM-DD HH:mm:ss.SSS')
},
{
title: t.END_TIMESTAMP,
dataIndex: 'endTimestamp',
key: 'endTimestamp',
align: 'center',
render: (text) => text < 0 ? 'N/A' : moment(text).format('YYYY-MM-DD HH:mm:ss.SSS')
},
{
title: t.COST_TIME,
dataIndex: 'costTime',
key: 'costTime',
align: 'center',
render: (text) => text < 0 ? 'N/A' : `${text === 0 ? '<1' : text}ms`
},
{
title: t.STATUS,
dataIndex: 'status',
key: 'status',
align: 'center',
render: (text) => <Tag
color={text === 'SUCCESS' ? 'green' : (text === 'FAILED' ? 'red' : 'default')}>{text}</Tag>
},
{
title: t.RETRY_TIMES,
dataIndex: 'retryTimes',
key: 'retryTimes',
align: 'center',
render: (text) => text < 0 ? 'N/A' : text
},
{title: t.CLIENT_HOST, dataIndex: 'clientHost', key: 'clientHost', align: 'center'}, {title: t.CLIENT_HOST, dataIndex: 'clientHost', key: 'clientHost', align: 'center'},
{title: t.STORE_HOST, dataIndex: 'storeHost', key: 'storeHost', align: 'center'}, {title: t.STORE_HOST, dataIndex: 'storeHost', key: 'storeHost', align: 'center'},
]; ];
return ( return (
<div style={{padding: '20px', backgroundColor: '#f0f2f5'}}> <div style={{padding: '20px', backgroundColor: '#f0f2f5'}}>
<div style={{ marginBottom: '20px', borderRadius: '8px', overflow: 'hidden', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)' }}> <div style={{
marginBottom: '20px',
borderRadius: '8px',
overflow: 'hidden',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)'
}}>
<Collapse defaultActiveKey={['messageTraceGraph']} expandIconPosition="right"> <Collapse defaultActiveKey={['messageTraceGraph']} expandIconPosition="right">
<Panel header={<Typography.Title level={3} style={{ margin: 0, color: '#333' }}>{t.MESSAGE_TRACE_GRAPH}</Typography.Title>} key="messageTraceGraph"> <Panel header={<Typography.Title level={3} style={{
<div ref={messageTraceGraphRef} style={{ height: 500, width: '100%', backgroundColor: '#fff', padding: '10px' }}> margin: 0,
color: '#333'
}}>{t.MESSAGE_TRACE_GRAPH}</Typography.Title>} key="messageTraceGraph">
<div ref={messageTraceGraphRef}
style={{height: 500, width: '100%', backgroundColor: '#fff', padding: '10px'}}>
{/* ECharts message trace graph will be rendered here */} {/* ECharts message trace graph will be rendered here */}
{(!producerNode && subscriptionNodeList.length === 0) && ( {(!producerNode && subscriptionNodeList.length === 0) && (
<Text type="secondary" style={{ display: 'block', textAlign: 'center', marginTop: '150px' }}>{t.TRACE_GRAPH_PLACEHOLDER}</Text> <Text type="secondary" style={{
display: 'block',
textAlign: 'center',
marginTop: '150px'
}}>{t.TRACE_GRAPH_PLACEHOLDER}</Text>
)} )}
</div> </div>
</Panel> </Panel>
</Collapse> </Collapse>
</div> </div>
<div style={{ marginBottom: '20px', borderRadius: '8px', overflow: 'hidden', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)' }}> <div style={{
marginBottom: '20px',
borderRadius: '8px',
overflow: 'hidden',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)'
}}>
<Collapse defaultActiveKey={['sendMessageTrace']} expandIconPosition="right"> <Collapse defaultActiveKey={['sendMessageTrace']} expandIconPosition="right">
<Panel header={<Typography.Title level={3} style={{ margin: 0, color: '#333' }}>{t.SEND_MESSAGE_TRACE}</Typography.Title>} key="sendMessageTrace"> <Panel header={<Typography.Title level={3} style={{
margin: 0,
color: '#333'
}}>{t.SEND_MESSAGE_TRACE}</Typography.Title>} key="sendMessageTrace">
{!producerNode ? ( {!producerNode ? (
<Paragraph style={{ padding: '16px', textAlign: 'center', color: '#666' }}>{t.NO_PRODUCER_TRACE_DATA}</Paragraph> <Paragraph style={{
padding: '16px',
textAlign: 'center',
color: '#666'
}}>{t.NO_PRODUCER_TRACE_DATA}</Paragraph>
) : ( ) : (
<div style={{padding: '16px', backgroundColor: '#fff'}}> <div style={{padding: '16px', backgroundColor: '#fff'}}>
<Typography.Title level={4} style={{marginBottom: '20px'}}> <Typography.Title level={4} style={{marginBottom: '20px'}}>
{t.SEND_MESSAGE_INFO} : ( {t.MESSAGE_ID} <Text strong copyable>{producerNode.msgId}</Text> ) {t.SEND_MESSAGE_INFO} : ( {t.MESSAGE_ID} <Text strong
copyable>{producerNode.msgId}</Text> )
</Typography.Title> </Typography.Title>
<Form layout="vertical" colon={false}> <Form layout="vertical" colon={false}>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: '16px' }}> <div style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))',
gap: '16px'
}}>
<Form.Item label={<Text strong>{t.TOPIC}</Text>}> <Form.Item label={<Text strong>{t.TOPIC}</Text>}>
<Input value={producerNode.topic} readOnly/> <Input value={producerNode.topic} readOnly/>
</Form.Item> </Form.Item>
@@ -381,13 +462,19 @@ const MessageTraceDetailViewDialog = ({ ngDialogData }) => {
</Form.Item> </Form.Item>
<Form.Item label={<Text strong>{t.BEGIN_TIMESTAMP}</Text>}> <Form.Item label={<Text strong>{t.BEGIN_TIMESTAMP}</Text>}>
<Input value={moment(producerNode.traceNode.beginTimestamp).format('YYYY-MM-DD HH:mm:ss.SSS')} readOnly /> <Input
value={moment(producerNode.traceNode.beginTimestamp).format('YYYY-MM-DD HH:mm:ss.SSS')}
readOnly/>
</Form.Item> </Form.Item>
<Form.Item label={<Text strong>{t.END_TIMESTAMP}</Text>}> <Form.Item label={<Text strong>{t.END_TIMESTAMP}</Text>}>
<Input value={moment(producerNode.traceNode.endTimestamp).format('YYYY-MM-DD HH:mm:ss.SSS')} readOnly /> <Input
value={moment(producerNode.traceNode.endTimestamp).format('YYYY-MM-DD HH:mm:ss.SSS')}
readOnly/>
</Form.Item> </Form.Item>
<Form.Item label={<Text strong>{t.COST_TIME}</Text>}> <Form.Item label={<Text strong>{t.COST_TIME}</Text>}>
<Input value={`${producerNode.traceNode.costTime === 0 ? '<1' : producerNode.traceNode.costTime}ms`} readOnly /> <Input
value={`${producerNode.traceNode.costTime === 0 ? '<1' : producerNode.traceNode.costTime}ms`}
readOnly/>
</Form.Item> </Form.Item>
<Form.Item label={<Text strong>{t.MSG_TYPE}</Text>}> <Form.Item label={<Text strong>{t.MSG_TYPE}</Text>}>
<Input value={producerNode.traceNode.msgType} readOnly/> <Input value={producerNode.traceNode.msgType} readOnly/>
@@ -410,7 +497,8 @@ const MessageTraceDetailViewDialog = ({ ngDialogData }) => {
{producerNode.transactionNodeList && producerNode.transactionNodeList.length > 0 && ( {producerNode.transactionNodeList && producerNode.transactionNodeList.length > 0 && (
<div style={{marginTop: '30px'}}> <div style={{marginTop: '30px'}}>
<Typography.Title level={4} style={{ marginBottom: '15px' }}>{t.CHECK_TRANSACTION_INFO}:</Typography.Title> <Typography.Title level={4}
style={{marginBottom: '15px'}}>{t.CHECK_TRANSACTION_INFO}:</Typography.Title>
<Table <Table
columns={transactionColumns} columns={transactionColumns}
dataSource={producerNode.transactionNodeList} dataSource={producerNode.transactionNodeList}
@@ -430,9 +518,16 @@ const MessageTraceDetailViewDialog = ({ ngDialogData }) => {
<div style={{borderRadius: '8px', overflow: 'hidden', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)'}}> <div style={{borderRadius: '8px', overflow: 'hidden', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)'}}>
<Collapse defaultActiveKey={['consumeMessageTrace']} expandIconPosition="right"> <Collapse defaultActiveKey={['consumeMessageTrace']} expandIconPosition="right">
<Panel header={<Typography.Title level={3} style={{ margin: 0, color: '#333' }}>{t.CONSUME_MESSAGE_TRACE}</Typography.Title>} key="consumeMessageTrace"> <Panel header={<Typography.Title level={3} style={{
margin: 0,
color: '#333'
}}>{t.CONSUME_MESSAGE_TRACE}</Typography.Title>} key="consumeMessageTrace">
{subscriptionNodeList.length === 0 ? ( {subscriptionNodeList.length === 0 ? (
<Paragraph style={{ padding: '16px', textAlign: 'center', color: '#666' }}>{t.NO_CONSUMER_TRACE_DATA}</Paragraph> <Paragraph style={{
padding: '16px',
textAlign: 'center',
color: '#666'
}}>{t.NO_CONSUMER_TRACE_DATA}</Paragraph>
) : ( ) : (
<div style={{padding: '16px', backgroundColor: '#fff'}}> <div style={{padding: '16px', backgroundColor: '#fff'}}>
{subscriptionNodeList.map(subscriptionNode => ( {subscriptionNodeList.map(subscriptionNode => (
@@ -443,7 +538,9 @@ const MessageTraceDetailViewDialog = ({ ngDialogData }) => {
ghost ghost
> >
<Panel <Panel
header={<Typography.Title level={4} style={{ margin: 0 }}>{t.SUBSCRIPTION_GROUP}: <Text strong>{subscriptionNode.subscriptionGroup}</Text></Typography.Title>} header={<Typography.Title level={4}
style={{margin: 0}}>{t.SUBSCRIPTION_GROUP}: <Text
strong>{subscriptionNode.subscriptionGroup}</Text></Typography.Title>}
key={subscriptionNode.subscriptionGroup} key={subscriptionNode.subscriptionGroup}
> >
<Table <Table

View File

@@ -16,8 +16,8 @@
*/ */
import React, {useEffect, useState} from 'react'; import React, {useEffect, useState} from 'react';
import { Layout, Menu, Dropdown, Button, Drawer, Grid, Space } from 'antd'; import {Button, Drawer, Dropdown, Grid, Layout, Menu, Space} from 'antd';
import {GlobalOutlined, DownOutlined, UserOutlined, MenuOutlined, BgColorsOutlined} from '@ant-design/icons'; import {BgColorsOutlined, DownOutlined, GlobalOutlined, MenuOutlined, UserOutlined} from '@ant-design/icons';
import {useLocation, useNavigate} from 'react-router-dom'; import {useLocation, useNavigate} from 'react-router-dom';
import {useLanguage} from '../i18n/LanguageContext'; import {useLanguage} from '../i18n/LanguageContext';
import {useTheme} from "../store/context/ThemeContext"; import {useTheme} from "../store/context/ThemeContext";
@@ -102,7 +102,7 @@ const Navbar = ({ rmqVersion = true, showAcl = true}) => {
{key: 'message', label: t.MESSAGE}, {key: 'message', label: t.MESSAGE},
{key: 'dlqMessage', label: t.DLQ_MESSAGE}, {key: 'dlqMessage', label: t.DLQ_MESSAGE},
{key: 'messageTrace', label: t.MESSAGETRACE}, {key: 'messageTrace', label: t.MESSAGETRACE},
...(showAcl ? [{ key: 'acl', label: t.WHITE_LIST }] : []), ...(showAcl ? [{key: 'acl', label: t.ACL_MANAGEMENT}] : []),
]; ];
// Determine if it's a small screen (e.g., less than md) // Determine if it's a small screen (e.g., less than md)

View File

@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { Input, Select, Tag, Space } from 'antd'; import {Input, Select, Space, Tag} from 'antd';
import {PlusOutlined} from '@ant-design/icons'; import {PlusOutlined} from '@ant-design/icons';
import React, {useState} from 'react'; import React, {useState} from 'react';

View File

@@ -16,7 +16,7 @@
*/ */
import {Input, Select} from 'antd'; import {Input, Select} from 'antd';
import React, { useState, useEffect } from 'react'; import React, {useEffect, useState} from 'react';
const {Option} = Select; const {Option} = Select;

View File

@@ -15,8 +15,8 @@
* limitations under the License. * limitations under the License.
*/ */
import React, { useState, useEffect } from 'react'; import React, {useEffect, useState} from 'react';
import { Modal, Table, Spin } from 'antd'; import {Modal, Spin, Table} from 'antd';
import {remoteApi} from '../../api/remoteApi/remoteApi'; import {remoteApi} from '../../api/remoteApi/remoteApi';
import {useLanguage} from '../../i18n/LanguageContext'; import {useLanguage} from '../../i18n/LanguageContext';

View File

@@ -16,12 +16,22 @@
*/ */
import React, {useEffect, useState} from 'react'; import React, {useEffect, useState} from 'react';
import { Button, Descriptions, Form, Input, Select, Switch, message } from 'antd'; import {Button, Descriptions, Form, Input, message, Select, Switch} from 'antd';
import {remoteApi} from '../../api/remoteApi/remoteApi'; // 确保路径正确 import {remoteApi} from '../../api/remoteApi/remoteApi'; // 确保路径正确
const {Option} = Select; const {Option} = Select;
const ConsumerConfigItem = ({ initialConfig, isAddConfig, group, brokerName, allBrokerList, allClusterNames,onCancel, onSuccess, t }) => { const ConsumerConfigItem = ({
initialConfig,
isAddConfig,
group,
brokerName,
allBrokerList,
allClusterNames,
onCancel,
onSuccess,
t
}) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const [currentBrokerName, setCurrentBrokerName] = useState(brokerName); const [currentBrokerName, setCurrentBrokerName] = useState(brokerName);

View File

@@ -15,8 +15,8 @@
* limitations under the License. * limitations under the License.
*/ */
import React, { useState, useEffect } from 'react'; import React, {useEffect, useState} from 'react';
import { Modal, Table, Spin } from 'antd'; import {Modal, Spin, Table} from 'antd';
import {remoteApi} from '../../api/remoteApi/remoteApi'; import {remoteApi} from '../../api/remoteApi/remoteApi';
import {useLanguage} from '../../i18n/LanguageContext'; import {useLanguage} from '../../i18n/LanguageContext';

View File

@@ -15,8 +15,8 @@
* limitations under the License. * limitations under the License.
*/ */
import React, { useState, useEffect } from 'react'; import React, {useEffect, useState} from 'react';
import { Modal, Spin, Checkbox, Button, notification } from 'antd'; import {Button, Checkbox, Modal, notification, Spin} from 'antd';
import {remoteApi} from '../../api/remoteApi/remoteApi'; import {remoteApi} from '../../api/remoteApi/remoteApi';
import {useLanguage} from '../../i18n/LanguageContext'; import {useLanguage} from '../../i18n/LanguageContext';

View File

@@ -63,7 +63,11 @@ const ConsumerViewDialog = ({ visible, onClose, topic, consumerData, consumerGro
bordered bordered
pagination={false} pagination={false}
showHeader={false} showHeader={false}
dataSource={[{ consumerGroup, diffTotal: consumeDetail.diffTotal, lastTimestamp: consumeDetail.lastTimestamp }]} dataSource={[{
consumerGroup,
diffTotal: consumeDetail.diffTotal,
lastTimestamp: consumeDetail.lastTimestamp
}]}
columns={[ columns={[
{title: t.SUBSCRIPTION_GROUP, dataIndex: 'consumerGroup', key: 'consumerGroup'}, {title: t.SUBSCRIPTION_GROUP, dataIndex: 'consumerGroup', key: 'consumerGroup'},
{title: t.DELAY, dataIndex: 'diffTotal', key: 'diffTotal'}, {title: t.DELAY, dataIndex: 'diffTotal', key: 'diffTotal'},

View File

@@ -30,7 +30,11 @@ const RouterViewDialog = ({ visible, onClose, topic, routeData, t }) => {
key: 'brokerAddrs', key: 'brokerAddrs',
render: (_, record) => ( render: (_, record) => (
<Table <Table
dataSource={Object.entries(record.brokerAddrs || []).map(([key, value]) => ({ key, idx: key, address: value }))} dataSource={Object.entries(record.brokerAddrs || []).map(([key, value]) => ({
key,
idx: key,
address: value
}))}
columns={[ columns={[
{title: 'Index', dataIndex: 'idx', key: 'idx'}, {title: 'Index', dataIndex: 'idx', key: 'idx'},
{title: 'Address', dataIndex: 'address', key: 'address'}, {title: 'Address', dataIndex: 'address', key: 'address'},

View File

@@ -61,5 +61,4 @@ const SendResultDialog = ({ visible, onClose, result, t }) => {
}; };
export default SendResultDialog; export default SendResultDialog;

View File

@@ -18,7 +18,14 @@
import {Button, Form, message, Modal, Select} from "antd"; import {Button, Form, message, Modal, Select} from "antd";
import React, {useEffect, useState} from "react"; import React, {useEffect, useState} from "react";
const SkipMessageAccumulateDialog = ({ visible, onClose, topic, allConsumerGroupList, handleSkipMessageAccumulate, t }) => { const SkipMessageAccumulateDialog = ({
visible,
onClose,
topic,
allConsumerGroupList,
handleSkipMessageAccumulate,
t
}) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const [selectedConsumerGroup, setSelectedConsumerGroup] = useState([]); const [selectedConsumerGroup, setSelectedConsumerGroup] = useState([]);

View File

@@ -17,7 +17,7 @@
// TopicSingleModifyForm.js // TopicSingleModifyForm.js
import React, {useEffect} from "react"; import React, {useEffect} from "react";
import {Button, Form, Input, Select, Divider, Row, Col} from "antd"; import {Button, Col, Divider, Form, Input, Row, Select} from "antd";
const TopicSingleModifyForm = ({ const TopicSingleModifyForm = ({
initialData, initialData,
@@ -68,7 +68,8 @@ const TopicSingleModifyForm = ({
return ( return (
<div style={{paddingBottom: 24}}> <div style={{paddingBottom: 24}}>
{bIsUpdate && <Divider orientation="left">{`${t.TOPIC_CONFIG} - ${initialData.brokerNameList ? initialData.brokerNameList.join(', ') : t.UNKNOWN_BROKER}`}</Divider>} {bIsUpdate && <Divider
orientation="left">{`${t.TOPIC_CONFIG} - ${initialData.brokerNameList ? initialData.brokerNameList.join(', ') : t.UNKNOWN_BROKER}`}</Divider>}
<Row justify="center"> {/* 使用 Row 居中内容 */} <Row justify="center"> {/* 使用 Row 居中内容 */}
<Col span={16}> {/* 表单内容占据 12 栅格宽度,并自动居中 */} <Col span={16}> {/* 表单内容占据 12 栅格宽度,并自动居中 */}
<Form <Form

View File

@@ -15,12 +15,13 @@
* limitations under the License. * limitations under the License.
*/ */
import React, { createContext, useState, useContext } from 'react'; import React, {createContext, useContext, useState} from 'react';
import {translations} from '../i18n'; import {translations} from '../i18n';
const LanguageContext = createContext({ const LanguageContext = createContext({
lang: 'en', lang: 'en',
setLang: () => {}, setLang: () => {
},
t: translations['en'], // 当前语言的文本资源 t: translations['en'], // 当前语言的文本资源
}); });

View File

@@ -156,7 +156,6 @@ export const translations = {
"NO_MATCH_RESULT": "没有查到符合条件的结果", "NO_MATCH_RESULT": "没有查到符合条件的结果",
"BATCH_RESEND": "批量重发", "BATCH_RESEND": "批量重发",
"BATCH_EXPORT": "批量导出", "BATCH_EXPORT": "批量导出",
"WHITE_LIST":"白名单",
"ACCOUNT_INFO": "账户信息", "ACCOUNT_INFO": "账户信息",
"IS_ADMIN": "是否管理员", "IS_ADMIN": "是否管理员",
"DEFAULT_TOPIC_PERM": "topic默认权限", "DEFAULT_TOPIC_PERM": "topic默认权限",
@@ -279,6 +278,12 @@ export const translations = {
"ENTER_IP_HINT": "请输入 IP 地址,按回车键添加,支持 IPv4、IPv6 和 CIDR", "ENTER_IP_HINT": "请输入 IP 地址,按回车键添加,支持 IPv4、IPv6 和 CIDR",
"PLEASE_ENTER_DECISION": "请输入决策!", "PLEASE_ENTER_DECISION": "请输入决策!",
"MENU": "菜单", "MENU": "菜单",
"SELECT_PROXY": "选择代理",
"ENABLE_PROXY": "启用代理",
"PROXY_DISABLED": "代理禁用",
"PROXY_ENABLED": "代理启用",
"BROKER_OVERVIEW": "Broker概览",
"TOTAL_MSG_RECEIVED_TODAY": "今天接收的总消息数",
}, },
en: { en: {
"DEFAULT": "Default", "DEFAULT": "Default",
@@ -325,7 +330,7 @@ export const translations = {
"ADDRESS": "Address", "ADDRESS": "Address",
"VERSION": "Version", "VERSION": "Version",
"PRO_MSG_TPS": "Produce Message TPS", "PRO_MSG_TPS": "Produce Message TPS",
"CUS_MSG_TPS": "Consume Message TPS", "CUS_MSG_TPS": "Consumer Message TPS",
"YESTERDAY_PRO_COUNT": "Yesterday Produce Count", "YESTERDAY_PRO_COUNT": "Yesterday Produce Count",
"YESTERDAY_CUS_COUNT": "Yesterday Consume Count", "YESTERDAY_CUS_COUNT": "Yesterday Consume Count",
"TODAY_PRO_COUNT": "Today Produce Count", "TODAY_PRO_COUNT": "Today Produce Count",
@@ -421,7 +426,6 @@ export const translations = {
"NO_MATCH_RESULT": "no match result", "NO_MATCH_RESULT": "no match result",
"BATCH_RESEND": "batchReSend", "BATCH_RESEND": "batchReSend",
"BATCH_EXPORT": "batchExport", "BATCH_EXPORT": "batchExport",
"WHITE_LIST":"White List",
"ACCOUNT_INFO": "Account Info", "ACCOUNT_INFO": "Account Info",
"IS_ADMIN": "Is Admin", "IS_ADMIN": "Is Admin",
"DEFAULT_TOPIC_PERM": "Default Topic Permission", "DEFAULT_TOPIC_PERM": "Default Topic Permission",
@@ -536,6 +540,13 @@ export const translations = {
"ENTER_IP_HINT": "Please enter IP address, press Enter to add. Supports IPv4, IPv6, and CIDR.", "ENTER_IP_HINT": "Please enter IP address, press Enter to add. Supports IPv4, IPv6, and CIDR.",
"PLEASE_ENTER_DECISION": "Please enter decision!", "PLEASE_ENTER_DECISION": "Please enter decision!",
"MENU": "Menu", "MENU": "Menu",
"SELECT_PROXY": "Select Proxy",
"ENABLE_PROXY": "Enable Proxy",
"PROXY_DISABLED": "Proxy Disabled",
"PROXY_ENABLED": "Proxy Enabled",
"BROKER_OVERVIEW": "Broker Overview",
"TOTAL_MSG_RECEIVED_TODAY": "Total messages received today",
} }

View File

@@ -27,7 +27,6 @@ import store from './store';
const root = ReactDOM.createRoot(document.getElementById('root')); const root = ReactDOM.createRoot(document.getElementById('root'));
root.render( root.render(
<LanguageProvider> <LanguageProvider>
<React.StrictMode> <React.StrictMode>
<AntdApp> <AntdApp>
@@ -37,7 +36,6 @@ root.render(
</AntdApp> </AntdApp>
</React.StrictMode> </React.StrictMode>
</LanguageProvider> </LanguageProvider>
); );
// If you want to start measuring performance in your app, pass a function // If you want to start measuring performance in your app, pass a function

View File

@@ -16,7 +16,7 @@
*/ */
import React, {useCallback, useEffect, useState} from 'react'; import React, {useCallback, useEffect, useState} from 'react';
import {Button, Checkbox, Input, message, notification, Spin, Table} from 'antd'; import {Button, Checkbox, Input, message, notification, Select, Spin, Switch, Table} from 'antd';
import {useLanguage} from '../../i18n/LanguageContext'; import {useLanguage} from '../../i18n/LanguageContext';
import {remoteApi} from '../../api/remoteApi/remoteApi'; import {remoteApi} from '../../api/remoteApi/remoteApi';
import ClientInfoModal from "../../components/consumer/ClientInfoModal"; import ClientInfoModal from "../../components/consumer/ClientInfoModal";
@@ -46,6 +46,27 @@ const ConsumerGroupList = () => {
const [messageApi, msgContextHolder] = message.useMessage(); const [messageApi, msgContextHolder] = message.useMessage();
const [notificationApi, notificationContextHolder] = notification.useNotification(); const [notificationApi, notificationContextHolder] = notification.useNotification();
const [proxyEnabled, setProxyEnabled] = useState(() => {
try {
const storedValue = localStorage.getItem('proxyEnabled');
return storedValue ? JSON.parse(storedValue) : false;
} catch (error) {
console.error("Failed to read proxyEnabled from localStorage:", error);
return false;
}
});
const [selectedProxy, setSelectedProxy] = useState(() => {
try {
const storedValue = localStorage.getItem('selectedProxy');
return storedValue || undefined;
} catch (error) {
console.error("Failed to read selectedProxy from localStorage:", error);
return undefined;
}
});
const [proxyOptions ,setProxyOptions]= useState([]);
const [paginationConf, setPaginationConf] = useState({ const [paginationConf, setPaginationConf] = useState({
current: 1, current: 1,
pageSize: 10, pageSize: 10,
@@ -60,7 +81,12 @@ const ConsumerGroupList = () => {
const loadConsumerGroups = useCallback(async (currentPage) => { const loadConsumerGroups = useCallback(async (currentPage) => {
setLoading(true); setLoading(true);
try { try {
const response = await remoteApi.queryConsumerGroupList(false); var response;
if(!proxyEnabled){
response = await remoteApi.queryConsumerGroupList(false);
}else{
response = await remoteApi.queryConsumerGroupList(false, selectedProxy);
}
if (response.status === 0) { if (response.status === 0) {
setAllConsumerGroupList(response.data); setAllConsumerGroupList(response.data);
if (currentPage != null) { if (currentPage != null) {
@@ -87,7 +113,6 @@ const ConsumerGroupList = () => {
}; };
const filterList = useCallback((currentPage, data) => { const filterList = useCallback((currentPage, data) => {
// 排序处理
let sortedData = [...data]; let sortedData = [...data];
if (sortConfig.sortKey) { if (sortConfig.sortKey) {
sortedData.sort((a, b) => { sortedData.sort((a, b) => {
@@ -153,6 +178,48 @@ const ConsumerGroupList = () => {
filterList(paginationConf.current, sortedList); filterList(paginationConf.current, sortedList);
}, [sortConfig, allConsumerGroupList, paginationConf.current]); }, [sortConfig, allConsumerGroupList, paginationConf.current]);
const fetchProxyList = useCallback(async () => {
remoteApi.queryProxyHomePage((resp) => {
setLoading(false);
if (resp.status === 0) {
const {proxyAddrList, currentProxyAddr} = resp.data;
const options = proxyAddrList.map(proxyAddress => ({
label: proxyAddress,
value: proxyAddress,
}));
setProxyOptions(options || []);
setSelectedProxy(prevSelectedProxy => {
if (prevSelectedProxy) {
return prevSelectedProxy;
}
if (options.length > 0) {
return options[0].value;
}
return undefined;
});
} else {
notificationApi.error({message: resp.errMsg || t.FETCH_PROXY_LIST_FAILED, duration: 2});
}
});
}, [t]);
useEffect(() => {
localStorage.setItem('proxyEnabled', JSON.stringify(proxyEnabled));
}, [proxyEnabled]);
useEffect(() => {
if (selectedProxy) {
localStorage.setItem('selectedProxy', selectedProxy);
} else {
localStorage.removeItem('selectedProxy');
}
}, [selectedProxy]);
useEffect(() => {
fetchProxyList();
}, []);
useEffect(() => { useEffect(() => {
loadConsumerGroups(); loadConsumerGroups();
}, [loadConsumerGroups]); }, [loadConsumerGroups]);
@@ -391,14 +458,16 @@ const ConsumerGroupList = () => {
{notificationContextHolder} {notificationContextHolder}
<div style={{ padding: '20px' }}> <div style={{ padding: '20px' }}>
<Spin spinning={loading} tip={t.LOADING}> <Spin spinning={loading} tip={t.LOADING}>
<div style={{marginBottom: '20px'}}> <div style={{ marginBottom: '20px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{display: 'flex', alignItems: 'center', gap: '15px'}}> {/* 左侧:筛选和操作按钮 */}
<div style={{ display: 'flex', alignItems: 'center', gap: '15px', flexWrap: 'wrap' }}>
<div style={{ display: 'flex', alignItems: 'center' }}> <div style={{ display: 'flex', alignItems: 'center' }}>
<label style={{marginRight: '8px'}}>{t.SUBSCRIPTION_GROUP}:</label> <label style={{ marginRight: '8px', whiteSpace: 'nowrap' }}>{t.SUBSCRIPTION_GROUP}:</label>
<Input <Input
style={{ width: '200px' }} style={{ width: '200px' }}
value={filterStr} value={filterStr}
onChange={(e) => handleFilterInputChange(e.target.value)} onChange={(e) => handleFilterInputChange(e.target.value)}
placeholder="输入订阅组名称"
/> />
</div> </div>
<Checkbox checked={filterNormal} <Checkbox checked={filterNormal}
@@ -423,12 +492,35 @@ const ConsumerGroupList = () => {
<Button type="primary" onClick={handleRefreshConsumerData}> <Button type="primary" onClick={handleRefreshConsumerData}>
{t.REFRESH} {t.REFRESH}
</Button> </Button>
{/*<Switch*/} </div>
{/* checked={intervalProcessSwitch}*/}
{/* onChange={(checked) => setIntervalProcessSwitch(checked)}*/} {/* 右侧:代理选项 */}
{/* checkedChildren={t.AUTO_REFRESH}*/} <div style={{ display: 'flex', alignItems: 'center', gap: '15px' }}>
{/* unCheckedChildren={t.AUTO_REFRESH}*/} <label style={{ marginRight: '8px', whiteSpace: 'nowrap' }}>{t.SELECT_PROXY}:</label>
{/*/>*/} <Select
style={{ width: '220px' }}
placeholder={t.SELECT_PROXY}
onChange={(value) => setSelectedProxy(value)}
value={selectedProxy}
options={proxyOptions}
disabled={!proxyEnabled}
allowClear
/>
<label style={{ marginRight: '8px', whiteSpace: 'nowrap' }}>{t.ENABLE_PROXY}:</label>
<Switch
checked={proxyEnabled}
onChange={(checked) => {
setProxyEnabled(checked);
if (!checked) {
setSelectedProxy(undefined);
messageApi.info(t.PROXY_DISABLED);
} else {
messageApi.info(t.PROXY_ENABLED);
}
}}
checkedChildren={t.ENABLED}
unCheckedChildren={t.DISABLED}
/>
</div> </div>
</div> </div>
@@ -443,6 +535,7 @@ const ConsumerGroupList = () => {
/> />
</Spin> </Spin>
{/* 模态框组件保持不变 */}
<ClientInfoModal <ClientInfoModal
visible={showClientInfo} visible={showClientInfo}
group={selectedGroup} group={selectedGroup}

View File

@@ -250,17 +250,18 @@ const DashboardPage = () => {
const brokerAddrTable = resp.data.clusterInfo.brokerAddrTable; // Corrected to brokerAddrTable const brokerAddrTable = resp.data.clusterInfo.brokerAddrTable; // Corrected to brokerAddrTable
const brokerDetail = resp.data.brokerServer; const brokerDetail = resp.data.brokerServer;
const clusterMap = tools.generateBrokerMap(brokerDetail, clusterAddrTable, brokerAddrTable); const clusterMap = tools.generateBrokerMap(brokerDetail, clusterAddrTable, brokerAddrTable);
console.log(brokerAddrTable)
let brokerArray = []; let brokerArray = [];
Object.values(clusterMap).forEach(brokersInCluster => { Object.values(clusterMap).forEach(brokersInCluster => {
brokerArray = brokerArray.concat(brokersInCluster); brokerArray = brokerArray.concat(brokersInCluster);
}); });
// Update broker table data const newData = brokerArray.map(broker => ({
setBrokerTableData(brokerArray.map(broker => ({
...broker, ...broker,
key: broker.brokerName // Ant Design Table needs a unique key key: broker.brokerName,
}))); }));
console.log("即将设置的数据:", newData); // 先打印
setBrokerTableData(newData); // 再设置状态
brokerArray.sort((firstBroker, lastBroker) => { brokerArray.sort((firstBroker, lastBroker) => {
const firstTotalMsg = parseFloat(firstBroker.msgGetTotalTodayNow || 0); const firstTotalMsg = parseFloat(firstBroker.msgGetTotalTodayNow || 0);
@@ -347,7 +348,7 @@ const DashboardPage = () => {
const brokerColumns = [ const brokerColumns = [
{title: t.BROKER_NAME, dataIndex: 'brokerName', key: 'brokerName'}, {title: t.BROKER_NAME, dataIndex: 'brokerName', key: 'brokerName'},
{title: t.BROKER_ADDR, dataIndex: 'brokerAddress', key: 'brokerAddress'}, {title: t.BROKER_ADDR, dataIndex: 'address', key: 'address'},
{ {
title: t.TOTAL_MSG_RECEIVED_TODAY, title: t.TOTAL_MSG_RECEIVED_TODAY,
dataIndex: 'msgGetTotalTodayNow', dataIndex: 'msgGetTotalTodayNow',

View File

@@ -179,7 +179,6 @@ const DlqMessageQueryPage = () => {
return; return;
} }
setLoading(true); setLoading(true);
// console.log("根据Message ID查询DLQ消息:", { msgId: messageId, consumerGroup: selectedConsumerGroup });
try { try {
const resp = await remoteApi.viewMessage(messageId, DLQ_GROUP_TOPIC_PREFIX + selectedConsumerGroup); const resp = await remoteApi.viewMessage(messageId, DLQ_GROUP_TOPIC_PREFIX + selectedConsumerGroup);
if (resp.status === 0) { if (resp.status === 0) {
@@ -323,7 +322,6 @@ const DlqMessageQueryPage = () => {
msgId: message.properties.ORIGIN_MESSAGE_ID, msgId: message.properties.ORIGIN_MESSAGE_ID,
consumerGroup: selectedConsumerGroup, consumerGroup: selectedConsumerGroup,
})); }));
// console.log(`批量重发DLQ消息到 ${selectedConsumerGroup}:`, messagesToResend);
try { try {
const resp = await remoteApi.batchResendDlqMessage(messagesToResend); const resp = await remoteApi.batchResendDlqMessage(messagesToResend);
if (resp.status === 0) { if (resp.status === 0) {
@@ -355,7 +353,6 @@ const DlqMessageQueryPage = () => {
message: t.ERROR, message: t.ERROR,
description: t.BATCH_RESEND_FAILED, description: t.BATCH_RESEND_FAILED,
}); });
console.error("批量重发失败:", error);
} finally { } finally {
setLoading(false); setLoading(false);
} }

View File

@@ -16,7 +16,7 @@
*/ */
import React from 'react'; import React from 'react';
import { Form, Input, Button, message, Typography } from 'antd'; import {Button, Form, Input, message, Typography} from 'antd';
import {remoteApi} from "../../api/remoteApi/remoteApi"; import {remoteApi} from "../../api/remoteApi/remoteApi";
const {Title} = Typography; const {Title} = Typography;

View File

@@ -146,7 +146,6 @@ const MessageQueryPage = () => {
message: t.ERROR, message: t.ERROR,
description: t.QUERY_FAILED, description: t.QUERY_FAILED,
}); });
console.error("查询失败:", error);
} finally { } finally {
setLoading(false); setLoading(false);
} }
@@ -182,7 +181,6 @@ const MessageQueryPage = () => {
message: t.ERROR, message: t.ERROR,
description: t.QUERY_FAILED, description: t.QUERY_FAILED,
}); });
console.error("查询失败:", error);
} finally { } finally {
setLoading(false); setLoading(false);
} }
@@ -241,7 +239,6 @@ const MessageQueryPage = () => {
message: t.ERROR, message: t.ERROR,
description: t.RESEND_FAILED, description: t.RESEND_FAILED,
}); });
console.error("重发失败:", error);
} finally { } finally {
setLoading(false); setLoading(false);
// Optionally, you might want to refresh the message detail after resend // Optionally, you might want to refresh the message detail after resend
@@ -455,7 +452,6 @@ const MessageQueryPage = () => {
</Button> </Button>
</Form.Item> </Form.Item>
</Form> </Form>
{/* Message ID 查询结果通常直接弹窗显示,这里不需要表格 */}
</div> </div>
</TabPane> </TabPane>
</Tabs> </Tabs>

View File

@@ -15,8 +15,8 @@
* limitations under the License. * limitations under the License.
*/ */
import React, { useState, useEffect } from 'react'; import React, {useEffect, useState} from 'react';
import { Select, Button, Switch, Input, Typography, Space, message } from 'antd'; import {Button, Input, message, Select, Space, Switch, Typography} from 'antd';
import {remoteApi} from '../../api/remoteApi/remoteApi'; import {remoteApi} from '../../api/remoteApi/remoteApi';
const {Title} = Typography; const {Title} = Typography;

View File

@@ -15,8 +15,8 @@
* limitations under the License. * limitations under the License.
*/ */
import React, { useState, useEffect } from 'react'; import React, {useEffect, useState} from 'react';
import { Modal, Button, Select, Input, Card, Row, Col, notification, Spin } from 'antd'; import {Button, Card, Col, Input, Modal, notification, Row, Select, Spin} from 'antd';
import {useLanguage} from '../../i18n/LanguageContext'; import {useLanguage} from '../../i18n/LanguageContext';
import {remoteApi} from "../../api/remoteApi/remoteApi"; import {remoteApi} from "../../api/remoteApi/remoteApi";
@@ -71,7 +71,10 @@ const ProxyManager = () => {
const handleAddProxyAddr = () => { const handleAddProxyAddr = () => {
if (!newProxyAddr.trim()) { if (!newProxyAddr.trim()) {
notificationApi.warning({ message: t.INPUT_PROXY_ADDR_REQUIRED || "Please input a new proxy address.", duration: 2 }); notificationApi.warning({
message: t.INPUT_PROXY_ADDR_REQUIRED || "Please input a new proxy address.",
duration: 2
});
return; return;
} }
setLoading(true); setLoading(true);
@@ -167,7 +170,8 @@ const ProxyManager = () => {
)) ))
) : ( ) : (
<tr> <tr>
<td colSpan="2" style={{ textAlign: 'center' }}>{t.NO_CONFIG_DATA || "No configuration data available."}</td> <td colSpan="2"
style={{textAlign: 'center'}}>{t.NO_CONFIG_DATA || "No configuration data available."}</td>
</tr> </tr>
)} )}
</tbody> </tbody>

View File

@@ -173,7 +173,6 @@ const DeployHistoryList = () => {
}; };
const filterList = (currentPage) => { const filterList = (currentPage) => {
const lowExceptStr = filterStr.toLowerCase(); const lowExceptStr = filterStr.toLowerCase();
const canShowList = allTopicList.filter((topic, index) => { const canShowList = allTopicList.filter((topic, index) => {

View File

@@ -15,8 +15,8 @@
* limitations under the License. * limitations under the License.
*/ */
import {useEffect} from 'react'; import {useEffect} from 'react';
import { useSelector, useDispatch } from 'react-redux'; import {useDispatch, useSelector} from 'react-redux';
import { themes, defaultTheme } from '../../assets/styles/theme'; import {defaultTheme, themes} from '../../assets/styles/theme';
import {setTheme} from '../actions/themeActions'; import {setTheme} from '../actions/themeActions';
export const useTheme = () => { export const useTheme = () => {

View File

@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import { createStore,combineReducers } from 'redux'; import {combineReducers, createStore} from 'redux';
import themeReducer from './reducers/themeReducer'; import themeReducer from './reducers/themeReducer';
// 组合所有的 reducers // 组合所有的 reducers

View File

@@ -16,7 +16,8 @@
~ limitations under the License. ~ limitations under the License.
--> -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<groupId>org.apache</groupId> <groupId>org.apache</groupId>

View File

@@ -91,6 +91,10 @@ public class RMQConfigure {
@Getter @Getter
private Integer clientCallbackExecutorThreads = 4; private Integer clientCallbackExecutorThreads = 4;
@Setter
@Getter
private String authMode = "file";
public void setProxyAddrs(List<String> proxyAddrs) { public void setProxyAddrs(List<String> proxyAddrs) {
this.proxyAddrs = proxyAddrs; this.proxyAddrs = proxyAddrs;
if (CollectionUtils.isNotEmpty(proxyAddrs)) { if (CollectionUtils.isNotEmpty(proxyAddrs)) {
@@ -112,10 +116,12 @@ public class RMQConfigure {
logger.info("setNameSrvAddrByProperty nameSrvAddr={}", namesrvAddr); logger.info("setNameSrvAddrByProperty nameSrvAddr={}", namesrvAddr);
} }
} }
public boolean isACLEnabled() { public boolean isACLEnabled() {
return !(StringUtils.isAnyBlank(this.accessKey, this.secretKey) || return !(StringUtils.isAnyBlank(this.accessKey, this.secretKey) ||
StringUtils.isAnyEmpty(this.accessKey, this.secretKey)); StringUtils.isAnyEmpty(this.accessKey, this.secretKey));
} }
public String getRocketMqDashboardDataPath() { public String getRocketMqDashboardDataPath() {
return dataPath; return dataPath;
} }

View File

@@ -23,6 +23,7 @@ import org.apache.rocketmq.dashboard.model.request.UserUpdateRequest;
import org.apache.rocketmq.dashboard.service.impl.AclServiceImpl; import org.apache.rocketmq.dashboard.service.impl.AclServiceImpl;
import org.apache.rocketmq.remoting.protocol.body.UserInfo; import org.apache.rocketmq.remoting.protocol.body.UserInfo;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@@ -31,24 +32,23 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List; import java.util.List;
@RestController @Controller
@RequestMapping("/acl") @RequestMapping("/acl")
public class AclController { public class AclController {
@Autowired @Autowired
private AclServiceImpl aclService; private AclServiceImpl aclService;
@GetMapping("/listUsers") @GetMapping("/users.query")
@ResponseBody @ResponseBody
public List<UserInfo> listUsers(@RequestParam(required = false) String brokerAddress) { public List<UserInfo> listUsers(@RequestParam(required = false) String brokerAddress) {
return aclService.listUsers(brokerAddress); return aclService.listUsers(brokerAddress);
} }
@GetMapping("/listAcls") @GetMapping("/acls.query")
@ResponseBody @ResponseBody
public Object listAcls( public Object listAcls(
@RequestParam(required = false) String brokerAddress, @RequestParam(required = false) String brokerAddress,
@@ -56,34 +56,34 @@ public class AclController {
return aclService.listAcls(brokerAddress, searchParam); return aclService.listAcls(brokerAddress, searchParam);
} }
@PostMapping("/createAcl") @PostMapping("/createAcl.do")
@ResponseBody @ResponseBody
public Object createAcl(@RequestBody PolicyRequest request) { public Object createAcl(@RequestBody PolicyRequest request) {
aclService.createAcl(request); aclService.createAcl(request);
return true; return true;
} }
@DeleteMapping("/deleteUser") @DeleteMapping("/deleteUser.do")
@ResponseBody @ResponseBody
public Object deleteUser(@RequestParam(required = false) String brokerAddress, @RequestParam String username) { public Object deleteUser(@RequestParam(required = false) String brokerAddress, @RequestParam String username) {
aclService.deleteUser(brokerAddress, username); aclService.deleteUser(brokerAddress, username);
return true; return true;
} }
@RequestMapping(value = "/updateUser", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") @RequestMapping(value = "/updateUser.do", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
@ResponseBody @ResponseBody
public Object updateUser(@RequestBody UserUpdateRequest request) { public Object updateUser(@RequestBody UserUpdateRequest request) {
aclService.updateUser(request.getBrokerAddress(), request.getUserInfo()); aclService.updateUser(request.getBrokerAddress(), request.getUserInfo());
return true; return true;
} }
@PostMapping("/createUser") @PostMapping("/createUser.do")
public Object createUser(@RequestBody UserCreateRequest request) { public Object createUser(@RequestBody UserCreateRequest request) {
aclService.createUser(request.getBrokerAddress(), request.getUserInfo()); aclService.createUser(request.getBrokerAddress(), request.getUserInfo());
return true; return true;
} }
@DeleteMapping("/deleteAcl") @DeleteMapping("/deleteAcl.do")
public Object deleteAcl( public Object deleteAcl(
@RequestParam(required = false) String brokerAddress, @RequestParam(required = false) String brokerAddress,
@RequestParam String subject, @RequestParam String subject,
@@ -92,7 +92,7 @@ public class AclController {
return true; return true;
} }
@RequestMapping(value = "/updateAcl", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") @RequestMapping(value = "/updateAcl.do", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
@ResponseBody @ResponseBody
public Object updateAcl(@RequestBody PolicyRequest request) { public Object updateAcl(@RequestBody PolicyRequest request) {
aclService.updateAcl(request); aclService.updateAcl(request);

View File

@@ -31,6 +31,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
public class ProxyController { public class ProxyController {
@Resource @Resource
private ProxyService proxyService; private ProxyService proxyService;
@RequestMapping(value = "/homePage.query", method = RequestMethod.GET) @RequestMapping(value = "/homePage.query", method = RequestMethod.GET)
@ResponseBody @ResponseBody
public Object homePage() { public Object homePage() {

View File

@@ -72,7 +72,8 @@ public class TestController {
new Thread(new Runnable() { new Thread(new Runnable() {
@Override public void run() { @Override
public void run() {
int i = 0; int i = 0;
while (true) { while (true) {
@@ -85,13 +86,11 @@ public class TestController {
Thread.sleep(1000L); Thread.sleep(1000L);
SendResult sendResult = producer.send(msg); SendResult sendResult = producer.send(msg);
logger.info("sendMessage={}", JsonUtil.obj2String(sendResult)); logger.info("sendMessage={}", JsonUtil.obj2String(sendResult));
} } catch (Exception e) {
catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
try { try {
Thread.sleep(1000); Thread.sleep(1000);
} } catch (Exception ignore) {
catch (Exception ignore) {
} }
} }
} }

View File

@@ -29,7 +29,6 @@ import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
@WebFilter(urlPatterns = "/*", filterName = "httpBasicAuthorizedFilter") @WebFilter(urlPatterns = "/*", filterName = "httpBasicAuthorizedFilter")
public class HttpBasicAuthorizedFilter implements Filter { public class HttpBasicAuthorizedFilter implements Filter {

View File

@@ -25,7 +25,9 @@ import java.util.Map;
public class MessageView { public class MessageView {
/** from MessageExt **/ /**
* from MessageExt
**/
private int queueId; private int queueId;
private int storeSize; private int storeSize;
private long queueOffset; private long queueOffset;
@@ -41,13 +43,17 @@ public class MessageView {
private long preparedTransactionOffset; private long preparedTransactionOffset;
/**from MessageExt**/ /**from MessageExt**/
/** from Message **/ /**
* from Message
**/
private String topic; private String topic;
private int flag; private int flag;
private Map<String, String> properties; private Map<String, String> properties;
private String messageBody; // body private String messageBody; // body
/** from Message **/ /**
* from Message
**/
public static MessageView fromMessageExt(MessageExt messageExt) { public static MessageView fromMessageExt(MessageExt messageExt) {
MessageView messageView = new MessageView(); MessageView messageView = new MessageView();

View File

@@ -19,8 +19,8 @@ package org.apache.rocketmq.dashboard.model;
import org.hibernate.validator.constraints.Range; import org.hibernate.validator.constraints.Range;
public class User { public class User {
public static final int ORDINARY = 0; public static final int SUPER = 0;
public static final int ADMIN = 1; public static final int NORMAL = 1;
private long id; private long id;
private String name; private String name;

View File

@@ -15,6 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.apache.rocketmq.dashboard.model.request; package org.apache.rocketmq.dashboard.model.request;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import java.util.List; import java.util.List;
@@ -24,7 +25,9 @@ public class TopicConfigInfo {
private List<String> clusterNameList; private List<String> clusterNameList;
private List<String> brokerNameList; private List<String> brokerNameList;
/** topicConfig */ /**
* topicConfig
*/
private String topicName; private String topicName;
private int writeQueueNums; private int writeQueueNums;
private int readQueueNums; private int readQueueNums;
@@ -32,6 +35,7 @@ public class TopicConfigInfo {
private boolean order; private boolean order;
private String messageType; private String messageType;
public List<String> getClusterNameList() { public List<String> getClusterNameList() {
return clusterNameList; return clusterNameList;
} }
@@ -40,8 +44,9 @@ public class TopicConfigInfo {
this.clusterNameList = clusterNameList; this.clusterNameList = clusterNameList;
} }
/** topicConfig */ /**
* topicConfig
*/
public List<String> getBrokerNameList() { public List<String> getBrokerNameList() {
@@ -102,8 +107,6 @@ public class TopicConfigInfo {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) if (this == o)

View File

@@ -17,9 +17,11 @@
package org.apache.rocketmq.dashboard.model.request; package org.apache.rocketmq.dashboard.model.request;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@Data @Data
@AllArgsConstructor
public class UserInfoParam { public class UserInfoParam {
private String username; private String username;
private String password; private String password;

View File

@@ -29,6 +29,7 @@ import java.util.Set;
public abstract class AbstractCommonService { public abstract class AbstractCommonService {
@Resource @Resource
protected MQAdminExt mqAdminExt; protected MQAdminExt mqAdminExt;
protected final Set<String> changeToBrokerNameSet(Map<String, Set<String>> clusterAddrTable, protected final Set<String> changeToBrokerNameSet(Map<String, Set<String>> clusterAddrTable,
List<String> clusterNameList, List<String> brokerNameList) { List<String> clusterNameList, List<String> brokerNameList) {
Set<String> finalBrokerNameList = Sets.newHashSet(); Set<String> finalBrokerNameList = Sets.newHashSet();
@@ -37,8 +38,7 @@ public abstract class AbstractCommonService {
for (String clusterName : clusterNameList) { for (String clusterName : clusterNameList) {
finalBrokerNameList.addAll(clusterAddrTable.get(clusterName)); finalBrokerNameList.addAll(clusterAddrTable.get(clusterName));
} }
} } catch (Exception e) {
catch (Exception e) {
Throwables.throwIfUnchecked(e); Throwables.throwIfUnchecked(e);
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -39,8 +39,7 @@ public interface MessageService {
/** /**
* @param topic * @param topic
* @param begin * @param begin
* @param end * @param end org.apache.rocketmq.tools.command.message.PrintMessageSubCommand
* org.apache.rocketmq.tools.command.message.PrintMessageSubCommand
*/ */
List<MessageView> queryMessageByTopic(final String topic, final long begin, List<MessageView> queryMessageByTopic(final String topic, final long begin,
final long end); final long end);
@@ -54,6 +53,4 @@ public interface MessageService {
MessagePage queryMessageByPage(MessageQuery query); MessagePage queryMessageByPage(MessageQuery query);
} }

View File

@@ -94,7 +94,8 @@ public class MQAdminExtImpl implements MQAdminExt {
private Logger logger = LoggerFactory.getLogger(MQAdminExtImpl.class); private Logger logger = LoggerFactory.getLogger(MQAdminExtImpl.class);
public MQAdminExtImpl() {} public MQAdminExtImpl() {
}
@Override @Override
@@ -134,8 +135,7 @@ public class MQAdminExtImpl implements MQAdminExt {
RemotingCommand response = null; RemotingCommand response = null;
try { try {
response = remotingClient.invokeSync(addr, request, 8000); response = remotingClient.invokeSync(addr, request, 8000);
} } catch (Exception err) {
catch (Exception err) {
Throwables.throwIfUnchecked(err); Throwables.throwIfUnchecked(err);
throw new RuntimeException(err); throw new RuntimeException(err);
} }
@@ -566,17 +566,20 @@ public class MQAdminExtImpl implements MQAdminExt {
} }
// 4.0.0 added // 4.0.0 added
@Override public void updateNameServerConfig(Properties properties, @Override
public void updateNameServerConfig(Properties properties,
List<String> list) throws InterruptedException, RemotingConnectException, UnsupportedEncodingException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, MQBrokerException { List<String> list) throws InterruptedException, RemotingConnectException, UnsupportedEncodingException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, MQBrokerException {
} }
@Override public Map<String, Properties> getNameServerConfig( @Override
public Map<String, Properties> getNameServerConfig(
List<String> list) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException, UnsupportedEncodingException { List<String> list) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException, UnsupportedEncodingException {
return null; return null;
} }
@Override public QueryConsumeQueueResponseBody queryConsumeQueue(String brokerAddr, String topic, @Override
public QueryConsumeQueueResponseBody queryConsumeQueue(String brokerAddr, String topic,
int queueId, long index, int count, int queueId, long index, int count,
String consumerGroup) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException { String consumerGroup) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException {
return null; return null;
@@ -588,7 +591,8 @@ public class MQAdminExtImpl implements MQAdminExt {
} }
@Override public boolean resumeCheckHalfMessage(String topic, @Override
public boolean resumeCheckHalfMessage(String topic,
String msgId) throws RemotingException, MQClientException, InterruptedException, MQBrokerException { String msgId) throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
return false; return false;
} }

View File

@@ -58,7 +58,7 @@ public abstract class AbstractFileStore {
} }
} }
abstract void load(InputStream inputStream); protected abstract void load(InputStream inputStream);
private void load() { private void load() {
load(null); load(null);

View File

@@ -212,7 +212,6 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
} else { } else {
consumeInfo.setSubGroupType(subscriptionGroupTable.get(consumerGroup).isConsumeMessageOrderly() ? "FIFO" : "NORMAL"); consumeInfo.setSubGroupType(subscriptionGroupTable.get(consumerGroup).isConsumeMessageOrderly() ? "FIFO" : "NORMAL");
} }
consumeInfo.setGroup(consumerGroup);
consumeInfo.setUpdateTime(new Date()); consumeInfo.setUpdateTime(new Date());
groupConsumeInfoList.add(consumeInfo); groupConsumeInfoList.add(consumeInfo);
} catch (Exception e) { } catch (Exception e) {
@@ -270,6 +269,7 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
logger.warn("examineConsumeStats or examineConsumerConnectionInfo exception, " logger.warn("examineConsumeStats or examineConsumerConnectionInfo exception, "
+ consumerGroup, e); + consumerGroup, e);
} }
groupConsumeInfo.setGroup(consumerGroup);
return groupConsumeInfo; return groupConsumeInfo;
} }

View File

@@ -96,6 +96,7 @@ public class DashboardCollectServiceImpl implements DashboardCollectService {
public LoadingCache<String, List<String>> getBrokerMap() { public LoadingCache<String, List<String>> getBrokerMap() {
return brokerMap; return brokerMap;
} }
@Override @Override
public LoadingCache<String, List<String>> getTopicMap() { public LoadingCache<String, List<String>> getTopicMap() {
return topicMap; return topicMap;
@@ -106,8 +107,7 @@ public class DashboardCollectServiceImpl implements DashboardCollectService {
List<String> strings; List<String> strings;
try { try {
strings = Files.readLines(file, Charsets.UTF_8); strings = Files.readLines(file, Charsets.UTF_8);
} } catch (IOException e) {
catch (IOException e) {
Throwables.throwIfUnchecked(e); Throwables.throwIfUnchecked(e);
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -34,6 +34,7 @@ public class DashboardServiceImpl implements DashboardService {
@Resource @Resource
private DashboardCollectService dashboardCollectService; private DashboardCollectService dashboardCollectService;
/** /**
* @param date format yyyy-MM-dd * @param date format yyyy-MM-dd
*/ */

View File

@@ -17,13 +17,10 @@
package org.apache.rocketmq.dashboard.service.impl; package org.apache.rocketmq.dashboard.service.impl;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.apache.rocketmq.dashboard.config.RMQConfigure;
import org.apache.rocketmq.dashboard.service.LoginService; import org.apache.rocketmq.dashboard.service.LoginService;
import org.apache.rocketmq.dashboard.service.UserService; import org.apache.rocketmq.dashboard.service.strategy.UserContext;
import org.apache.rocketmq.dashboard.service.provider.UserInfoProvider;
import org.apache.rocketmq.dashboard.util.UserInfoContext; import org.apache.rocketmq.dashboard.util.UserInfoContext;
import org.apache.rocketmq.dashboard.util.WebUtil; import org.apache.rocketmq.dashboard.util.WebUtil;
import org.apache.rocketmq.remoting.protocol.body.UserInfo; import org.apache.rocketmq.remoting.protocol.body.UserInfo;
@@ -33,34 +30,29 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@Service @Service
public class LoginServiceImpl implements LoginService { public class LoginServiceImpl implements LoginService {
private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource
private RMQConfigure rmqConfigure;
@Autowired @Autowired
private UserService userService; private UserContext userContext;
@Autowired
private UserInfoProvider userInfoProvider;
@Override @Override
public boolean login(HttpServletRequest request, HttpServletResponse response) { public boolean login(HttpServletRequest request, HttpServletResponse response) {
String username = (String) WebUtil.getValueFromSession(request, WebUtil.USER_NAME); String username = (String) WebUtil.getValueFromSession(request, WebUtil.USER_NAME);
if (username != null) { if (username != null) {
UserInfo userInfo = userInfoProvider.getUserInfoByUsername(username); UserInfo userInfo = userContext.queryByUsername(username);
if (userInfo == null) { if (userInfo == null) {
auth(request, response); auth(request, response);
return false; return false;
} }
UserInfoContext.set(WebUtil.USER_NAME, userInfo); UserInfoContext.set(WebUtil.USER_NAME, userInfo);
return true; return true;
} }
auth(request, response); auth(request, response);
return false; return false;
@@ -69,11 +61,7 @@ public class LoginServiceImpl implements LoginService {
protected void auth(HttpServletRequest request, HttpServletResponse response) { protected void auth(HttpServletRequest request, HttpServletResponse response) {
try { try {
String url = WebUtil.getUrl(request); String url = WebUtil.getUrl(request);
try { url = URLEncoder.encode(url, StandardCharsets.UTF_8);
url = URLEncoder.encode(url, "UTF-8");
} catch (UnsupportedEncodingException e) {
logger.error("url encode:{}", url, e);
}
logger.debug("redirect url : {}", url); logger.debug("redirect url : {}", url);
WebUtil.redirect(response, request, "/#/login?redirect=" + url); WebUtil.redirect(response, request, "/#/login?redirect=" + url);
} catch (IOException e) { } catch (IOException e) {

View File

@@ -33,7 +33,6 @@ import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
import org.apache.rocketmq.client.consumer.PullResult; import org.apache.rocketmq.client.consumer.PullResult;
import org.apache.rocketmq.client.consumer.PullStatus; import org.apache.rocketmq.client.consumer.PullStatus;
import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.Pair; import org.apache.rocketmq.common.Pair;
import org.apache.rocketmq.common.message.MessageClientIDSetter; import org.apache.rocketmq.common.message.MessageClientIDSetter;
import org.apache.rocketmq.common.message.MessageExt; import org.apache.rocketmq.common.message.MessageExt;
@@ -74,6 +73,9 @@ import java.util.stream.Collectors;
@Service @Service
public class MessageServiceImpl implements MessageService { public class MessageServiceImpl implements MessageService {
@Resource
private AutoCloseConsumerWrapper autoCloseConsumerWrapper;
private Logger logger = LoggerFactory.getLogger(MessageServiceImpl.class); private Logger logger = LoggerFactory.getLogger(MessageServiceImpl.class);
private static final Cache<String, List<QueueOffsetInfo>> CACHE = CacheBuilder.newBuilder() private static final Cache<String, List<QueueOffsetInfo>> CACHE = CacheBuilder.newBuilder()
@@ -128,8 +130,8 @@ public class MessageServiceImpl implements MessageService {
if (isEnableAcl) { if (isEnableAcl) {
rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey())); rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey()));
} }
AutoCloseConsumerWrapper consumerWrapper = new AutoCloseConsumerWrapper();
DefaultMQPullConsumer consumer = consumerWrapper.getConsumer(rpcHook, configure.isUseTLS()); DefaultMQPullConsumer consumer = autoCloseConsumerWrapper.getConsumer(rpcHook, configure.isUseTLS());
List<MessageView> messageViewList = Lists.newArrayList(); List<MessageView> messageViewList = Lists.newArrayList();
try { try {
String subExpression = "*"; String subExpression = "*";
@@ -262,8 +264,8 @@ public class MessageServiceImpl implements MessageService {
if (isEnableAcl) { if (isEnableAcl) {
rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey())); rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey()));
} }
AutoCloseConsumerWrapper consumerWrapper = new AutoCloseConsumerWrapper();
DefaultMQPullConsumer consumer = consumerWrapper.getConsumer(rpcHook, configure.isUseTLS()); DefaultMQPullConsumer consumer = autoCloseConsumerWrapper.getConsumer(rpcHook, configure.isUseTLS());
long total = 0; long total = 0;
List<QueueOffsetInfo> queueOffsetInfos = new ArrayList<>(); List<QueueOffsetInfo> queueOffsetInfos = new ArrayList<>();
@@ -402,8 +404,8 @@ public class MessageServiceImpl implements MessageService {
if (isEnableAcl) { if (isEnableAcl) {
rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey())); rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey()));
} }
AutoCloseConsumerWrapper consumerWrapper = new AutoCloseConsumerWrapper();
DefaultMQPullConsumer consumer = consumerWrapper.getConsumer(rpcHook, configure.isUseTLS()); DefaultMQPullConsumer consumer = autoCloseConsumerWrapper.getConsumer(rpcHook, configure.isUseTLS());
List<MessageView> messageViews = new ArrayList<>(); List<MessageView> messageViews = new ArrayList<>();
long offset = query.getPageNum() * query.getPageSize(); long offset = query.getPageNum() * query.getPageSize();
@@ -541,9 +543,9 @@ public class MessageServiceImpl implements MessageService {
} }
} }
public DefaultMQPullConsumer buildDefaultMQPullConsumer(RPCHook rpcHook, boolean useTLS) { // public DefaultMQPullConsumer buildDefaultMQPullConsumer(RPCHook rpcHook, boolean useTLS) {
DefaultMQPullConsumer consumer = new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, rpcHook); // DefaultMQPullConsumer consumer = new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, rpcHook);
consumer.setUseTLS(useTLS); // consumer.setUseTLS(useTLS);
return consumer; // return consumer;
} // }
} }

View File

@@ -81,8 +81,7 @@ public class MonitorServiceImpl implements MonitorService {
private void writeDataJsonToFile(String path, String dataStr) { private void writeDataJsonToFile(String path, String dataStr) {
try { try {
MixAll.string2File(dataStr, path); MixAll.string2File(dataStr, path);
} } catch (Exception e) {
catch (Exception e) {
Throwables.throwIfUnchecked(e); Throwables.throwIfUnchecked(e);
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -33,8 +33,7 @@ public class ProducerServiceImpl implements ProducerService {
public ProducerConnection getProducerConnection(String producerGroup, String topic) { public ProducerConnection getProducerConnection(String producerGroup, String topic) {
try { try {
return mqAdminExt.examineProducerConnectionInfo(producerGroup, topic); return mqAdminExt.examineProducerConnectionInfo(producerGroup, topic);
} } catch (Exception e) {
catch (Exception e) {
Throwables.throwIfUnchecked(e); Throwables.throwIfUnchecked(e);
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -17,29 +17,26 @@
package org.apache.rocketmq.dashboard.service.impl; package org.apache.rocketmq.dashboard.service.impl;
import jakarta.annotation.Resource;
import org.apache.rocketmq.auth.authentication.enums.UserType; import org.apache.rocketmq.auth.authentication.enums.UserType;
import org.apache.rocketmq.dashboard.admin.UserMQAdminPoolManager; import org.apache.rocketmq.dashboard.admin.UserMQAdminPoolManager;
import org.apache.rocketmq.dashboard.config.RMQConfigure;
import org.apache.rocketmq.dashboard.model.User; import org.apache.rocketmq.dashboard.model.User;
import org.apache.rocketmq.dashboard.service.UserService; import org.apache.rocketmq.dashboard.service.UserService;
import org.apache.rocketmq.dashboard.service.provider.UserInfoProvider; import org.apache.rocketmq.dashboard.service.strategy.UserContext;
import org.apache.rocketmq.remoting.protocol.body.UserInfo; import org.apache.rocketmq.remoting.protocol.body.UserInfo;
import org.apache.rocketmq.tools.admin.MQAdminExt; import org.apache.rocketmq.tools.admin.MQAdminExt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service @Service
public class UserServiceImpl implements UserService { public class UserServiceImpl implements UserService {
private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class); private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
@Resource
private RMQConfigure configure;
@Autowired @Autowired
private UserInfoProvider userInfoProvider; private UserContext userContext;
@Autowired @Autowired
private UserMQAdminPoolManager userMQAdminPoolManager; private UserMQAdminPoolManager userMQAdminPoolManager;
@@ -47,7 +44,7 @@ public class UserServiceImpl implements UserService {
@Override @Override
public User queryByName(String name) { public User queryByName(String name) {
UserInfo userInfo = userInfoProvider.getUserInfoByUsername(name); UserInfo userInfo = userContext.queryByUsername(name);
if (userInfo == null) { if (userInfo == null) {
return null; return null;
} }

View File

@@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.dashboard.service.strategy;
import lombok.AllArgsConstructor;
import org.apache.rocketmq.dashboard.service.ClusterInfoService;
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
import org.apache.rocketmq.remoting.protocol.route.BrokerData;
import org.apache.rocketmq.tools.admin.MQAdminExt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
@AllArgsConstructor
public class AclUserStrategy implements UserStrategy {
private static final Logger log = LoggerFactory.getLogger(AclUserStrategy.class);
private final MQAdminExt mqAdminExt;
private final ClusterInfoService clusterInfoService;
@Override
public UserInfo getUserInfoByUsername(String username) {
ClusterInfo clusterInfo = clusterInfoService.get();
if (clusterInfo == null || clusterInfo.getBrokerAddrTable() == null || clusterInfo.getBrokerAddrTable().isEmpty()) {
log.warn("Cluster information is not available or has no broker addresses.");
return null;
}
for (BrokerData brokerLiveInfo : clusterInfo.getBrokerAddrTable().values()) {
if (brokerLiveInfo == null || brokerLiveInfo.getBrokerAddrs() == null || brokerLiveInfo.getBrokerAddrs().isEmpty()) {
continue;
}
String brokerAddr = brokerLiveInfo.getBrokerAddrs().get(0L); // Assuming 0L is the primary address
if (brokerAddr == null) {
continue;
}
try {
UserInfo userInfo = mqAdminExt.getUser(brokerAddr, username);
if (userInfo != null) {
return userInfo;
}
} catch (Exception e) {
log.warn("Failed to get user {} from broker {}. Trying next broker if available. Error: {}", username, brokerAddr, e.getMessage());
}
}
return null;
}
}

View File

@@ -0,0 +1,119 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.dashboard.service.strategy;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotNull;
import org.apache.rocketmq.dashboard.config.RMQConfigure;
import org.apache.rocketmq.dashboard.exception.ServiceException;
import org.apache.rocketmq.dashboard.model.User;
import org.apache.rocketmq.dashboard.service.impl.AbstractFileStore;
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
import java.io.FileReader;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class FileUserStrategy implements UserStrategy, InitializingBean {
@Resource
private RMQConfigure configure;
private FileBasedUserInfoStore fileBasedUserInfoStore;
@Override
public void afterPropertiesSet() throws Exception {
if (configure.isLoginRequired()) {
fileBasedUserInfoStore = new FileBasedUserInfoStore(configure);
}
}
@Override
public UserInfo getUserInfoByUsername(String username) {
User user = fileBasedUserInfoStore.queryByUsernameAndPassword(username);
if (user != null) {
return UserInfo.of(user.getName(), user.getPassword(), user.getType() == 0 ? "normal" : "super");
}
return null;
}
public static class FileBasedUserInfoStore extends AbstractFileStore {
private static final String FILE_NAME = "users.properties";
private static Map<String, User> userMap = new ConcurrentHashMap<>();
public FileBasedUserInfoStore(RMQConfigure configure) {
super(configure, FILE_NAME);
}
@Override
public void load(InputStream inputStream) {
Properties prop = new Properties();
try {
if (inputStream == null) {
prop.load(new FileReader(filePath));
} else {
prop.load(inputStream);
}
} catch (Exception e) {
log.error("load user.properties failed", e);
throw new ServiceException(0, String.format("Failed to load loginUserInfo property file: %s", filePath));
}
Map<String, User> loadUserMap = new HashMap<>();
String[] arrs;
int role;
for (String key : prop.stringPropertyNames()) {
String v = prop.getProperty(key);
if (v == null)
continue;
arrs = v.split(",", 2);
if (arrs.length == 0) {
continue;
} else if (arrs.length == 1) {
role = 0;
} else {
role = Integer.parseInt(arrs[1].trim());
}
loadUserMap.put(key, new User(key, arrs[0].trim(), role));
}
userMap.clear();
userMap.putAll(loadUserMap);
}
public User queryByName(String name) {
return userMap.get(name);
}
public User queryByUsernameAndPassword(@NotNull String username) {
User user = queryByName(username);
if (user != null) {
return user.cloneOne();
}
return null;
}
}
}

View File

@@ -0,0 +1,54 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.dashboard.service.strategy;
import jakarta.annotation.PostConstruct;
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
public class UserContext {
private UserStrategy userStrategy;
@Autowired
private Map<String, UserStrategy> userStrategies;
@Value("${rocketmq.config.authMode}")
private String authMode;
@PostConstruct
public void init() {
switch (authMode.toLowerCase()) {
case "acl":
this.userStrategy = userStrategies.get("aclUserStrategy");
break;
case "file":
default:
this.userStrategy = userStrategies.get("fileUserStrategy");
break;
}
}
public UserInfo queryByUsername(String username) {
return userStrategy.getUserInfoByUsername(username);
}
}

View File

@@ -15,11 +15,10 @@
* limitations under the License. * limitations under the License.
*/ */
package org.apache.rocketmq.dashboard.admin; package org.apache.rocketmq.dashboard.service.strategy;
import org.apache.rocketmq.tools.admin.MQAdminExt; import org.apache.rocketmq.remoting.protocol.body.UserInfo;
@FunctionalInterface public interface UserStrategy {
public interface MQAdminExtCallback<T> { UserInfo getUserInfoByUsername(String username);
T doInMQAdminExt(MQAdminExt mqAdminExt) throws Exception;
} }

View File

@@ -23,6 +23,7 @@ import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.remoting.RPCHook; import org.apache.rocketmq.remoting.RPCHook;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
@@ -32,9 +33,10 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@Component
public class AutoCloseConsumerWrapper { public class AutoCloseConsumerWrapper {
private final Logger logger = LoggerFactory.getLogger(GlobalRestfulResponseBodyAdvice.class); private final Logger logger = LoggerFactory.getLogger(AutoCloseConsumerWrapper.class);
private static final AtomicReference<DefaultMQPullConsumer> CONSUMER_REF = new AtomicReference<>(); private static final AtomicReference<DefaultMQPullConsumer> CONSUMER_REF = new AtomicReference<>();
private final AtomicBoolean isTaskScheduled = new AtomicBoolean(false); private final AtomicBoolean isTaskScheduled = new AtomicBoolean(false);
@@ -77,7 +79,10 @@ public class AutoCloseConsumerWrapper {
protected DefaultMQPullConsumer createNewConsumer(RPCHook rpcHook, Boolean useTLS) { protected DefaultMQPullConsumer createNewConsumer(RPCHook rpcHook, Boolean useTLS) {
return new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, rpcHook) { return new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, rpcHook) {
{ setUseTLS(useTLS); } }; {
setUseTLS(useTLS);
}
};
} }
private void startIdleCheckTask() { private void startIdleCheckTask() {

View File

@@ -37,8 +37,7 @@ public class GlobalExceptionHandler {
if (ex instanceof ServiceException) { if (ex instanceof ServiceException) {
logger.error("Occur service exception: {}", ex.getMessage()); logger.error("Occur service exception: {}", ex.getMessage());
value = new JsonResult<Object>(((ServiceException) ex).getCode(), ex.getMessage()); value = new JsonResult<Object>(((ServiceException) ex).getCode(), ex.getMessage());
} } else {
else {
logger.error("op=global_exception_handler_print_error", ex); logger.error("op=global_exception_handler_print_error", ex);
value = new JsonResult<Object>(-1, ex.getMessage() == null ? ex.toString() : ex.getMessage()); value = new JsonResult<Object>(-1, ex.getMessage() == null ? ex.toString() : ex.getMessage());
} }

View File

@@ -47,8 +47,7 @@ public class GlobalRestfulResponseBodyAdvice implements ResponseBodyAdvice<Objec
JsonResult value; JsonResult value;
if (obj instanceof JsonResult) { if (obj instanceof JsonResult) {
value = (JsonResult) obj; value = (JsonResult) obj;
} } else {
else {
value = new JsonResult(obj); value = new JsonResult(obj);
} }
return value; return value;

View File

@@ -87,8 +87,7 @@ public class DashboardCollectTask {
CollectTaskRunnble collectTask = new CollectTaskRunnble(topic, mqAdminExt, dashboardCollectService); CollectTaskRunnble collectTask = new CollectTaskRunnble(topic, mqAdminExt, dashboardCollectService);
collectExecutor.submit(collectTask); collectExecutor.submit(collectTask);
} }
} } catch (Exception err) {
catch (Exception err) {
Throwables.throwIfUnchecked(err); Throwables.throwIfUnchecked(err);
throw new RuntimeException(err); throw new RuntimeException(err);
} }
@@ -100,7 +99,6 @@ public class DashboardCollectTask {
} }
@Scheduled(cron = "0 0/1 * * * ?") @Scheduled(cron = "0 0/1 * * * ?")
public void collectBroker() { public void collectBroker() {
if (!rmqConfigure.isEnableDashBoardCollect()) { if (!rmqConfigure.isEnableDashBoardCollect()) {
@@ -139,8 +137,7 @@ public class DashboardCollectTask {
dashboardCollectService.getBrokerMap().put(entry.getValue(), list); dashboardCollectService.getBrokerMap().put(entry.getValue(), list);
} }
log.debug("Broker Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getBrokerMap().asMap())); log.debug("Broker Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getBrokerMap().asMap()));
} } catch (Exception e) {
catch (Exception e) {
Throwables.throwIfUnchecked(e); Throwables.throwIfUnchecked(e);
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -152,12 +149,10 @@ public class DashboardCollectTask {
} }
try { try {
return mqAdminExt.fetchBrokerRuntimeStats(brokerAddr); return mqAdminExt.fetchBrokerRuntimeStats(brokerAddr);
} } catch (Exception e) {
catch (Exception e) {
try { try {
Thread.sleep(1000); Thread.sleep(1000);
} } catch (InterruptedException e1) {
catch (InterruptedException e1) {
Throwables.throwIfUnchecked(e1); Throwables.throwIfUnchecked(e1);
throw new RuntimeException(e1); throw new RuntimeException(e1);
} }
@@ -189,16 +184,14 @@ public class DashboardCollectTask {
Map<String, List<String>> topicFileMap; Map<String, List<String>> topicFileMap;
if (brokerFile.exists()) { if (brokerFile.exists()) {
brokerFileMap = dashboardCollectService.jsonDataFile2map(brokerFile); brokerFileMap = dashboardCollectService.jsonDataFile2map(brokerFile);
} } else {
else {
brokerFileMap = Maps.newHashMap(); brokerFileMap = Maps.newHashMap();
Files.createParentDirs(brokerFile); Files.createParentDirs(brokerFile);
} }
if (topicFile.exists()) { if (topicFile.exists()) {
topicFileMap = dashboardCollectService.jsonDataFile2map(topicFile); topicFileMap = dashboardCollectService.jsonDataFile2map(topicFile);
} } else {
else {
topicFileMap = Maps.newHashMap(); topicFileMap = Maps.newHashMap();
Files.createParentDirs(topicFile); Files.createParentDirs(topicFile);
} }
@@ -211,8 +204,7 @@ public class DashboardCollectTask {
log.debug("Broker Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getBrokerMap().asMap())); log.debug("Broker Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getBrokerMap().asMap()));
log.debug("Topic Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getTopicMap().asMap())); log.debug("Topic Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getTopicMap().asMap()));
} } catch (IOException e) {
catch (IOException e) {
Throwables.throwIfUnchecked(e); Throwables.throwIfUnchecked(e);
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -224,8 +216,7 @@ public class DashboardCollectTask {
Map<String, List<String>> resultMap = Maps.newHashMap(); Map<String, List<String>> resultMap = Maps.newHashMap();
if (fileMap.size() == 0) { if (fileMap.size() == 0) {
resultMap = newMap; resultMap = newMap;
} } else {
else {
for (Map.Entry<String, List<String>> entry : fileMap.entrySet()) { for (Map.Entry<String, List<String>> entry : fileMap.entrySet()) {
List<String> oldList = entry.getValue(); List<String> oldList = entry.getValue();
List<String> newList = newMap.get(entry.getKey()); List<String> newList = newMap.get(entry.getKey());

View File

@@ -55,8 +55,7 @@ public class JsonUtil {
public static void writeValue(Writer writer, Object obj) { public static void writeValue(Writer writer, Object obj) {
try { try {
objectMapper.writeValue(writer, obj); objectMapper.writeValue(writer, obj);
} } catch (IOException e) {
catch (IOException e) {
Throwables.propagateIfPossible(e); Throwables.propagateIfPossible(e);
} }
} }
@@ -68,8 +67,7 @@ public class JsonUtil {
try { try {
return src instanceof String ? (String) src : objectMapper.writeValueAsString(src); return src instanceof String ? (String) src : objectMapper.writeValueAsString(src);
} } catch (Exception e) {
catch (Exception e) {
logger.error("Parse Object to String error src=" + src, e); logger.error("Parse Object to String error src=" + src, e);
return null; return null;
} }
@@ -82,8 +80,7 @@ public class JsonUtil {
try { try {
return src instanceof byte[] ? (byte[]) src : objectMapper.writeValueAsBytes(src); return src instanceof byte[] ? (byte[]) src : objectMapper.writeValueAsBytes(src);
} } catch (Exception e) {
catch (Exception e) {
logger.error("Parse Object to byte[] error", e); logger.error("Parse Object to byte[] error", e);
return null; return null;
} }
@@ -96,8 +93,7 @@ public class JsonUtil {
str = escapesSpecialChar(str); str = escapesSpecialChar(str);
try { try {
return clazz.equals(String.class) ? (T) str : objectMapper.readValue(str, clazz); return clazz.equals(String.class) ? (T) str : objectMapper.readValue(str, clazz);
} } catch (Exception e) {
catch (Exception e) {
logger.error("Parse String to Object error\nString: {}\nClass<T>: {}\nError: {}", str, clazz.getName(), e); logger.error("Parse String to Object error\nString: {}\nClass<T>: {}\nError: {}", str, clazz.getName(), e);
return null; return null;
} }
@@ -109,8 +105,7 @@ public class JsonUtil {
} }
try { try {
return clazz.equals(byte[].class) ? (T) bytes : objectMapper.readValue(bytes, clazz); return clazz.equals(byte[].class) ? (T) bytes : objectMapper.readValue(bytes, clazz);
} } catch (Exception e) {
catch (Exception e) {
logger.error("Parse byte[] to Object error\nbyte[]: {}\nClass<T>: {}\nError: {}", bytes, clazz.getName(), e); logger.error("Parse byte[] to Object error\nbyte[]: {}\nClass<T>: {}\nError: {}", bytes, clazz.getName(), e);
return null; return null;
} }
@@ -123,8 +118,7 @@ public class JsonUtil {
str = escapesSpecialChar(str); str = escapesSpecialChar(str);
try { try {
return (T) (typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference)); return (T) (typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference));
} } catch (Exception e) {
catch (Exception e) {
logger.error("Parse String to Object error\nString: {}\nTypeReference<T>: {}\nError: {}", str, logger.error("Parse String to Object error\nString: {}\nTypeReference<T>: {}\nError: {}", str,
typeReference.getType(), e); typeReference.getType(), e);
return null; return null;
@@ -138,8 +132,7 @@ public class JsonUtil {
try { try {
return (T) (typeReference.getType().equals(byte[].class) ? bytes : objectMapper.readValue(bytes, return (T) (typeReference.getType().equals(byte[].class) ? bytes : objectMapper.readValue(bytes,
typeReference)); typeReference));
} } catch (Exception e) {
catch (Exception e) {
logger.error("Parse byte[] to Object error\nbyte[]: {}\nTypeReference<T>: {}\nError: {}", bytes, logger.error("Parse byte[] to Object error\nbyte[]: {}\nTypeReference<T>: {}\nError: {}", bytes,
typeReference.getType(), e); typeReference.getType(), e);
return null; return null;

View File

@@ -58,6 +58,12 @@ rocketmq:
ticketKey: ticket ticketKey: ticket
# must create userInfo file: ${rocketmq.config.dataPath}/users.properties if the login is required # must create userInfo file: ${rocketmq.config.dataPath}/users.properties if the login is required
loginRequired: false loginRequired: false
# Authentication mode for RocketMQ Dashboard
# Available options:
# - 'file': Use username/password stored in a file (requires 'auth.file.path')
# - 'acl': Use credentials from ACL system (requires 'acl.access.key' and 'acl.secret.key')
# Default: file
authMode: file
useTLS: false useTLS: false
proxyAddr: 127.0.0.1:8080 proxyAddr: 127.0.0.1:8080
proxyAddrs: proxyAddrs:

View File

@@ -14,13 +14,10 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# This file supports hot change, any change will be auto-reloaded without Dashboard restarting. # This file supports hot change, any change will be auto-reloaded without Dashboard restarting.
# Format: a user per line, username=password[,N] #N is optional, 0 (Normal User); 1 (Admin) # Format: a user per line, username=password[,N] #N is optional, 0 (Normal User); 1 (Admin)
# Define Admin # Define Admin
admin=admin,1 super=admin,1
# Define Users # Define Users
user1=user1 user1=user
user2=user2 user2=user

View File

@@ -17,12 +17,20 @@
package org.apache.rocketmq.dashboard; package org.apache.rocketmq.dashboard;
import java.lang.reflect.Field; import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
import java.util.ArrayList; import org.apache.rocketmq.remoting.protocol.route.BrokerData;
import java.util.List;
import org.mockito.internal.util.MockUtil; import org.mockito.internal.util.MockUtil;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class BaseTest { public class BaseTest {
/** /**
* Inject the corresponding mock class automatically * Inject the corresponding mock class automatically
@@ -69,4 +77,20 @@ public class BaseTest {
ReflectionUtils.doWithFields(leafClass, fc); ReflectionUtils.doWithFields(leafClass, fc);
return fields; return fields;
} }
protected ClusterInfo getClusterInfo() {
ClusterInfo clusterInfo = new ClusterInfo();
Map<String, Set<String>> clusterAddrTable = new HashMap<>();
clusterAddrTable.put("DefaultCluster", new HashSet<>(Arrays.asList("broker-a")));
Map<String, BrokerData> brokerAddrTable = new HashMap<>();
BrokerData brokerData = new BrokerData();
brokerData.setBrokerName("broker-a");
HashMap<Long, String> brokerNameTable = new HashMap<>();
brokerNameTable.put(0L, "localhost:10911");
brokerData.setBrokerAddrs(brokerNameTable);
brokerAddrTable.put("broker-a", brokerData);
clusterInfo.setBrokerAddrTable(brokerAddrTable);
clusterInfo.setClusterAddrTable(clusterAddrTable);
return clusterInfo;
}
} }

View File

@@ -17,26 +17,45 @@
package org.apache.rocketmq.dashboard.admin; package org.apache.rocketmq.dashboard.admin;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.apache.commons.pool2.impl.GenericObjectPool; import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.rocketmq.dashboard.aspect.admin.MQAdminAspect; import org.apache.rocketmq.dashboard.aspect.admin.MQAdminAspect;
import org.apache.rocketmq.dashboard.config.RMQConfigure;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt; import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.MQAdminExt; import org.apache.rocketmq.tools.admin.MQAdminExt;
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature; import org.aspectj.lang.reflect.MethodSignature;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.jupiter.MockitoExtension;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class MQAdminAspectTest { public class MQAdminAspectTest {
@Mock
private RMQConfigure rmqConfigure;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(rmqConfigure.isLoginRequired()).thenReturn(false);
}
@Test @Test
public void testAroundMQAdminMethod() throws Throwable { public void testAroundMQAdminMethod() throws Throwable {
MQAdminAspect mqAdminAspect = new MQAdminAspect(); MQAdminAspect mqAdminAspect = new MQAdminAspect();
Field field = mqAdminAspect.getClass().getDeclaredField("rmqConfigure");
field.setAccessible(true);
field.set(mqAdminAspect, rmqConfigure);
ProceedingJoinPoint joinPoint = mock(ProceedingJoinPoint.class); ProceedingJoinPoint joinPoint = mock(ProceedingJoinPoint.class);
MethodSignature signature = mock(MethodSignature.class); MethodSignature signature = mock(MethodSignature.class);
Method method = mock(Method.class); Method method = mock(Method.class);
@@ -44,16 +63,39 @@ public class MQAdminAspectTest {
when(joinPoint.getSignature()).thenReturn(signature); when(joinPoint.getSignature()).thenReturn(signature);
GenericObjectPool<MQAdminExt> mqAdminExtPool = mock(GenericObjectPool.class); GenericObjectPool<MQAdminExt> mqAdminExtPool = mock(GenericObjectPool.class);
// 1. Mock borrowObject() 行为:第一次抛异常,第二次返回 DefaultMQAdminExt
when(mqAdminExtPool.borrowObject()) when(mqAdminExtPool.borrowObject())
.thenThrow(new RuntimeException("borrowObject exception")) .thenThrow(new RuntimeException("borrowObject exception"))
.thenReturn(new DefaultMQAdminExt()); .thenReturn(new DefaultMQAdminExt());
doNothing().doThrow(new RuntimeException("returnObject exception"))
// 2. Mock returnObject() 行为:第一次什么都不做,第二次抛异常
doNothing().when(mqAdminExtPool).returnObject(any());
doThrow(new RuntimeException("returnObject exception"))
.when(mqAdminExtPool).returnObject(any()); .when(mqAdminExtPool).returnObject(any());
Field field = mqAdminAspect.getClass().getDeclaredField("mqAdminExtPool");
// 3. 通过反射注入 Mock 对象
field = mqAdminAspect.getClass().getDeclaredField("mqAdminExtPool");
field.setAccessible(true); field.setAccessible(true);
field.set(mqAdminAspect, mqAdminExtPool); field.set(mqAdminAspect, mqAdminExtPool);
// exception
// 4. 第一次调用 aroundMQAdminMethod预期 borrowObject() 抛异常
try {
mqAdminAspect.aroundMQAdminMethod(joinPoint); mqAdminAspect.aroundMQAdminMethod(joinPoint);
fail("Expected RuntimeException but no exception was thrown");
} catch (RuntimeException e) {
assertEquals("borrowObject exception", e.getMessage());
}
// 5. 第二次调用 aroundMQAdminMethod预期 borrowObject() 成功,但 returnObject() 抛异常
try {
mqAdminAspect.aroundMQAdminMethod(joinPoint); mqAdminAspect.aroundMQAdminMethod(joinPoint);
fail("Expected RuntimeException but no exception was thrown");
} catch (RuntimeException e) {
assertEquals("returnObject exception", e.getMessage());
}
// 6. 验证 borrowObject() 和 returnObject() 各调用了两次
verify(mqAdminExtPool, times(2)).borrowObject();
verify(mqAdminExtPool, times(1)).returnObject(any());
} }
} }

View File

@@ -19,27 +19,25 @@ package org.apache.rocketmq.dashboard.admin;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.rocketmq.client.QueryResult; import org.apache.rocketmq.client.QueryResult;
import org.apache.rocketmq.client.exception.MQBrokerException; import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.impl.MQAdminImpl; import org.apache.rocketmq.client.impl.MQAdminImpl;
import org.apache.rocketmq.client.impl.MQClientAPIImpl; import org.apache.rocketmq.client.impl.MQClientAPIImpl;
import org.apache.rocketmq.client.impl.factory.MQClientInstance; import org.apache.rocketmq.client.impl.factory.MQClientInstance;
import org.apache.rocketmq.common.PlainAccessConfig;
import org.apache.rocketmq.common.TopicConfig; import org.apache.rocketmq.common.TopicConfig;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.dashboard.service.client.MQAdminExtImpl;
import org.apache.rocketmq.dashboard.service.client.MQAdminInstance;
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
import org.apache.rocketmq.remoting.RemotingClient;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
import org.apache.rocketmq.remoting.protocol.ResponseCode;
import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats; import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats;
import org.apache.rocketmq.remoting.protocol.admin.RollbackStats; import org.apache.rocketmq.remoting.protocol.admin.RollbackStats;
import org.apache.rocketmq.remoting.protocol.admin.TopicStatsTable; import org.apache.rocketmq.remoting.protocol.admin.TopicStatsTable;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.protocol.ResponseCode;
import org.apache.rocketmq.remoting.protocol.body.BrokerStatsData; import org.apache.rocketmq.remoting.protocol.body.BrokerStatsData;
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo; import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult; import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult;
@@ -55,12 +53,6 @@ import org.apache.rocketmq.remoting.protocol.body.TopicConfigSerializeWrapper;
import org.apache.rocketmq.remoting.protocol.body.TopicList; import org.apache.rocketmq.remoting.protocol.body.TopicList;
import org.apache.rocketmq.remoting.protocol.route.TopicRouteData; import org.apache.rocketmq.remoting.protocol.route.TopicRouteData;
import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig; import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
import org.apache.rocketmq.dashboard.service.client.MQAdminExtImpl;
import org.apache.rocketmq.dashboard.service.client.MQAdminInstance;
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
import org.apache.rocketmq.remoting.RemotingClient;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
import org.apache.rocketmq.store.stats.BrokerStatsManager; import org.apache.rocketmq.store.stats.BrokerStatsManager;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt; import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl; import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
@@ -74,22 +66,25 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.*;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static org.mockito.ArgumentMatchers.eq;
@RunWith(MockitoJUnitRunner.Silent.class) @RunWith(MockitoJUnitRunner.Silent.class)
public class MQAdminExtImplTest { public class MQAdminExtImplTest {
@@ -154,35 +149,6 @@ public class MQAdminExtImplTest {
mqAdminExtImpl.createAndUpdateTopicConfig(brokerAddr, new TopicConfig()); mqAdminExtImpl.createAndUpdateTopicConfig(brokerAddr, new TopicConfig());
} }
@Test
public void testDeletePlainAccessConfig() throws Exception {
assertNotNull(mqAdminExtImpl);
mqAdminExtImpl.deletePlainAccessConfig(brokerAddr, "rocketmq");
}
@Test
public void testUpdateGlobalWhiteAddrConfig() throws Exception {
assertNotNull(mqAdminExtImpl);
mqAdminExtImpl.updateGlobalWhiteAddrConfig(brokerAddr, "192.168.*.*");
}
@Test
public void testCreateAndUpdatePlainAccessConfig() throws Exception {
assertNotNull(mqAdminExtImpl);
mqAdminExtImpl.createAndUpdatePlainAccessConfig(brokerAddr, new PlainAccessConfig());
}
@Test
public void testExamineBrokerClusterAclVersionInfo() throws Exception {
assertNotNull(mqAdminExtImpl);
assertNull(mqAdminExtImpl.examineBrokerClusterAclVersionInfo(brokerAddr));
}
@Test
public void testExamineBrokerClusterAclConfig() throws Exception {
assertNotNull(mqAdminExtImpl);
assertNull(mqAdminExtImpl.examineBrokerClusterAclConfig(brokerAddr));
}
@Test @Test
public void testQueryConsumerStatus() throws Exception { public void testQueryConsumerStatus() throws Exception {
@@ -226,18 +192,15 @@ public class MQAdminExtImplTest {
public void testExamineTopicConfig() throws Exception { public void testExamineTopicConfig() throws Exception {
assertNotNull(mqAdminExtImpl); assertNotNull(mqAdminExtImpl);
// Create valid TopicConfigSerializeWrapper with topic_test entry // Create valid TopicConfigSerializeWrapper with topictest entry
TopicConfigSerializeWrapper wrapper = new TopicConfigSerializeWrapper();
ConcurrentMap<String, TopicConfig> topicConfigTable = new ConcurrentHashMap<>();
TopicConfig config = new TopicConfig(); TopicConfig config = new TopicConfig();
config.setTopicName("topic_test"); config.setTopicName("topic_test");
topicConfigTable.put("topic_test", config);
wrapper.setTopicConfigTable(topicConfigTable);
// Create successful response // Create successful response
RemotingCommand successResponse = RemotingCommand.createResponseCommand(null); RemotingCommand successResponse = RemotingCommand.createResponseCommand(null);
successResponse.setCode(ResponseCode.SUCCESS); successResponse.setCode(ResponseCode.SUCCESS);
successResponse.setBody(RemotingSerializable.encode(wrapper)); successResponse.setBody(RemotingSerializable.encode(config));
// Mock the remote invocation // Mock the remote invocation
when(remotingClient.invokeSync(eq(brokerAddr), any(RemotingCommand.class), anyLong())) when(remotingClient.invokeSync(eq(brokerAddr), any(RemotingCommand.class), anyLong()))
@@ -249,6 +212,7 @@ public class MQAdminExtImplTest {
Assert.assertEquals("topic_test", topicConfig.getTopicName()); Assert.assertEquals("topic_test", topicConfig.getTopicName());
} }
@Test @Test
public void testExamineTopicStats() throws Exception { public void testExamineTopicStats() throws Exception {
assertNotNull(mqAdminExtImpl); assertNotNull(mqAdminExtImpl);
@@ -257,7 +221,7 @@ public class MQAdminExtImplTest {
} }
TopicStatsTable topicStatsTable = mqAdminExtImpl.examineTopicStats("topic_test"); TopicStatsTable topicStatsTable = mqAdminExtImpl.examineTopicStats("topic_test");
Assert.assertNotNull(topicStatsTable); Assert.assertNotNull(topicStatsTable);
Assert.assertEquals(topicStatsTable.getOffsetTable().size(), 1); Assert.assertEquals(1, topicStatsTable.getOffsetTable().size());
} }
@Test @Test
@@ -535,12 +499,10 @@ public class MQAdminExtImplTest {
public void testConsumeMessageDirectly() throws Exception { public void testConsumeMessageDirectly() throws Exception {
assertNotNull(mqAdminExtImpl); assertNotNull(mqAdminExtImpl);
{ {
when(defaultMQAdminExt.consumeMessageDirectly(anyString(), anyString(), anyString())).thenReturn(new ConsumeMessageDirectlyResult());
when(defaultMQAdminExt.consumeMessageDirectly(anyString(), anyString(), anyString(), anyString())).thenReturn(new ConsumeMessageDirectlyResult()); when(defaultMQAdminExt.consumeMessageDirectly(anyString(), anyString(), anyString(), anyString())).thenReturn(new ConsumeMessageDirectlyResult());
} }
ConsumeMessageDirectlyResult result1 = mqAdminExtImpl.consumeMessageDirectly("group_test", "", "7F000001ACC018B4AAC2116AF6500000");
ConsumeMessageDirectlyResult result2 = mqAdminExtImpl.consumeMessageDirectly("group_test", "", "topic_test", "7F000001ACC018B4AAC2116AF6500000"); ConsumeMessageDirectlyResult result2 = mqAdminExtImpl.consumeMessageDirectly("group_test", "", "topic_test", "7F000001ACC018B4AAC2116AF6500000");
Assert.assertNotNull(result1);
Assert.assertNotNull(result2); Assert.assertNotNull(result2);
} }
@@ -616,15 +578,6 @@ public class MQAdminExtImplTest {
Assert.assertEquals(storeTime, 1628495765398L); Assert.assertEquals(storeTime, 1628495765398L);
} }
@Test
public void testViewMessage() throws Exception {
assertNotNull(mqAdminExtImpl);
{
when(defaultMQAdminExt.viewMessage(anyString())).thenReturn(new MessageExt());
}
MessageExt messageExt = mqAdminExtImpl.viewMessage("7F000001ACC018B4AAC2116AF6500000");
Assert.assertNotNull(messageExt);
}
@Test @Test
public void testQueryMessage() throws Exception { public void testQueryMessage() throws Exception {
@@ -666,15 +619,6 @@ public class MQAdminExtImplTest {
Assert.assertNotNull(timeSpans); Assert.assertNotNull(timeSpans);
} }
@Test
public void testViewMessage2() throws Exception {
assertNotNull(mqAdminExtImpl);
{
when(MQAdminInstance.threadLocalMqClientInstance().getMQAdminImpl()).thenReturn(mock(MQAdminImpl.class));
when(defaultMQAdminExt.viewMessage(anyString())).thenThrow(new RuntimeException("viewMessage exception"));
}
mqAdminExtImpl.viewMessage("topic_test", "7F000001ACC018B4AAC2116AF6500000");
}
@Test @Test
public void testGetBrokerConfig() throws Exception { public void testGetBrokerConfig() throws Exception {
@@ -788,7 +732,6 @@ public class MQAdminExtImplTest {
@Test @Test
public void testResumeCheckHalfMessage() throws Exception { public void testResumeCheckHalfMessage() throws Exception {
assertNotNull(mqAdminExtImpl); assertNotNull(mqAdminExtImpl);
Assert.assertFalse(mqAdminExtImpl.resumeCheckHalfMessage("7F000001ACC018B4AAC2116AF6500000"));
Assert.assertFalse(mqAdminExtImpl.resumeCheckHalfMessage("topic_test", "7F000001ACC018B4AAC2116AF6500000")); Assert.assertFalse(mqAdminExtImpl.resumeCheckHalfMessage("topic_test", "7F000001ACC018B4AAC2116AF6500000"));
} }

View File

@@ -18,56 +18,41 @@
package org.apache.rocketmq.dashboard.config; package org.apache.rocketmq.dashboard.config;
import org.apache.rocketmq.dashboard.BaseTest; import org.apache.rocketmq.dashboard.BaseTest;
import org.apache.rocketmq.dashboard.interceptor.AuthInterceptor;
import org.assertj.core.util.Lists;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import java.util.List;
public class AuthWebMVCConfigurerAdapterTest extends BaseTest { public class AuthWebMVCConfigurerAdapterTest extends BaseTest {
@InjectMocks // @InjectMocks
private AuthWebMVCConfigurerAdapter authWebMVCConfigurerAdapter; // private AuthWebMVCConfigurerAdapter authWebMVCConfigurerAdapter;
//
@Mock // @Mock
private RMQConfigure configure; // private RMQConfigure configure;
//
@Mock // @Mock
private AuthInterceptor authInterceptor; // private AuthInterceptor authInterceptor;
//
@Before // @Before
public void init() throws Exception { // public void init() throws Exception {
MockitoAnnotations.initMocks(this); // MockitoAnnotations.initMocks(this);
} // }
//
//
@Test // @Test
public void addInterceptors() { // public void addInterceptors() {
Mockito.when(configure.isLoginRequired()).thenReturn(true); // Mockito.when(configure.isLoginRequired()).thenReturn(true);
InterceptorRegistry registry = new InterceptorRegistry(); // InterceptorRegistry registry = new InterceptorRegistry();
Assertions.assertDoesNotThrow(() -> authWebMVCConfigurerAdapter.addInterceptors(registry)); // Assertions.assertDoesNotThrow(() -> authWebMVCConfigurerAdapter.addInterceptors(registry));
} // }
//
@Test // @Test
public void addArgumentResolvers() { // public void addArgumentResolvers() {
List<HandlerMethodArgumentResolver> argumentResolvers = Lists.newArrayList(); // List<HandlerMethodArgumentResolver> argumentResolvers = Lists.newArrayList();
authWebMVCConfigurerAdapter.addArgumentResolvers(argumentResolvers); // authWebMVCConfigurerAdapter.addArgumentResolvers(argumentResolvers);
Assertions.assertEquals(1, argumentResolvers.size()); // Assertions.assertEquals(1, argumentResolvers.size());
} // }
//
@Test // @Test
public void addViewControllers() { // public void addViewControllers() {
ViewControllerRegistry registry = new ViewControllerRegistry(new ClassPathXmlApplicationContext()); // ViewControllerRegistry registry = new ViewControllerRegistry(new ClassPathXmlApplicationContext());
Assertions.assertDoesNotThrow(() -> authWebMVCConfigurerAdapter.addViewControllers(registry)); // Assertions.assertDoesNotThrow(() -> authWebMVCConfigurerAdapter.addViewControllers(registry));
} // }
} }

View File

@@ -16,11 +16,12 @@
*/ */
package org.apache.rocketmq.dashboard.config; package org.apache.rocketmq.dashboard.config;
import org.junit.Assert;
import org.junit.Test;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Assert;
import org.junit.Test;
public class CollectExecutorConfigTest { public class CollectExecutorConfigTest {

View File

@@ -18,13 +18,14 @@
package org.apache.rocketmq.dashboard.config; package org.apache.rocketmq.dashboard.config;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import java.io.File;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.web.server.ErrorPage; import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar; import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry; import org.springframework.boot.web.server.ErrorPageRegistry;
import java.io.File;
public class RMQConfigureTest { public class RMQConfigureTest {
private RMQConfigure rmqConfigure = new RMQConfigure(); private RMQConfigure rmqConfigure = new RMQConfigure();
@@ -41,6 +42,7 @@ public class RMQConfigureTest {
rmqConfigure.setNamesrvAddr("127.0.0.1:9876"); rmqConfigure.setNamesrvAddr("127.0.0.1:9876");
rmqConfigure.setTimeoutMillis(3000L); rmqConfigure.setTimeoutMillis(3000L);
rmqConfigure.setNamesrvAddrs(Lists.asList("127.0.0.1:9876", new String[]{"127.0.0.2:9876"})); rmqConfigure.setNamesrvAddrs(Lists.asList("127.0.0.1:9876", new String[]{"127.0.0.2:9876"}));
rmqConfigure.setAuthMode("file");
} }
@Test @Test
@@ -58,6 +60,7 @@ public class RMQConfigureTest {
Assert.assertEquals(rmqConfigure.getNamesrvAddr(), "127.0.0.1:9876"); Assert.assertEquals(rmqConfigure.getNamesrvAddr(), "127.0.0.1:9876");
Assert.assertEquals(rmqConfigure.getNamesrvAddrs().size(), 2); Assert.assertEquals(rmqConfigure.getNamesrvAddrs().size(), 2);
Assert.assertEquals(rmqConfigure.getTimeoutMillis().longValue(), 3000L); Assert.assertEquals(rmqConfigure.getTimeoutMillis().longValue(), 3000L);
Assert.assertEquals(rmqConfigure.getAuthMode(), "file");
ErrorPageRegistrar registrar = rmqConfigure.errorPageRegistrar(); ErrorPageRegistrar registrar = rmqConfigure.errorPageRegistrar();
registrar.registerErrorPages(new ErrorPageRegistry() { registrar.registerErrorPages(new ErrorPageRegistry() {
@Override @Override

View File

@@ -16,353 +16,228 @@
*/ */
package org.apache.rocketmq.dashboard.controller; package org.apache.rocketmq.dashboard.controller;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists; import org.apache.rocketmq.auth.authentication.enums.UserStatus;
import java.util.List; import org.apache.rocketmq.auth.authentication.enums.UserType;
import org.apache.rocketmq.common.AclConfig; import org.apache.rocketmq.auth.authorization.enums.Decision;
import org.apache.rocketmq.common.PlainAccessConfig; import org.apache.rocketmq.dashboard.model.Policy;
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo; import org.apache.rocketmq.dashboard.model.PolicyRequest;
import org.apache.rocketmq.dashboard.model.request.AclRequest; import org.apache.rocketmq.dashboard.model.request.UserCreateRequest;
import org.apache.rocketmq.dashboard.model.request.UserInfoParam;
import org.apache.rocketmq.dashboard.model.request.UserUpdateRequest;
import org.apache.rocketmq.dashboard.service.impl.AclServiceImpl; import org.apache.rocketmq.dashboard.service.impl.AclServiceImpl;
import org.apache.rocketmq.dashboard.util.MockObjectUtil; import org.apache.rocketmq.dashboard.support.GlobalExceptionHandler;
import org.apache.rocketmq.remoting.protocol.body.AclInfo;
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Spy; import org.mockito.Mock;
import org.springframework.http.MediaType; import org.mockito.MockitoAnnotations;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.mockito.ArgumentMatchers.any; import java.util.Arrays;
import static org.mockito.ArgumentMatchers.anyString; import java.util.List;
import static org.mockito.Mockito.doNothing;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public class AclControllerTest extends BaseControllerTest { public class AclControllerTest extends BaseControllerTest {
@Mock
private AclServiceImpl aclService;
@InjectMocks @InjectMocks
private AclController aclController; private AclController aclController;
@Spy
private AclServiceImpl aclService;
@Before @Before
public void init() throws Exception { public void init() {
AclConfig aclConfig = MockObjectUtil.createAclConfig(); MockitoAnnotations.initMocks(this);
when(mqAdminExt.examineBrokerClusterAclConfig(anyString())).thenReturn(aclConfig); mockMvc = MockMvcBuilders.standaloneSetup(aclController).setControllerAdvice(GlobalExceptionHandler.class).build();
ClusterInfo clusterInfo = MockObjectUtil.createClusterInfo(); }
when(mqAdminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo);
doNothing().when(mqAdminExt).createAndUpdatePlainAccessConfig(anyString(), any(PlainAccessConfig.class));
doNothing().when(mqAdminExt).deletePlainAccessConfig(anyString(), anyString()); @Test
doNothing().when(mqAdminExt).updateGlobalWhiteAddrConfig(anyString(), anyString()); public void testListUsers() {
// Prepare test data
String brokerAddress = "localhost:10911";
List<UserInfo> expectedUsers = Arrays.asList(
UserInfo.of("user1", "password1", "super"),
UserInfo.of("user2", "password2", "super")
);
// Mock service behavior
when(aclService.listUsers(brokerAddress)).thenReturn(expectedUsers);
// Call controller method
List<UserInfo> result = aclController.listUsers(brokerAddress);
// Verify
assertEquals(expectedUsers, result);
verify(aclService, times(1)).listUsers(brokerAddress);
} }
@Test @Test
public void testIsEnableAcl() throws Exception { public void testListUsersWithoutBrokerAddress() {
final String url = "/acl/enable.query"; // Prepare test data
// 1. disable acl. List<UserInfo> expectedUsers = Arrays.asList(
requestBuilder = MockMvcRequestBuilders.get(url); UserInfo.of("user1", "password1", "super")
perform = mockMvc.perform(requestBuilder); );
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.data").value(false));
// 2.enable acl. // Mock service behavior
super.mockRmqConfigure(); when(aclService.listUsers(null)).thenReturn(expectedUsers);
perform = mockMvc.perform(requestBuilder); // Call controller method
perform.andExpect(status().isOk()) List<UserInfo> result = aclController.listUsers(null);
.andExpect(jsonPath("$.data").value(true)); // Verify
assertEquals(expectedUsers, result);
verify(aclService, times(1)).listUsers(null);
} }
@Test @Test
public void testGetAclConfig() throws Exception { public void testListAcls() {
final String url = "/acl/config.query"; // Prepare test data
String brokerAddress = "localhost:9092";
String searchParam = "user1";
Object expectedAcls = Arrays.asList(
AclInfo.of("user1", List.of("READ", "test"), List.of("TOPIC:test"), List.of("localhost:10911"), Decision.ALLOW.getName())
);
// 1. broker addr table is not empty. // Mock service behavior
ClusterInfo clusterInfo = MockObjectUtil.createClusterInfo(); when(aclService.listAcls(brokerAddress, searchParam)).thenReturn(expectedAcls);
when(mqAdminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo);
requestBuilder = MockMvcRequestBuilders.get(url);
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.data").isMap())
.andExpect(jsonPath("$.data.globalWhiteAddrs").isNotEmpty())
.andExpect(jsonPath("$.data.plainAccessConfigs").isNotEmpty())
.andExpect(jsonPath("$.data.plainAccessConfigs[0].secretKey").isNotEmpty());
// 2. broker addr table is empty. // Call controller method
clusterInfo.getBrokerAddrTable().clear(); Object result = aclController.listAcls(brokerAddress, searchParam);
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.data").isMap())
.andExpect(jsonPath("$.data.globalWhiteAddrs").isEmpty())
.andExpect(jsonPath("$.data.plainAccessConfigs").isEmpty());
// 3. login required and user info is null. // Verify
when(configure.isLoginRequired()).thenReturn(true); assertEquals(expectedAcls, result);
when(mqAdminExt.examineBrokerClusterInfo()).thenReturn(MockObjectUtil.createClusterInfo()); verify(aclService, times(1)).listAcls(brokerAddress, searchParam);
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.data").isMap())
.andExpect(jsonPath("$.data.globalWhiteAddrs").isNotEmpty())
.andExpect(jsonPath("$.data.plainAccessConfigs").isNotEmpty())
.andExpect(jsonPath("$.data.plainAccessConfigs[0].secretKey").isEmpty());
// 4. login required, but user is not admin. emmmm, Mockito may can not mock static method.
} }
@Test @Test
public void testAddAclConfig() throws Exception { public void testCreateAcl() {
final String url = "/acl/add.do"; // Prepare test data
PlainAccessConfig accessConfig = new PlainAccessConfig(); PolicyRequest request = new PolicyRequest();
requestBuilder = MockMvcRequestBuilders.post(url); request.setBrokerAddress("localhost:9092");
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8); request.setSubject("user1");
request.setPolicies(List.of(
new Policy()
));
// 1. access key is null. // Call controller method
requestBuilder.content(JSON.toJSONString(accessConfig)); Object result = aclController.createAcl(request);
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(-1))
.andExpect(jsonPath("$.errMsg").exists());
// 2. secret key is null. // Verify
accessConfig.setAccessKey("test-access-key"); assertEquals(true, result);
requestBuilder.content(JSON.toJSONString(accessConfig)); verify(aclService, times(1)).createAcl(request);
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(-1))
.andExpect(jsonPath("$.errMsg").exists());
ClusterInfo clusterInfo = MockObjectUtil.createClusterInfo();
when(mqAdminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo);
// 3. add if the access key not exist.
accessConfig.setSecretKey("12345678");
requestBuilder.content(JSON.toJSONString(accessConfig));
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(0));
// 4. add failed if the access key is existed.
accessConfig.setAccessKey("rocketmq2");
requestBuilder.content(JSON.toJSONString(accessConfig));
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(-1))
.andExpect(jsonPath("$.errMsg").exists());
// 5. add failed if there is no alive broker.
clusterInfo.getBrokerAddrTable().clear();
accessConfig.setAccessKey("test-access-key");
requestBuilder.content(JSON.toJSONString(accessConfig));
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(-1))
.andExpect(jsonPath("$.errMsg").exists());
} }
@Test @Test
public void testDeleteAclConfig() throws Exception { public void testDeleteUser() {
final String url = "/acl/delete.do"; // Prepare test data
PlainAccessConfig accessConfig = new PlainAccessConfig(); String brokerAddress = "localhost:9092";
requestBuilder = MockMvcRequestBuilders.post(url); String username = "user1";
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
// 1. access key is null. // Call controller method
requestBuilder.content(JSON.toJSONString(accessConfig)); Object result = aclController.deleteUser(brokerAddress, username);
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(-1))
.andExpect(jsonPath("$.errMsg").exists());
// 2. access key is not null. // Verify
accessConfig.setAccessKey("rocketmq"); assertEquals(true, result);
requestBuilder.content(JSON.toJSONString(accessConfig)); verify(aclService, times(1)).deleteUser(brokerAddress, username);
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(0));
} }
@Test @Test
public void testUpdateAclConfig() throws Exception { public void testDeleteUserWithoutBrokerAddress() {
final String url = "/acl/update.do"; // Prepare test data
PlainAccessConfig accessConfig = new PlainAccessConfig(); String username = "user1";
requestBuilder = MockMvcRequestBuilders.post(url);
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
// 1. secret key is null. // Call controller method
accessConfig.setAccessKey("rocketmq"); Object result = aclController.deleteUser(null, username);
requestBuilder.content(JSON.toJSONString(accessConfig));
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(-1))
.andExpect(jsonPath("$.errMsg").exists());
// 2. update. // Verify
accessConfig.setSecretKey("abcdefghjkl"); assertEquals(true, result);
requestBuilder.content(JSON.toJSONString(accessConfig)); verify(aclService, times(1)).deleteUser(null, username);
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(0));
} }
@Test @Test
public void testAddAclTopicConfig() throws Exception { public void testUpdateUser() {
final String url = "/acl/topic/add.do"; // Prepare test data
AclRequest request = new AclRequest(); UserUpdateRequest request = new UserUpdateRequest();
request.setConfig(createDefaultPlainAccessConfig()); request.setBrokerAddress("localhost:9092");
request.setUserInfo(new UserInfoParam("user1", "newPassword", UserStatus.ENABLE.getName(), UserType.SUPER.getName()));
// 1. if not exist. // Call controller method
request.setTopicPerm("test_topic=PUB"); Object result = aclController.updateUser(request);
requestBuilder = MockMvcRequestBuilders.post(url);
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
requestBuilder.content(JSON.toJSONString(request));
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(0));
// 2. if exist. // Verify
request.setTopicPerm("topicA=PUB"); assertEquals(true, result);
requestBuilder.content(JSON.toJSONString(request)); verify(aclService, times(1)).updateUser(request.getBrokerAddress(), request.getUserInfo());
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(0));
// 3. if access key not exist.
request.getConfig().setAccessKey("test_access_key123");
requestBuilder.content(JSON.toJSONString(request));
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(0));
} }
@Test @Test
public void testAddAclGroupConfig() throws Exception { public void testCreateUser() {
final String url = "/acl/group/add.do"; // Prepare test data
AclRequest request = new AclRequest(); UserCreateRequest request = new UserCreateRequest();
request.setConfig(createDefaultPlainAccessConfig()); request.setBrokerAddress("localhost:9092");
request.setUserInfo(new UserInfoParam("user1", "newPassword", UserStatus.ENABLE.getName(), UserType.SUPER.getName()));
// 1. if not exist. // Call controller method
request.setGroupPerm("test_consumer=PUB|SUB"); Object result = aclController.createUser(request);
requestBuilder = MockMvcRequestBuilders.post(url);
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
requestBuilder.content(JSON.toJSONString(request));
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(0));
// 2. if exist. // Verify
request.setGroupPerm("groupA=PUB|SUB"); assertEquals(true, result);
requestBuilder.content(JSON.toJSONString(request)); verify(aclService, times(1)).createUser(request.getBrokerAddress(), request.getUserInfo());
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(0));
// 3. if access key not exist.
request.getConfig().setAccessKey("test_access_key123");
requestBuilder.content(JSON.toJSONString(request));
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(0));
} }
@Test @Test
public void testDeletePermConfig() throws Exception { public void testDeleteAcl() {
final String url = "/acl/perm/delete.do"; // Prepare test data
AclRequest request = new AclRequest(); String brokerAddress = "localhost:9092";
request.setConfig(createDefaultPlainAccessConfig()); String subject = "user1";
requestBuilder = MockMvcRequestBuilders.post(url); String resource = "TOPIC:test";
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
requestBuilder.content(JSON.toJSONString(request));
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(0));
// if access key not exist. // Call controller method
request.getConfig().setAccessKey("test_access_key123"); Object result = aclController.deleteAcl(brokerAddress, subject, resource);
requestBuilder.content(JSON.toJSONString(request));
perform = mockMvc.perform(requestBuilder); // Verify
perform.andExpect(status().isOk()) assertEquals(true, result);
.andExpect(jsonPath("$.status").value(0)); verify(aclService, times(1)).deleteAcl(brokerAddress, subject, resource);
} }
@Test @Test
public void testSyncConfig() throws Exception { public void testDeleteAclWithoutBrokerAddressAndResource() {
final String url = "/acl/sync.do"; // Prepare test data
requestBuilder = MockMvcRequestBuilders.post(url); String subject = "user1";
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
requestBuilder.content(JSON.toJSONString(createDefaultPlainAccessConfig())); // Call controller method
perform = mockMvc.perform(requestBuilder); Object result = aclController.deleteAcl(null, subject, null);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(0)); // Verify
assertEquals(true, result);
verify(aclService, times(1)).deleteAcl(null, subject, null);
} }
@Test @Test
public void testAddWhiteList() throws Exception { public void testUpdateAcl() {
final String url = "/acl/white/list/add.do"; // Prepare test data
List<String> whiteList = Lists.newArrayList("192.168.0.1"); PolicyRequest request = new PolicyRequest();
request.setBrokerAddress("localhost:9092");
request.setSubject("user1");
request.setPolicies(List.of(
new Policy()
));
// 1. if global white list is not null. // Call controller method
requestBuilder = MockMvcRequestBuilders.post(url); Object result = aclController.updateAcl(request);
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
requestBuilder.content(JSON.toJSONString(whiteList));
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(0));
// 2. if global white list is null. // Verify
AclConfig aclConfig = MockObjectUtil.createAclConfig(); assertEquals(true, result);
aclConfig.setGlobalWhiteAddrs(null); verify(aclService, times(1)).updateAcl(request);
when(mqAdminExt.examineBrokerClusterAclConfig(anyString())).thenReturn(aclConfig);
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(0));
} }
@Test @Override
public void testDeleteWhiteAddr() throws Exception { protected Object getTestController() {
final String url = "/acl/white/list/delete.do";
requestBuilder = MockMvcRequestBuilders.delete(url);
requestBuilder.param("request", "localhost");
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(0));
}
@Test
public void testSynchronizeWhiteList() throws Exception {
final String url = "/acl/white/list/sync.do";
List<String> whiteList = Lists.newArrayList();
// 1. if white list for syncing is empty.
requestBuilder = MockMvcRequestBuilders.post(url);
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
requestBuilder.content(JSON.toJSONString(whiteList));
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(-1))
.andExpect(jsonPath("$.errMsg").exists());
// 2. if white list for syncing is not empty.
whiteList.add("localhost");
requestBuilder.content(JSON.toJSONString(whiteList));
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(0));
}
@Override protected Object getTestController() {
return aclController; return aclController;
} }
private PlainAccessConfig createDefaultPlainAccessConfig() {
PlainAccessConfig config = new PlainAccessConfig();
config.setAdmin(false);
config.setAccessKey("rocketmq");
config.setSecretKey("123456789");
config.setDefaultGroupPerm("SUB");
config.setDefaultTopicPerm("DENY");
config.setTopicPerms(Lists.newArrayList("topicA=DENY", "topicB=PUB|SUB"));
config.setGroupPerms(Lists.newArrayList("groupA=DENY", "groupB=PUB|SUB"));
return config;
}
} }

View File

@@ -23,6 +23,8 @@ import org.apache.rocketmq.dashboard.config.RMQConfigure;
import org.apache.rocketmq.dashboard.support.GlobalExceptionHandler; import org.apache.rocketmq.dashboard.support.GlobalExceptionHandler;
import org.apache.rocketmq.dashboard.support.GlobalRestfulResponseBodyAdvice; import org.apache.rocketmq.dashboard.support.GlobalRestfulResponseBodyAdvice;
import org.apache.rocketmq.dashboard.util.MyPrintingResultHandler; import org.apache.rocketmq.dashboard.util.MyPrintingResultHandler;
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
import org.apache.rocketmq.remoting.protocol.route.BrokerData;
import org.apache.rocketmq.tools.admin.MQAdminExt; import org.apache.rocketmq.tools.admin.MQAdminExt;
import org.junit.Before; import org.junit.Before;
import org.mockito.Mock; import org.mockito.Mock;
@@ -32,6 +34,12 @@ import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

View File

@@ -16,17 +16,18 @@
*/ */
package org.apache.rocketmq.dashboard.controller; package org.apache.rocketmq.dashboard.controller;
import java.util.HashMap;
import java.util.Properties;
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
import org.apache.rocketmq.remoting.protocol.body.KVTable;
import org.apache.rocketmq.dashboard.service.impl.ClusterServiceImpl; import org.apache.rocketmq.dashboard.service.impl.ClusterServiceImpl;
import org.apache.rocketmq.dashboard.util.MockObjectUtil; import org.apache.rocketmq.dashboard.util.MockObjectUtil;
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
import org.apache.rocketmq.remoting.protocol.body.KVTable;
import org.junit.Test; import org.junit.Test;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Spy; import org.mockito.Spy;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.util.HashMap;
import java.util.Properties;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

View File

@@ -19,44 +19,43 @@ package org.apache.rocketmq.dashboard.controller;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.dashboard.model.QueueStatInfo;
import org.apache.rocketmq.dashboard.model.TopicConsumerInfo;
import org.apache.rocketmq.dashboard.model.request.ConsumerConfigInfo;
import org.apache.rocketmq.dashboard.model.request.DeleteSubGroupRequest;
import org.apache.rocketmq.dashboard.model.request.ResetOffsetRequest;
import org.apache.rocketmq.dashboard.service.ClusterInfoService;
import org.apache.rocketmq.dashboard.service.impl.ConsumerServiceImpl;
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
import org.apache.rocketmq.remoting.protocol.ResponseCode;
import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats; import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats;
import org.apache.rocketmq.remoting.protocol.admin.RollbackStats; import org.apache.rocketmq.remoting.protocol.admin.RollbackStats;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.protocol.ResponseCode;
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo; import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
import org.apache.rocketmq.remoting.protocol.body.Connection;
import org.apache.rocketmq.remoting.protocol.body.ConsumerConnection; import org.apache.rocketmq.remoting.protocol.body.ConsumerConnection;
import org.apache.rocketmq.remoting.protocol.body.ConsumerRunningInfo; import org.apache.rocketmq.remoting.protocol.body.ConsumerRunningInfo;
import org.apache.rocketmq.remoting.protocol.body.SubscriptionGroupWrapper; import org.apache.rocketmq.remoting.protocol.body.SubscriptionGroupWrapper;
import org.apache.rocketmq.remoting.protocol.heartbeat.ConsumeType; import org.apache.rocketmq.remoting.protocol.heartbeat.ConsumeType;
import org.apache.rocketmq.remoting.protocol.heartbeat.MessageModel; import org.apache.rocketmq.remoting.protocol.heartbeat.MessageModel;
import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig; import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
import org.apache.rocketmq.dashboard.model.request.ConsumerConfigInfo;
import org.apache.rocketmq.dashboard.model.request.DeleteSubGroupRequest;
import org.apache.rocketmq.dashboard.model.request.ResetOffsetRequest;
import org.apache.rocketmq.dashboard.service.impl.ConsumerServiceImpl;
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
import org.apache.rocketmq.dashboard.model.TopicConsumerInfo;
import org.apache.rocketmq.dashboard.model.QueueStatInfo;
import org.apache.rocketmq.remoting.protocol.body.Connection;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy; import org.mockito.Spy;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.*;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -71,12 +70,18 @@ public class ConsumerControllerTest extends BaseControllerTest {
@Spy @Spy
private ConsumerServiceImpl consumerService; private ConsumerServiceImpl consumerService;
@Mock
private ClusterInfoService clusterInfoService;
@Before @Before
public void init() throws Exception { public void init() throws Exception {
// 2. mock ClusterInfo data
ClusterInfo mockClusterInfo = getClusterInfo();
when(clusterInfoService.get()).thenReturn(mockClusterInfo);
consumerService.afterPropertiesSet(); consumerService.afterPropertiesSet();
super.mockRmqConfigure(); super.mockRmqConfigure();
ClusterInfo clusterInfo = MockObjectUtil.createClusterInfo(); // ClusterInfo clusterInfo = MockObjectUtil.createClusterInfo();
when(mqAdminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo); // when(mqAdminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo);
SubscriptionGroupWrapper wrapper = MockObjectUtil.createSubscriptionGroupWrapper(); SubscriptionGroupWrapper wrapper = MockObjectUtil.createSubscriptionGroupWrapper();
when(mqAdminExt.getAllSubscriptionGroup(anyString(), anyLong())).thenReturn(wrapper); when(mqAdminExt.getAllSubscriptionGroup(anyString(), anyLong())).thenReturn(wrapper);
ConsumeStats stats = MockObjectUtil.createConsumeStats(); ConsumeStats stats = MockObjectUtil.createConsumeStats();
@@ -109,6 +114,7 @@ public class ConsumerControllerTest extends BaseControllerTest {
@Test @Test
public void testGroupQuery() throws Exception { public void testGroupQuery() throws Exception {
final String url = "/consumer/group.query"; final String url = "/consumer/group.query";
requestBuilder = MockMvcRequestBuilders.get(url); requestBuilder = MockMvcRequestBuilders.get(url);
requestBuilder.param("consumerGroup", "group_test"); requestBuilder.param("consumerGroup", "group_test");
perform = mockMvc.perform(requestBuilder); perform = mockMvc.perform(requestBuilder);
@@ -177,6 +183,10 @@ public class ConsumerControllerTest extends BaseControllerTest {
@Test @Test
public void testExamineSubscriptionGroupConfig() throws Exception { public void testExamineSubscriptionGroupConfig() throws Exception {
ClusterInfo mockClusterInfo = getClusterInfo();
{
when(clusterInfoService.get()).thenReturn(mockClusterInfo);
}
final String url = "/consumer/examineSubscriptionGroupConfig.query"; final String url = "/consumer/examineSubscriptionGroupConfig.query";
requestBuilder = MockMvcRequestBuilders.get(url); requestBuilder = MockMvcRequestBuilders.get(url);
requestBuilder.param("consumerGroup", "group_test"); requestBuilder.param("consumerGroup", "group_test");
@@ -188,7 +198,9 @@ public class ConsumerControllerTest extends BaseControllerTest {
@Test @Test
public void testDelete() throws Exception { public void testDelete() throws Exception {
final String url = "/consumer/deleteSubGroup.do"; final String url = "/consumer/deleteSubGroup.do";
ClusterInfo mockClusterInfo = getClusterInfo();
{ {
when(clusterInfoService.get()).thenReturn(mockClusterInfo);
doNothing().when(mqAdminExt).deleteSubscriptionGroup(any(), anyString()); doNothing().when(mqAdminExt).deleteSubscriptionGroup(any(), anyString());
doNothing().when(mqAdminExt).deleteTopicInBroker(any(), anyString()); doNothing().when(mqAdminExt).deleteTopicInBroker(any(), anyString());
doNothing().when(mqAdminExt).deleteTopicInNameServer(any(), anyString()); doNothing().when(mqAdminExt).deleteTopicInNameServer(any(), anyString());
@@ -214,7 +226,6 @@ public class ConsumerControllerTest extends BaseControllerTest {
requestBuilder.content(JSON.toJSONString(consumerConfigInfo)); requestBuilder.content(JSON.toJSONString(consumerConfigInfo));
perform = mockMvc.perform(requestBuilder); perform = mockMvc.perform(requestBuilder);
performErrorExpect(perform); performErrorExpect(perform);
{ {
doNothing().when(mqAdminExt).createAndUpdateSubscriptionGroupConfig(anyString(), any()); doNothing().when(mqAdminExt).createAndUpdateSubscriptionGroupConfig(anyString(), any());
} }
@@ -233,6 +244,7 @@ public class ConsumerControllerTest extends BaseControllerTest {
.andExpect(jsonPath("$.data").value(true)); .andExpect(jsonPath("$.data").value(true));
} }
@Test @Test
public void testQueryConsumerByTopic() throws Exception { public void testQueryConsumerByTopic() throws Exception {
// Prepare test data // Prepare test data
@@ -313,7 +325,8 @@ public class ConsumerControllerTest extends BaseControllerTest {
.andExpect(jsonPath("$.data.jstack").value("test")); .andExpect(jsonPath("$.data.jstack").value("test"));
} }
@Override protected Object getTestController() { @Override
protected Object getTestController() {
return consumerController; return consumerController;
} }
} }

View File

@@ -18,14 +18,6 @@ package org.apache.rocketmq.dashboard.controller;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.io.Files; import com.google.common.io.Files;
import java.io.File;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.rocketmq.dashboard.service.impl.DashboardCollectServiceImpl; import org.apache.rocketmq.dashboard.service.impl.DashboardCollectServiceImpl;
import org.apache.rocketmq.dashboard.service.impl.DashboardServiceImpl; import org.apache.rocketmq.dashboard.service.impl.DashboardServiceImpl;
import org.apache.rocketmq.dashboard.util.JsonUtil; import org.apache.rocketmq.dashboard.util.JsonUtil;
@@ -36,6 +28,15 @@ import org.mockito.InjectMocks;
import org.mockito.Spy; import org.mockito.Spy;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.io.File;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;

View File

@@ -18,13 +18,8 @@ package org.apache.rocketmq.dashboard.controller;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import java.util.List;
import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.MixAll; import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.remoting.protocol.ResponseCode;
import org.apache.rocketmq.remoting.protocol.body.CMResult;
import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult;
import org.apache.rocketmq.remoting.protocol.route.TopicRouteData;
import org.apache.rocketmq.dashboard.model.DlqMessageRequest; import org.apache.rocketmq.dashboard.model.DlqMessageRequest;
import org.apache.rocketmq.dashboard.model.MessagePage; import org.apache.rocketmq.dashboard.model.MessagePage;
import org.apache.rocketmq.dashboard.model.MessageView; import org.apache.rocketmq.dashboard.model.MessageView;
@@ -32,6 +27,10 @@ import org.apache.rocketmq.dashboard.model.request.MessageQuery;
import org.apache.rocketmq.dashboard.service.impl.DlqMessageServiceImpl; import org.apache.rocketmq.dashboard.service.impl.DlqMessageServiceImpl;
import org.apache.rocketmq.dashboard.service.impl.MessageServiceImpl; import org.apache.rocketmq.dashboard.service.impl.MessageServiceImpl;
import org.apache.rocketmq.dashboard.util.MockObjectUtil; import org.apache.rocketmq.dashboard.util.MockObjectUtil;
import org.apache.rocketmq.remoting.protocol.ResponseCode;
import org.apache.rocketmq.remoting.protocol.body.CMResult;
import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult;
import org.apache.rocketmq.remoting.protocol.route.TopicRouteData;
import org.junit.Test; import org.junit.Test;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
@@ -41,6 +40,8 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.util.List;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -172,7 +173,8 @@ public class DlqMessageControllerTest extends BaseControllerTest {
.andExpect(content().contentType("application/vnd.ms-excel;charset=utf-8")); .andExpect(content().contentType("application/vnd.ms-excel;charset=utf-8"));
} }
@Override protected Object getTestController() { @Override
protected Object getTestController() {
return dlqMessageController; return dlqMessageController;
} }
} }

View File

@@ -16,20 +16,26 @@
*/ */
package org.apache.rocketmq.dashboard.controller; package org.apache.rocketmq.dashboard.controller;
import java.lang.reflect.Field;
import org.apache.rocketmq.dashboard.model.User; import org.apache.rocketmq.dashboard.model.User;
import org.apache.rocketmq.dashboard.service.impl.UserServiceImpl; import org.apache.rocketmq.dashboard.service.impl.UserServiceImpl;
import org.apache.rocketmq.dashboard.service.strategy.UserContext;
import org.apache.rocketmq.dashboard.service.strategy.UserStrategy;
import org.apache.rocketmq.dashboard.support.GlobalExceptionHandler;
import org.apache.rocketmq.dashboard.util.WebUtil; import org.apache.rocketmq.dashboard.util.WebUtil;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy; import org.mockito.Spy;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Field;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -38,19 +44,27 @@ public class LoginControllerTest extends BaseControllerTest {
@InjectMocks @InjectMocks
private LoginController loginController; private LoginController loginController;
@Spy @Mock
private UserServiceImpl userService; private UserServiceImpl userService;
@Spy
private UserContext userContext;
@Spy
private UserStrategy userStrategy;
private String contextPath = "/rocketmq-console"; private String contextPath = "/rocketmq-console";
@Before @Before
public void init() { public void init() {
MockitoAnnotations.initMocks(this);
super.mockRmqConfigure(); super.mockRmqConfigure();
when(configure.isLoginRequired()).thenReturn(true); when(configure.isLoginRequired()).thenReturn(true);
when(configure.getRocketMqDashboardDataPath()).thenReturn(""); when(configure.getRocketMqDashboardDataPath()).thenReturn("");
Field contextPathField = ReflectionUtils.findField(LoginController.class, "contextPath"); Field contextPathField = ReflectionUtils.findField(LoginController.class, "contextPath");
ReflectionUtils.makeAccessible(contextPathField); ReflectionUtils.makeAccessible(contextPathField);
ReflectionUtils.setField(contextPathField, loginController, contextPath); ReflectionUtils.setField(contextPathField, loginController, contextPath);
mockMvc = MockMvcBuilders.standaloneSetup(loginController).setControllerAdvice(GlobalExceptionHandler.class).build();
} }
@Test @Test
@@ -60,57 +74,56 @@ public class LoginControllerTest extends BaseControllerTest {
requestBuilder.sessionAttr(WebUtil.USER_NAME, "admin"); requestBuilder.sessionAttr(WebUtil.USER_NAME, "admin");
perform = mockMvc.perform(requestBuilder); perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk()) perform.andExpect(status().isOk())
.andExpect(jsonPath("$.data.logined").value(true)) .andExpect(jsonPath("$.logined").value(true))
.andExpect(jsonPath("$.data.loginRequired").value(true)); .andExpect(jsonPath("$.loginRequired").value(true));
} }
@Test @Test
public void testLogin() throws Exception { public void testLogin() throws Exception {
final String url = "/login/login.do"; final String url = "/login/login.do";
final String username = "admin"; final String username = "admin";
final String rightPwd = "admin"; final String rightPwd = "admin";
final String wrongPwd = "rocketmq"; final String wrongPwd = "rocketmq";
{
UserServiceImpl.FileBasedUserInfoStore store // 模拟 userService.queryByName 方法返回一个用户
= new UserServiceImpl.FileBasedUserInfoStore(configure); User user = new User("admin", "admin", 1);
User user = store.queryByName(username); user.setPassword(rightPwd);
Assert.assertNotNull(user);
Assert.assertEquals(user.getPassword(), rightPwd);
ReflectionTestUtils.setField(userService, "fileBasedUserInfoStore", store);
}
// 1、login fail // 1、login fail
requestBuilder = MockMvcRequestBuilders.post(url); perform = mockMvc.perform(post(url)
requestBuilder.param("username", username) .param("username", username)
.param("password", wrongPwd); .param("password", wrongPwd));
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk()) perform.andExpect(status().isOk())
.andExpect(jsonPath("$.data").doesNotExist()) .andExpect(jsonPath("$.data").doesNotExist())
.andExpect(jsonPath("$.status").value(-1)) .andExpect(jsonPath("$.status").value(-1))
.andExpect(jsonPath("$.errMsg").value("Bad username or password!")); .andExpect(jsonPath("$.errMsg").value("Bad username or password!"));
// 2、login success when(userService.queryByUsernameAndPassword(username, rightPwd)).thenReturn(user);
requestBuilder = MockMvcRequestBuilders.post(url);
requestBuilder.param("username", username)
.param("password", rightPwd);
perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.data.contextPath").value(contextPath));
// 2、login success
perform = mockMvc.perform(post(url)
.param("username", username)
.param("password", rightPwd));
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.contextPath").value(contextPath));
} }
@Test @Test
public void testLogout() throws Exception { public void testLogout() throws Exception {
final String url = "/login/logout.do"; final String url = "/login/logout.do";
requestBuilder = MockMvcRequestBuilders.post(url); requestBuilder = post(url);
requestBuilder.sessionAttr(WebUtil.USER_NAME, "root"); requestBuilder.sessionAttr(WebUtil.USER_NAME, "root");
perform = mockMvc.perform(requestBuilder); perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk()) perform.andExpect(status().isOk())
.andExpect(jsonPath("$.data").value(contextPath)); .andExpect(jsonPath("$.data").value(contextPath));
} }
@Override protected Object getTestController() { @Override
protected Object getTestController() {
return loginController; return loginController;
} }
} }

View File

@@ -18,11 +18,6 @@ package org.apache.rocketmq.dashboard.controller;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.rocketmq.client.QueryResult; import org.apache.rocketmq.client.QueryResult;
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer; import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
import org.apache.rocketmq.client.consumer.PullResult; import org.apache.rocketmq.client.consumer.PullResult;
@@ -32,28 +27,33 @@ import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageClientIDSetter; import org.apache.rocketmq.common.message.MessageClientIDSetter;
import org.apache.rocketmq.common.message.MessageExt; import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue; import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.protocol.body.CMResult;
import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult;
import org.apache.rocketmq.remoting.protocol.body.ConsumerConnection;
import org.apache.rocketmq.dashboard.model.QueueOffsetInfo; import org.apache.rocketmq.dashboard.model.QueueOffsetInfo;
import org.apache.rocketmq.dashboard.model.request.MessageQuery; import org.apache.rocketmq.dashboard.model.request.MessageQuery;
import org.apache.rocketmq.dashboard.service.impl.MessageServiceImpl; import org.apache.rocketmq.dashboard.service.impl.MessageServiceImpl;
import org.apache.rocketmq.dashboard.support.AutoCloseConsumerWrapper;
import org.apache.rocketmq.dashboard.util.MockObjectUtil; import org.apache.rocketmq.dashboard.util.MockObjectUtil;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.remoting.protocol.body.CMResult;
import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult;
import org.apache.rocketmq.remoting.protocol.body.ConsumerConnection;
import org.apache.rocketmq.tools.admin.api.MessageTrack; import org.apache.rocketmq.tools.admin.api.MessageTrack;
import org.apache.rocketmq.tools.admin.api.TrackType; import org.apache.rocketmq.tools.admin.api.TrackType;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy; import org.mockito.Spy;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.*;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
@@ -71,6 +71,9 @@ public class MessageControllerTest extends BaseControllerTest {
private DefaultMQPullConsumer defaultMQPullConsumer; private DefaultMQPullConsumer defaultMQPullConsumer;
@Mock
private AutoCloseConsumerWrapper autoCloseConsumerWrapper;
@Before @Before
public void init() throws Exception { public void init() throws Exception {
super.mockRmqConfigure(); super.mockRmqConfigure();
@@ -89,7 +92,6 @@ public class MessageControllerTest extends BaseControllerTest {
when(pullResult.getNextBeginOffset()).thenReturn(Long.MAX_VALUE); when(pullResult.getNextBeginOffset()).thenReturn(Long.MAX_VALUE);
when(pullResult.getPullStatus()).thenReturn(PullStatus.FOUND); when(pullResult.getPullStatus()).thenReturn(PullStatus.FOUND);
when(pullResult.getMsgFoundList()).thenReturn(wrappers); when(pullResult.getMsgFoundList()).thenReturn(wrappers);
when(messageService.buildDefaultMQPullConsumer(any(), anyBoolean())).thenReturn(defaultMQPullConsumer);
// Ensure searchOffset returns values that make sense for the test times // Ensure searchOffset returns values that make sense for the test times
when(defaultMQPullConsumer.searchOffset(any(MessageQueue.class), anyLong())).thenAnswer(invocation -> { when(defaultMQPullConsumer.searchOffset(any(MessageQueue.class), anyLong())).thenAnswer(invocation -> {
@@ -115,6 +117,7 @@ public class MessageControllerTest extends BaseControllerTest {
// Override the previous mock to ensure the test finds messages // Override the previous mock to ensure the test finds messages
when(defaultMQPullConsumer.pull(any(MessageQueue.class), anyString(), anyLong(), anyInt())) when(defaultMQPullConsumer.pull(any(MessageQueue.class), anyString(), anyLong(), anyInt()))
.thenReturn(pullResultWithMessages); .thenReturn(pullResultWithMessages);
when(autoCloseConsumerWrapper.getConsumer(any(RPCHook.class), anyBoolean())).thenReturn(defaultMQPullConsumer);
} }
} }
@@ -269,7 +272,8 @@ public class MessageControllerTest extends BaseControllerTest {
.andExpect(jsonPath("$.data.consumeResult").value(CMResult.CR_LATER.name())); .andExpect(jsonPath("$.data.consumeResult").value(CMResult.CR_LATER.name()));
} }
@Override protected Object getTestController() { @Override
protected Object getTestController() {
return messageController; return messageController;
} }
} }

View File

@@ -16,8 +16,6 @@
*/ */
package org.apache.rocketmq.dashboard.controller; package org.apache.rocketmq.dashboard.controller;
import java.util.ArrayList;
import java.util.List;
import org.apache.rocketmq.client.QueryResult; import org.apache.rocketmq.client.QueryResult;
import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.trace.TraceType; import org.apache.rocketmq.client.trace.TraceType;
@@ -33,6 +31,9 @@ import org.mockito.InjectMocks;
import org.mockito.Spy; import org.mockito.Spy;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
@@ -135,7 +136,8 @@ public class MessageTraceControllerTest extends BaseControllerTest {
.andExpect(jsonPath("$.data.messageTraceViews", hasSize(4))); .andExpect(jsonPath("$.data.messageTraceViews", hasSize(4)));
} }
@Override protected Object getTestController() { @Override
protected Object getTestController() {
return messageTraceController; return messageTraceController;
} }
} }

View File

@@ -17,9 +17,6 @@
package org.apache.rocketmq.dashboard.controller; package org.apache.rocketmq.dashboard.controller;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import java.io.File;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.rocketmq.common.MixAll; import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.dashboard.model.ConsumerMonitorConfig; import org.apache.rocketmq.dashboard.model.ConsumerMonitorConfig;
import org.apache.rocketmq.dashboard.service.impl.MonitorServiceImpl; import org.apache.rocketmq.dashboard.service.impl.MonitorServiceImpl;
@@ -33,6 +30,10 @@ import org.mockito.Spy;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.io.File;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -135,7 +136,8 @@ public class MonitorControllerTest extends BaseControllerTest {
} }
} }
@Override protected Object getTestController() { @Override
protected Object getTestController() {
return monitorController; return monitorController;
} }
} }

View File

@@ -44,7 +44,8 @@ public class NamesvrControllerTest extends BaseControllerTest {
Assert.assertEquals(namesrvAddr, "127.0.0.1:9876"); Assert.assertEquals(namesrvAddr, "127.0.0.1:9876");
} }
@Override protected Object getTestController() { @Override
protected Object getTestController() {
return namesvrController; return namesvrController;
} }
} }

View File

@@ -17,8 +17,6 @@
package org.apache.rocketmq.dashboard.controller; package org.apache.rocketmq.dashboard.controller;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.pool2.impl.GenericObjectPool; import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.rocketmq.dashboard.service.checker.RocketMqChecker; import org.apache.rocketmq.dashboard.service.checker.RocketMqChecker;
import org.apache.rocketmq.dashboard.service.checker.impl.ClusterHealthCheckerImpl; import org.apache.rocketmq.dashboard.service.checker.impl.ClusterHealthCheckerImpl;
@@ -34,6 +32,9 @@ import org.mockito.Spy;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;

View File

@@ -17,28 +17,33 @@
package org.apache.rocketmq.dashboard.controller; package org.apache.rocketmq.dashboard.controller;
import java.util.HashSet;
import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.remoting.protocol.body.Connection;
import org.apache.rocketmq.remoting.protocol.body.ProducerConnection;
import org.apache.rocketmq.dashboard.interceptor.AuthInterceptor; import org.apache.rocketmq.dashboard.interceptor.AuthInterceptor;
import org.apache.rocketmq.dashboard.service.impl.LoginServiceImpl; import org.apache.rocketmq.dashboard.service.impl.LoginServiceImpl;
import org.apache.rocketmq.dashboard.service.impl.ProducerServiceImpl; import org.apache.rocketmq.dashboard.service.impl.ProducerServiceImpl;
import org.apache.rocketmq.dashboard.service.strategy.UserContext;
import org.apache.rocketmq.dashboard.support.GlobalExceptionHandler; import org.apache.rocketmq.dashboard.support.GlobalExceptionHandler;
import org.apache.rocketmq.dashboard.support.GlobalRestfulResponseBodyAdvice; import org.apache.rocketmq.dashboard.support.GlobalRestfulResponseBodyAdvice;
import org.apache.rocketmq.dashboard.util.MyPrintingResultHandler; import org.apache.rocketmq.dashboard.util.MyPrintingResultHandler;
import org.apache.rocketmq.dashboard.util.WebUtil; import org.apache.rocketmq.dashboard.util.WebUtil;
import org.apache.rocketmq.remoting.protocol.LanguageCode; import org.apache.rocketmq.remoting.protocol.LanguageCode;
import org.apache.rocketmq.remoting.protocol.body.Connection;
import org.apache.rocketmq.remoting.protocol.body.ProducerConnection;
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy; import org.mockito.Spy;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.HashSet;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
@@ -52,9 +57,17 @@ public class ProducerControllerTest extends BaseControllerTest {
@Spy @Spy
private ProducerServiceImpl producerService; private ProducerServiceImpl producerService;
@Override protected MockMvc createMockMvc() { @Spy
private LoginServiceImpl loginService;
@Mock
private UserContext userContext;
@Override
protected MockMvc createMockMvc() {
AuthInterceptor authInterceptor = new AuthInterceptor(); AuthInterceptor authInterceptor = new AuthInterceptor();
ReflectionTestUtils.setField(authInterceptor, "loginService", new LoginServiceImpl()); ReflectionTestUtils.setField(authInterceptor, "loginService", loginService);
ReflectionTestUtils.setField(loginService, "userContext", userContext);
MockMvc innerMockMvc = MockMvcBuilders.standaloneSetup(getTestController()) MockMvc innerMockMvc = MockMvcBuilders.standaloneSetup(getTestController())
.addInterceptors(authInterceptor) .addInterceptors(authInterceptor)
.alwaysDo(MyPrintingResultHandler.me()) .alwaysDo(MyPrintingResultHandler.me())
@@ -88,6 +101,7 @@ public class ProducerControllerTest extends BaseControllerTest {
conn.setVersion(LanguageCode.JAVA.getCode()); conn.setVersion(LanguageCode.JAVA.getCode());
connections.add(conn); connections.add(conn);
producerConnection.setConnectionSet(connections); producerConnection.setConnectionSet(connections);
when(userContext.queryByUsername(any(String.class))).thenReturn(UserInfo.of("admin", "admin", "super"));
when(mqAdminExt.examineProducerConnectionInfo(anyString(), anyString())) when(mqAdminExt.examineProducerConnectionInfo(anyString(), anyString()))
.thenThrow(new MQClientException("Not found the producer group connection", null)) .thenThrow(new MQClientException("Not found the producer group connection", null))
.thenReturn(producerConnection); .thenReturn(producerConnection);

View File

@@ -19,49 +19,52 @@ package org.apache.rocketmq.dashboard.controller;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.rocketmq.client.impl.MQClientAPIImpl; import org.apache.rocketmq.client.impl.MQClientAPIImpl;
import org.apache.rocketmq.client.impl.factory.MQClientInstance; import org.apache.rocketmq.client.impl.factory.MQClientInstance;
import org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl; import org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl;
import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus; import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.TopicConfig; import org.apache.rocketmq.common.TopicConfig;
import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats;
import org.apache.rocketmq.remoting.protocol.admin.TopicStatsTable;
import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue; import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.dashboard.model.request.SendTopicMessageRequest;
import org.apache.rocketmq.dashboard.model.request.TopicConfigInfo;
import org.apache.rocketmq.dashboard.model.request.TopicTypeList;
import org.apache.rocketmq.dashboard.service.ClusterInfoService;
import org.apache.rocketmq.dashboard.service.ConsumerService;
import org.apache.rocketmq.dashboard.service.impl.TopicServiceImpl;
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats;
import org.apache.rocketmq.remoting.protocol.admin.TopicStatsTable;
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo; import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
import org.apache.rocketmq.remoting.protocol.body.ConsumerConnection; import org.apache.rocketmq.remoting.protocol.body.ConsumerConnection;
import org.apache.rocketmq.remoting.protocol.body.ConsumerRunningInfo; import org.apache.rocketmq.remoting.protocol.body.ConsumerRunningInfo;
import org.apache.rocketmq.remoting.protocol.body.GroupList; import org.apache.rocketmq.remoting.protocol.body.GroupList;
import org.apache.rocketmq.remoting.protocol.body.TopicList; import org.apache.rocketmq.remoting.protocol.body.TopicList;
import org.apache.rocketmq.remoting.protocol.route.BrokerData;
import org.apache.rocketmq.remoting.protocol.route.QueueData;
import org.apache.rocketmq.remoting.protocol.route.TopicRouteData; import org.apache.rocketmq.remoting.protocol.route.TopicRouteData;
import org.apache.rocketmq.dashboard.model.request.SendTopicMessageRequest;
import org.apache.rocketmq.dashboard.model.request.TopicConfigInfo;
import org.apache.rocketmq.dashboard.model.request.TopicTypeList;
import org.apache.rocketmq.dashboard.service.impl.ConsumerServiceImpl;
import org.apache.rocketmq.dashboard.service.impl.TopicServiceImpl;
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
import org.apache.rocketmq.remoting.RPCHook;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy; import org.mockito.Spy;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.*;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@@ -77,14 +80,23 @@ public class TopicControllerTest extends BaseControllerTest {
@Spy @Spy
private TopicServiceImpl topicService; private TopicServiceImpl topicService;
@Spy @Mock
private ConsumerServiceImpl consumerService; private ConsumerService consumerService;
@Mock
private DefaultMQProducer producer;
@Mock
private ClusterInfoService clusterInfoService;
private String topicName = "topic_test"; private String topicName = "topic_test";
@Before @Before
public void init() { public void init() {
super.mockRmqConfigure(); super.mockRmqConfigure();
ClusterInfo mockClusterInfo = getClusterInfo();
when(clusterInfoService.get()).thenReturn(mockClusterInfo);
} }
@Test @Test
@@ -258,15 +270,35 @@ public class TopicControllerTest extends BaseControllerTest {
public void testSendTopicMessage() throws Exception { public void testSendTopicMessage() throws Exception {
final String url = "/topic/sendTopicMessage.do"; final String url = "/topic/sendTopicMessage.do";
{ {
DefaultMQProducer producer = mock(DefaultMQProducer.class);
doNothing().when(producer).start(); doNothing().when(producer).start();
doNothing().when(producer).shutdown(); doNothing().when(producer).shutdown();
TopicConfig topicConfig = new TopicConfig(topicName);
topicConfig.setReadQueueNums(4);
topicConfig.setWriteQueueNums(4);
topicConfig.setPerm(6);
topicConfig.setOrder(false);
TopicRouteData topicRouteData = new TopicRouteData();
BrokerData brokerData = new BrokerData();
brokerData.setBrokerName("broker-a");
brokerData.setCluster("DefaultCluster");
HashMap<Long, String> brokerAddrs = new HashMap<>();
brokerAddrs.put(0L, "127.0.0.1:9876");
brokerData.setBrokerAddrs(brokerAddrs);
topicRouteData.setBrokerDatas(List.of(brokerData));
topicRouteData.setQueueDatas(List.of(new QueueData()));
topicRouteData.getQueueDatas().get(0).setReadQueueNums(4);
topicRouteData.getQueueDatas().get(0).setWriteQueueNums(4);
topicRouteData.getQueueDatas().get(0).setPerm(6);
topicRouteData.getQueueDatas().get(0).setBrokerName("broker-a");
when(mqAdminExt.examineTopicRouteInfo(topicName)).thenReturn(topicRouteData);
when(topicService.examineTopicConfig(topicName,"broker-a")).thenReturn(topicConfig);
SendResult result = new SendResult(SendStatus.SEND_OK, "7F000001E41A2E5D6D978B82C20F003D", SendResult result = new SendResult(SendStatus.SEND_OK, "7F000001E41A2E5D6D978B82C20F003D",
"0A8E83C300002A9F00000000000013D3", new MessageQueue(), 1000L); "0A8E83C300002A9F00000000000013D3", new MessageQueue(), 1000L);
when(producer.send(any(Message.class))).thenReturn(result); when(producer.send((Message) argThat(msg -> msg != null))).thenReturn(result);
doReturn(producer).when(topicService).buildDefaultMQProducer(anyString(), any(), anyBoolean()); doReturn(producer).when(topicService).buildDefaultMQProducer(any(String.class), any(RPCHook.class), anyBoolean());
} }
Assert.assertNotNull(topicService.buildDefaultMQProducer("group_test", mock(RPCHook.class))); Assert.assertNotNull(topicService.buildDefaultMQProducer(MixAll.SELF_TEST_PRODUCER_GROUP, mock(RPCHook.class),false));
SendTopicMessageRequest request = new SendTopicMessageRequest(); SendTopicMessageRequest request = new SendTopicMessageRequest();
request.setTopic(topicName); request.setTopic(topicName);
request.setMessageBody("hello world"); request.setMessageBody("hello world");
@@ -275,8 +307,8 @@ public class TopicControllerTest extends BaseControllerTest {
requestBuilder.content(JSON.toJSONString(request)); requestBuilder.content(JSON.toJSONString(request));
perform = mockMvc.perform(requestBuilder); perform = mockMvc.perform(requestBuilder);
perform.andExpect(status().isOk()) perform.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(-1)) .andExpect(jsonPath("$.data.sendStatus").value(SendStatus.SEND_OK.name()))
.andExpect(jsonPath("$.errMsg").value(containsString("NullPointerException"))); .andExpect(jsonPath("$.data.msgId").value("7F000001E41A2E5D6D978B82C20F003D"));
} }
@Test @Test
@@ -358,7 +390,8 @@ public class TopicControllerTest extends BaseControllerTest {
.andExpect(jsonPath("$.data.messageTypeList[2]").value("SYSTEM")); .andExpect(jsonPath("$.data.messageTypeList[2]").value("SYSTEM"));
} }
@Override protected Object getTestController() { @Override
protected Object getTestController() {
return topicController; return topicController;
} }
} }

View File

@@ -18,10 +18,6 @@ package org.apache.rocketmq.dashboard.permission;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.io.Files; import com.google.common.io.Files;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.rocketmq.dashboard.BaseTest; import org.apache.rocketmq.dashboard.BaseTest;
import org.apache.rocketmq.dashboard.config.RMQConfigure; import org.apache.rocketmq.dashboard.config.RMQConfigure;
import org.apache.rocketmq.dashboard.model.User; import org.apache.rocketmq.dashboard.model.User;
@@ -43,6 +39,11 @@ import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.request.ServletRequestAttributes;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;

View File

@@ -19,21 +19,21 @@ package org.apache.rocketmq.dashboard.service.impl;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
import org.apache.rocketmq.acl.common.AclClientRPCHook; import org.apache.rocketmq.acl.common.AclClientRPCHook;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer; import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
import org.apache.rocketmq.client.consumer.PullResult; import org.apache.rocketmq.client.consumer.PullResult;
import org.apache.rocketmq.client.consumer.PullStatus; import org.apache.rocketmq.client.consumer.PullStatus;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.Pair; import org.apache.rocketmq.common.Pair;
import org.apache.rocketmq.common.message.MessageClientIDSetter;
import org.apache.rocketmq.common.message.MessageExt; import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue; import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.dashboard.config.RMQConfigure; import org.apache.rocketmq.dashboard.config.RMQConfigure;
import org.apache.rocketmq.dashboard.exception.ServiceException; import org.apache.rocketmq.dashboard.exception.ServiceException;
import org.apache.rocketmq.dashboard.model.MessagePage; import org.apache.rocketmq.dashboard.model.MessagePage;
import org.apache.rocketmq.dashboard.model.MessageQueryByPage;
import org.apache.rocketmq.dashboard.model.MessageView; import org.apache.rocketmq.dashboard.model.MessageView;
import org.apache.rocketmq.dashboard.model.QueueOffsetInfo; import org.apache.rocketmq.dashboard.model.QueueOffsetInfo;
import org.apache.rocketmq.dashboard.model.request.MessageQuery; import org.apache.rocketmq.dashboard.support.AutoCloseConsumerWrapper;
import org.apache.rocketmq.dashboard.model.MessageQueryByPage; import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.remoting.protocol.body.Connection; import org.apache.rocketmq.remoting.protocol.body.Connection;
import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult; import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult;
import org.apache.rocketmq.remoting.protocol.body.ConsumerConnection; import org.apache.rocketmq.remoting.protocol.body.ConsumerConnection;
@@ -42,42 +42,24 @@ import org.apache.rocketmq.tools.admin.api.MessageTrack;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Spy; import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.*;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.Silent.class) @RunWith(MockitoJUnitRunner.Silent.class)
public class MessageServiceImplTest { public class MessageServiceImplTest {
@@ -93,11 +75,15 @@ public class MessageServiceImplTest {
private RMQConfigure configure; private RMQConfigure configure;
@Mock @Mock
private DefaultMQPullConsumer consumer; private DefaultMQPullConsumer defaultMQPullConsumer;
@Mock
private AutoCloseConsumerWrapper autoCloseConsumerWrapper;
@Mock @Mock
private Cache<String, MessagePage> messagePageCache; private Cache<String, MessagePage> messagePageCache;
private static final String TOPIC = "testTopic"; private static final String TOPIC = "testTopic";
private static final String MSG_ID = "testMsgId"; private static final String MSG_ID = "testMsgId";
private static final String CONSUMER_GROUP = "testConsumerGroup"; private static final String CONSUMER_GROUP = "testConsumerGroup";
@@ -109,10 +95,10 @@ public class MessageServiceImplTest {
public void setUp() throws Exception { public void setUp() throws Exception {
// Set up default mock responses // Set up default mock responses
when(configure.getNamesrvAddr()).thenReturn("localhost:9876"); when(configure.getNamesrvAddr()).thenReturn("localhost:9876");
when(configure.getAccessKey()).thenReturn("12345678");
when(configure.getSecretKey()).thenReturn("rocketmq");
when(configure.isUseTLS()).thenReturn(false); when(configure.isUseTLS()).thenReturn(false);
when(autoCloseConsumerWrapper.getConsumer(any(RPCHook.class), anyBoolean())).thenReturn(defaultMQPullConsumer);
// Mock the consumer creation to avoid actual RocketMQ calls
lenient().doReturn(consumer).when(messageService).buildDefaultMQPullConsumer(any(), anyBoolean());
} }
@Test @Test
@@ -194,8 +180,9 @@ public class MessageServiceImplTest {
Set<MessageQueue> messageQueues = new HashSet<>(); Set<MessageQueue> messageQueues = new HashSet<>();
messageQueues.add(new MessageQueue(TOPIC, "broker-1", 0)); messageQueues.add(new MessageQueue(TOPIC, "broker-1", 0));
messageQueues.add(new MessageQueue(TOPIC, "broker-2", 1)); messageQueues.add(new MessageQueue(TOPIC, "broker-2", 1));
when(consumer.fetchSubscribeMessageQueues(TOPIC)).thenReturn(messageQueues); System.out.println("Consumer from wrapper: " + autoCloseConsumerWrapper.getConsumer(new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey())), false));
when(defaultMQPullConsumer.fetchSubscribeMessageQueues(TOPIC)).thenReturn(messageQueues);
System.out.println(defaultMQPullConsumer.fetchSubscribeMessageQueues(TOPIC));
// Setup pull results for both queues // Setup pull results for both queues
PullResult pullResult1 = createPullResult(PullStatus.FOUND, Arrays.asList( PullResult pullResult1 = createPullResult(PullStatus.FOUND, Arrays.asList(
createMessageExt("id1", TOPIC, "body1", 1500), createMessageExt("id1", TOPIC, "body1", 1500),
@@ -210,7 +197,7 @@ public class MessageServiceImplTest {
PullResult emptyResult = createPullResult(PullStatus.NO_NEW_MSG, Collections.emptyList(), 10, 10); PullResult emptyResult = createPullResult(PullStatus.NO_NEW_MSG, Collections.emptyList(), 10, 10);
// First pull gets messages, second pull gets empty to terminate loop // First pull gets messages, second pull gets empty to terminate loop
when(consumer.pull(any(MessageQueue.class), anyString(), anyLong(), anyInt())) when(defaultMQPullConsumer.pull(any(MessageQueue.class), anyString(), anyLong(), anyInt()))
.thenReturn(pullResult1) .thenReturn(pullResult1)
.thenReturn(emptyResult) .thenReturn(emptyResult)
.thenReturn(pullResult2) .thenReturn(pullResult2)
@@ -230,9 +217,8 @@ public class MessageServiceImplTest {
assertEquals("id3", result.get(2).getMsgId()); // 1800 assertEquals("id3", result.get(2).getMsgId()); // 1800
assertEquals("id1", result.get(3).getMsgId()); // 1500 assertEquals("id1", result.get(3).getMsgId()); // 1500
verify(consumer, times(4)).pull(any(MessageQueue.class), eq("*"), anyLong(), anyInt()); verify(defaultMQPullConsumer, times(4)).pull(any(MessageQueue.class), eq("*"), anyLong(), anyInt());
verify(consumer).start();
verify(consumer).shutdown();
} }
@Test @Test
@@ -240,7 +226,7 @@ public class MessageServiceImplTest {
// Setup message queues // Setup message queues
Set<MessageQueue> messageQueues = new HashSet<>(); Set<MessageQueue> messageQueues = new HashSet<>();
messageQueues.add(new MessageQueue(TOPIC, "broker-1", 0)); messageQueues.add(new MessageQueue(TOPIC, "broker-1", 0));
when(consumer.fetchSubscribeMessageQueues(TOPIC)).thenReturn(messageQueues); when(defaultMQPullConsumer.fetchSubscribeMessageQueues(TOPIC)).thenReturn(messageQueues);
// Setup pull results - some messages are outside time range // Setup pull results - some messages are outside time range
PullResult pullResult = createPullResult(PullStatus.FOUND, Arrays.asList( PullResult pullResult = createPullResult(PullStatus.FOUND, Arrays.asList(
@@ -251,7 +237,7 @@ public class MessageServiceImplTest {
PullResult emptyResult = createPullResult(PullStatus.NO_NEW_MSG, Collections.emptyList(), 10, 10); PullResult emptyResult = createPullResult(PullStatus.NO_NEW_MSG, Collections.emptyList(), 10, 10);
when(consumer.pull(any(MessageQueue.class), anyString(), anyLong(), anyInt())) when(defaultMQPullConsumer.pull(any(MessageQueue.class), anyString(), anyLong(), anyInt()))
.thenReturn(pullResult) .thenReturn(pullResult)
.thenReturn(emptyResult); .thenReturn(emptyResult);
@@ -270,7 +256,7 @@ public class MessageServiceImplTest {
// Setup message queues // Setup message queues
Set<MessageQueue> messageQueues = new HashSet<>(); Set<MessageQueue> messageQueues = new HashSet<>();
messageQueues.add(new MessageQueue(TOPIC, "broker-1", 0)); messageQueues.add(new MessageQueue(TOPIC, "broker-1", 0));
when(consumer.fetchSubscribeMessageQueues(TOPIC)).thenReturn(messageQueues); when(defaultMQPullConsumer.fetchSubscribeMessageQueues(TOPIC)).thenReturn(messageQueues);
// Test all different pull statuses // Test all different pull statuses
PullResult pullResult1 = createPullResult(PullStatus.FOUND, PullResult pullResult1 = createPullResult(PullStatus.FOUND,
@@ -285,7 +271,7 @@ public class MessageServiceImplTest {
PullResult pullResult4 = createPullResult(PullStatus.OFFSET_ILLEGAL, PullResult pullResult4 = createPullResult(PullStatus.OFFSET_ILLEGAL,
Collections.emptyList(), 7, 8); Collections.emptyList(), 7, 8);
when(consumer.pull(any(MessageQueue.class), anyString(), anyLong(), anyInt())) when(defaultMQPullConsumer.pull(any(MessageQueue.class), anyString(), anyLong(), anyInt()))
.thenReturn(pullResult1) .thenReturn(pullResult1)
.thenReturn(pullResult2) .thenReturn(pullResult2)
.thenReturn(pullResult3) .thenReturn(pullResult3)
@@ -447,22 +433,6 @@ public class MessageServiceImplTest {
assertEquals(10, (qo1.getEndOffset() - 5L) + (qo2.getEndOffset() - 10L)); assertEquals(10, (qo1.getEndOffset() - 5L) + (qo2.getEndOffset() - 10L));
} }
@Test
public void testBuildDefaultMQPullConsumer() {
// Test with TLS enabled
DefaultMQPullConsumer tlsConsumer = messageService.buildDefaultMQPullConsumer(null, true);
assertNotNull(tlsConsumer);
// Test with TLS disabled
DefaultMQPullConsumer nonTlsConsumer = messageService.buildDefaultMQPullConsumer(null, false);
assertNotNull(nonTlsConsumer);
// Test with RPC hook
AclClientRPCHook rpcHook = mock(AclClientRPCHook.class);
DefaultMQPullConsumer hookConsumer = messageService.buildDefaultMQPullConsumer(rpcHook, false);
assertNotNull(hookConsumer);
}
// Helper methods // Helper methods
private MessageExt createMessageExt(String msgId, String topic, String body, long storeTimestamp) { private MessageExt createMessageExt(String msgId, String topic, String body, long storeTimestamp) {

View File

@@ -17,15 +17,27 @@
package org.apache.rocketmq.dashboard.service.impl; package org.apache.rocketmq.dashboard.service.impl;
import org.apache.rocketmq.acl.common.AclClientRPCHook;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionMQProducer;
import org.apache.rocketmq.common.attribute.TopicMessageType;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.dashboard.BaseTest;
import org.apache.rocketmq.dashboard.config.RMQConfigure;
import org.apache.rocketmq.dashboard.model.request.SendTopicMessageRequest;
import org.apache.rocketmq.dashboard.model.request.TopicConfigInfo; import org.apache.rocketmq.dashboard.model.request.TopicConfigInfo;
import org.apache.rocketmq.dashboard.model.request.TopicTypeList; import org.apache.rocketmq.dashboard.model.request.TopicTypeList;
import org.apache.rocketmq.dashboard.util.MockObjectUtil; import org.apache.rocketmq.dashboard.service.ClusterInfoService;
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
import org.apache.rocketmq.remoting.protocol.body.TopicList; import org.apache.rocketmq.remoting.protocol.body.TopicList;
import org.apache.rocketmq.tools.admin.MQAdminExt; import org.apache.rocketmq.tools.admin.MQAdminExt;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Spy; import org.mockito.Spy;
@@ -36,38 +48,11 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.*;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
import org.apache.rocketmq.acl.common.AclClientRPCHook;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionMQProducer;
import org.apache.rocketmq.common.attribute.TopicMessageType;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.dashboard.config.RMQConfigure;
import org.apache.rocketmq.dashboard.model.request.SendTopicMessageRequest;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.lenient;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class TopicServiceImplTest { public class TopicServiceImplTest extends BaseTest {
@InjectMocks @InjectMocks
@Spy @Spy
@@ -79,59 +64,16 @@ public class TopicServiceImplTest {
@Mock @Mock
private RMQConfigure configure; private RMQConfigure configure;
@Mock
private ClusterInfoService clusterInfoService;
@Before @Before
public void setUp() { public void setUp() {
// Setup common mocks
when(configure.getNamesrvAddr()).thenReturn("localhost:9876"); when(configure.getNamesrvAddr()).thenReturn("localhost:9876");
// Use lenient() to prevent the unnecessary stubbing error // Use lenient() to prevent the unnecessary stubbing error
lenient().when(configure.isUseTLS()).thenReturn(false); lenient().when(configure.isUseTLS()).thenReturn(false);
} }
@Test
public void testExamineAllTopicType() throws Exception {
// Create mock TopicList with different types of topics
TopicList topicList = new TopicList();
Set<String> topicSet = new HashSet<>();
topicSet.add("normalTopic");
topicSet.add("%RETRY%someGroup");
topicSet.add("%DLQ%someGroup");
topicSet.add("%SYS%sysTopic");
topicList.setTopicList(topicSet);
// Mock fetchAllTopicList to return our test topics
doReturn(topicList).when(topicService).fetchAllTopicList(anyBoolean(), anyBoolean());
// Mock examineTopicConfig for the normal topic
TopicConfigInfo configInfo = new TopicConfigInfo();
configInfo.setMessageType("NORMAL");
List<TopicConfigInfo> topicConfigInfos = new ArrayList<>();
topicConfigInfos.add(configInfo);
doReturn(topicConfigInfos).when(topicService).examineTopicConfig(anyString());
// Call the method being tested
TopicTypeList result = topicService.examineAllTopicType();
// Verify the results
Assert.assertNotNull(result);
Assert.assertEquals(4, result.getTopicNameList().size());
Assert.assertEquals(4, result.getMessageTypeList().size());
// Verify that the topics contain the expected names and types
// Note: the actual order might be different due to sorting in the method
// So we're checking that all expected items are included
Assert.assertTrue(result.getTopicNameList().contains("normalTopic"));
Assert.assertTrue(result.getTopicNameList().contains("%RETRY%someGroup"));
Assert.assertTrue(result.getTopicNameList().contains("%DLQ%someGroup"));
Assert.assertTrue(result.getTopicNameList().contains("%SYS%sysTopic"));
// Verify message types
Assert.assertTrue(result.getMessageTypeList().contains("NORMAL"));
Assert.assertTrue(result.getMessageTypeList().contains("RETRY"));
Assert.assertTrue(result.getMessageTypeList().contains("DELAY"));
Assert.assertTrue(result.getMessageTypeList().contains("SYSTEM"));
}
@Test @Test
public void testSendTopicMessageRequestNormal() throws Exception { public void testSendTopicMessageRequestNormal() throws Exception {
// Prepare test data // Prepare test data

View File

@@ -21,6 +21,28 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.dashboard.BaseTest;
import org.apache.rocketmq.dashboard.config.CollectExecutorConfig;
import org.apache.rocketmq.dashboard.config.RMQConfigure;
import org.apache.rocketmq.dashboard.service.impl.DashboardCollectServiceImpl;
import org.apache.rocketmq.dashboard.util.JsonUtil;
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
import org.apache.rocketmq.remoting.protocol.body.BrokerStatsData;
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
import org.apache.rocketmq.remoting.protocol.body.GroupList;
import org.apache.rocketmq.remoting.protocol.body.KVTable;
import org.apache.rocketmq.remoting.protocol.body.TopicList;
import org.apache.rocketmq.remoting.protocol.route.TopicRouteData;
import org.apache.rocketmq.tools.admin.MQAdminExt;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import java.io.File; import java.io.File;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@@ -32,27 +54,6 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.remoting.protocol.body.BrokerStatsData;
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
import org.apache.rocketmq.remoting.protocol.body.GroupList;
import org.apache.rocketmq.remoting.protocol.body.KVTable;
import org.apache.rocketmq.remoting.protocol.body.TopicList;
import org.apache.rocketmq.remoting.protocol.route.TopicRouteData;
import org.apache.rocketmq.dashboard.BaseTest;
import org.apache.rocketmq.dashboard.config.CollectExecutorConfig;
import org.apache.rocketmq.dashboard.config.RMQConfigure;
import org.apache.rocketmq.dashboard.service.impl.DashboardCollectServiceImpl;
import org.apache.rocketmq.dashboard.util.JsonUtil;
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
import org.apache.rocketmq.tools.admin.MQAdminExt;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;

View File

@@ -18,8 +18,7 @@ package org.apache.rocketmq.dashboard.testbase;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import java.util.List; import jakarta.annotation.Resource;
import javax.annotation.Resource;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
@@ -29,16 +28,18 @@ import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere; import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt; import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
import org.apache.rocketmq.dashboard.model.request.ConsumerConfigInfo; import org.apache.rocketmq.dashboard.model.request.ConsumerConfigInfo;
import org.apache.rocketmq.dashboard.model.request.TopicConfigInfo; import org.apache.rocketmq.dashboard.model.request.TopicConfigInfo;
import org.apache.rocketmq.dashboard.service.ConsumerService; import org.apache.rocketmq.dashboard.service.ConsumerService;
import org.apache.rocketmq.dashboard.service.TopicService; import org.apache.rocketmq.dashboard.service.TopicService;
import org.apache.rocketmq.dashboard.util.JsonUtil; import org.apache.rocketmq.dashboard.util.JsonUtil;
import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import java.util.List;
@ComponentScan(basePackageClasses = {TestRocketMQServer.class}) @ComponentScan(basePackageClasses = {TestRocketMQServer.class})
public abstract class RocketMQConsoleTestBase { public abstract class RocketMQConsoleTestBase {
private Logger consoleTestBaseLog = LoggerFactory.getLogger(RocketMQConsoleTestBase.class); private Logger consoleTestBaseLog = LoggerFactory.getLogger(RocketMQConsoleTestBase.class);
@@ -65,13 +66,13 @@ public abstract class RocketMQConsoleTestBase {
public static abstract class RetryTempLate<T> { public static abstract class RetryTempLate<T> {
protected abstract T process() throws Exception; protected abstract T process() throws Exception;
public T execute(int times, long waitTime) throws Exception { public T execute(int times, long waitTime) throws Exception {
Exception exception = null; Exception exception = null;
for (int i = 0; i < times; i++) { for (int i = 0; i < times; i++) {
try { try {
return process(); return process();
} } catch (Exception ignore) {
catch (Exception ignore) {
exception = ignore; exception = ignore;
if (waitTime > 0) { if (waitTime > 0) {
Thread.sleep(waitTime); Thread.sleep(waitTime);
@@ -84,14 +85,12 @@ public abstract class RocketMQConsoleTestBase {
} }
protected void startTestMQProducer() { protected void startTestMQProducer() {
producer = new DefaultMQProducer(TEST_PRODUCER_GROUP); producer = new DefaultMQProducer(TEST_PRODUCER_GROUP);
producer.setInstanceName(String.valueOf(System.currentTimeMillis())); producer.setInstanceName(String.valueOf(System.currentTimeMillis()));
try { try {
producer.start(); producer.start();
} } catch (Exception e) {
catch (Exception e) {
Throwables.throwIfUnchecked(e); Throwables.throwIfUnchecked(e);
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -107,8 +106,7 @@ public abstract class RocketMQConsoleTestBase {
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
try { try {
return producer.send(message); return producer.send(message);
} } catch (Exception ignore) {
catch (Exception ignore) {
Thread.sleep(500); Thread.sleep(500);
} }
} }
@@ -137,8 +135,7 @@ public abstract class RocketMQConsoleTestBase {
} }
}); });
consumer.start(); consumer.start();
} } catch (Exception e) {
catch (Exception e) {
Throwables.throwIfUnchecked(e); Throwables.throwIfUnchecked(e);
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -17,11 +17,8 @@
package org.apache.rocketmq.dashboard.testbase; package org.apache.rocketmq.dashboard.testbase;
import java.io.File; import jakarta.annotation.PostConstruct;
import java.text.SimpleDateFormat; import jakarta.annotation.PreDestroy;
import java.util.Date;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.rocketmq.broker.BrokerController; import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.common.BrokerConfig; import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.MixAll; import org.apache.rocketmq.common.MixAll;
@@ -34,6 +31,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import static java.io.File.separator; import static java.io.File.separator;
import static org.apache.rocketmq.dashboard.testbase.TestConstant.TEST_BROKER_NAME; import static org.apache.rocketmq.dashboard.testbase.TestConstant.TEST_BROKER_NAME;
import static org.apache.rocketmq.dashboard.testbase.TestConstant.TEST_CLUSTER_NAME; import static org.apache.rocketmq.dashboard.testbase.TestConstant.TEST_CLUSTER_NAME;
@@ -100,8 +101,7 @@ public class TestRocketMQServer {
namesrvController.initialize(); namesrvController.initialize();
log.info("Success to start Name Server:{}", TestConstant.NAME_SERVER_ADDRESS); log.info("Success to start Name Server:{}", TestConstant.NAME_SERVER_ADDRESS);
namesrvController.start(); namesrvController.start();
} } catch (Exception e) {
catch (Exception e) {
log.error("Failed to start Name Server", e); log.error("Failed to start Name Server", e);
System.exit(1); System.exit(1);
} }
@@ -124,8 +124,7 @@ public class TestRocketMQServer {
log.info("Broker Start name:{} address:{}", brokerConfig.getBrokerName(), brokerController.getBrokerAddr()); log.info("Broker Start name:{} address:{}", brokerConfig.getBrokerName(), brokerController.getBrokerAddr());
brokerController.start(); brokerController.start();
} } catch (Exception e) {
catch (Exception e) {
log.error("Failed to start Broker", e); log.error("Failed to start Broker", e);
System.exit(1); System.exit(1);
} }
@@ -144,8 +143,7 @@ public class TestRocketMQServer {
} }
if (file.isFile()) { if (file.isFile()) {
file.delete(); file.delete();
} } else if (file.isDirectory()) {
else if (file.isDirectory()) {
File[] files = file.listFiles(); File[] files = file.listFiles();
for (File file1 : files) { for (File file1 : files) {
deleteFile(file1); deleteFile(file1);

View File

@@ -18,17 +18,22 @@
package org.apache.rocketmq.dashboard.util; package org.apache.rocketmq.dashboard.util;
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer; import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.dashboard.support.AutoCloseConsumerWrapper; import org.apache.rocketmq.dashboard.support.AutoCloseConsumerWrapper;
import org.apache.rocketmq.remoting.RPCHook; import org.apache.rocketmq.remoting.RPCHook;
import java.lang.reflect.Field;
import static org.mockito.Mockito.mock;
import org.apache.rocketmq.client.exception.MQClientException;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import java.lang.reflect.Field;
import java.time.Instant; import java.time.Instant;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class AutoCloseConsumerWrapperTests { class AutoCloseConsumerWrapperTests {
@@ -66,7 +71,6 @@ class AutoCloseConsumerWrapperTests {
} }
@Test @Test
void shouldCloseIdleConsumer() throws Exception { void shouldCloseIdleConsumer() throws Exception {
TestableWrapper wrapper = new TestableWrapper(); TestableWrapper wrapper = new TestableWrapper();

View File

@@ -16,34 +16,21 @@
*/ */
package org.apache.rocketmq.dashboard.util; package org.apache.rocketmq.dashboard.util;
import com.google.common.collect.Lists;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.rocketmq.client.producer.LocalTransactionState; import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.trace.TraceConstants; import org.apache.rocketmq.client.trace.TraceConstants;
import org.apache.rocketmq.client.trace.TraceType; import org.apache.rocketmq.client.trace.TraceType;
import org.apache.rocketmq.common.AclConfig;
import org.apache.rocketmq.remoting.protocol.DataVersion;
import org.apache.rocketmq.common.MixAll; import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.PlainAccessConfig;
import org.apache.rocketmq.common.TopicConfig; import org.apache.rocketmq.common.TopicConfig;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.dashboard.model.DlqMessageRequest;
import org.apache.rocketmq.remoting.protocol.DataVersion;
import org.apache.rocketmq.remoting.protocol.LanguageCode;
import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats; import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats;
import org.apache.rocketmq.remoting.protocol.admin.OffsetWrapper; import org.apache.rocketmq.remoting.protocol.admin.OffsetWrapper;
import org.apache.rocketmq.remoting.protocol.admin.TopicOffset; import org.apache.rocketmq.remoting.protocol.admin.TopicOffset;
import org.apache.rocketmq.remoting.protocol.admin.TopicStatsTable; import org.apache.rocketmq.remoting.protocol.admin.TopicStatsTable;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.protocol.body.BrokerStatsData; import org.apache.rocketmq.remoting.protocol.body.BrokerStatsData;
import org.apache.rocketmq.remoting.protocol.body.BrokerStatsItem; import org.apache.rocketmq.remoting.protocol.body.BrokerStatsItem;
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo; import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
@@ -61,9 +48,18 @@ import org.apache.rocketmq.remoting.protocol.route.BrokerData;
import org.apache.rocketmq.remoting.protocol.route.QueueData; import org.apache.rocketmq.remoting.protocol.route.QueueData;
import org.apache.rocketmq.remoting.protocol.route.TopicRouteData; import org.apache.rocketmq.remoting.protocol.route.TopicRouteData;
import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig; import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
import org.apache.rocketmq.dashboard.model.DlqMessageRequest;
import org.apache.rocketmq.remoting.protocol.LanguageCode; import java.net.InetSocketAddress;
import org.checkerframework.checker.units.qual.A; import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static org.apache.rocketmq.remoting.protocol.heartbeat.ConsumeType.CONSUME_ACTIVELY; import static org.apache.rocketmq.remoting.protocol.heartbeat.ConsumeType.CONSUME_ACTIVELY;
@@ -317,25 +313,5 @@ public class MockObjectUtil {
return dlqMessages; return dlqMessages;
} }
public static AclConfig createAclConfig() {
PlainAccessConfig adminConfig = new PlainAccessConfig();
adminConfig.setAdmin(true);
adminConfig.setAccessKey("rocketmq2");
adminConfig.setSecretKey("12345678");
PlainAccessConfig normalConfig = new PlainAccessConfig();
normalConfig.setAdmin(false);
normalConfig.setAccessKey("rocketmq");
normalConfig.setSecretKey("123456789");
normalConfig.setDefaultGroupPerm("SUB");
normalConfig.setDefaultTopicPerm("DENY");
normalConfig.setTopicPerms(Lists.newArrayList("topicA=DENY", "topicB=PUB|SUB"));
normalConfig.setGroupPerms(Lists.newArrayList("groupA=DENY", "groupB=PUB|SUB"));
AclConfig aclConfig = new AclConfig();
aclConfig.setPlainAccessConfigs(Lists.newArrayList(adminConfig, normalConfig));
aclConfig.setGlobalWhiteAddrs(Lists.newArrayList("localhost"));
return aclConfig;
}
} }

View File

@@ -17,7 +17,6 @@
package org.apache.rocketmq.dashboard.util; package org.apache.rocketmq.dashboard.util;
import java.util.List;
import org.apache.rocketmq.client.trace.TraceConstants; import org.apache.rocketmq.client.trace.TraceConstants;
import org.apache.rocketmq.client.trace.TraceContext; import org.apache.rocketmq.client.trace.TraceContext;
import org.apache.rocketmq.common.UtilAll; import org.apache.rocketmq.common.UtilAll;
@@ -25,6 +24,8 @@ import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.util.List;
public class MsgTraceDecodeUtilTest { public class MsgTraceDecodeUtilTest {
private StringBuilder pubTraceDataBase; private StringBuilder pubTraceDataBase;
private StringBuilder subTraceDataBase; private StringBuilder subTraceDataBase;

View File

@@ -17,13 +17,14 @@
package org.apache.rocketmq.dashboard.util; package org.apache.rocketmq.dashboard.util;
import java.lang.reflect.Method;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.web.servlet.result.PrintingResultHandler; import org.springframework.test.web.servlet.result.PrintingResultHandler;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Method;
public class MyPrintingResultHandler extends PrintingResultHandler { public class MyPrintingResultHandler extends PrintingResultHandler {
public static MyPrintingResultHandler me() { public static MyPrintingResultHandler me() {
return new MyPrintingResultHandler(); return new MyPrintingResultHandler();

View File

@@ -18,10 +18,8 @@
package org.apache.rocketmq.dashboard.web; package org.apache.rocketmq.dashboard.web;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import java.util.Map;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
@@ -32,6 +30,8 @@ import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)