This is an automated email from the ASF dual-hosted git repository. hanahmily pushed a commit to branch feature/5.0.0 in repository https://gitbox.apache.org/repos/asf/incubator-skywalking-ui.git
commit 5a20076f3e095619c0e29fc46ed3293b7b0bc17e Author: gaohongtao <hanahm...@gmail.com> AuthorDate: Mon Jan 8 21:16:17 2018 +0800 Fetching dashboard data --- src/main/frontend/.roadhogrc.mock.js | 60 ++---- src/main/frontend/src/models/dashboard.js | 28 ++- .../frontend/src/routes/Dashboard/Dashboard.js | 205 ++++++++------------- src/main/frontend/src/services/graphql.js | 8 + src/main/frontend/src/utils/request.js | 8 +- src/main/frontend/src/utils/utils.js | 4 + 6 files changed, 134 insertions(+), 179 deletions(-) diff --git a/src/main/frontend/.roadhogrc.mock.js b/src/main/frontend/.roadhogrc.mock.js index 11cfdbc..e85899f 100644 --- a/src/main/frontend/.roadhogrc.mock.js +++ b/src/main/frontend/.roadhogrc.mock.js @@ -8,51 +8,27 @@ import { delay } from 'roadhog-api-doc'; const noProxy = process.env.NO_PROXY === 'true'; // 代码中会兼容本地 service mock 以及部署站点的静态数据 -const proxy = { +const proxy = mockjs.mock({ // 支持值为 Object 和 Array - 'GET /api/currentUser': { - $desc: "获取当前用户接口", - $params: { - pageSize: { - desc: '分页', - exp: 2, + 'POST /api/dashboard': { + data: { + getClusterBrief: { + 'numOfApplication|1-100': 1, + 'numOfService|1-100': 1, + 'numOfDatabase|1-100': 1, + 'numOfCache|1-100': 1, + 'numOfMQ|1-100': 1, }, - }, - $body: { - name: 'Serati Ma', - avatar: 'https://gw.alipayobjects.com/zos/rmsportal/dRFVcIqZOYPcSNrlJsqQ.png', - userid: '00000001', - notifyCount: 12, - }, - }, - // GET POST 可省略 - 'GET /api/users': [{ - key: '1', - name: 'John Brown', - age: 32, - address: 'New York No. 1 Lake Park', - }, { - key: '2', - name: 'Jim Green', - age: 42, - address: 'London No. 1 Lake Park', - }, { - key: '3', - name: 'Joe Black', - age: 32, - address: 'Sidney No. 1 Lake Park', - }], - 'GET /api/rule': getRule, - 'POST /api/rule': { - $params: { - pageSize: { - desc: '分页', - exp: 2, + getAlarmTrend: { + 'numOfAlarmRate|5': [5, 3, 2,], + }, + getConjecturalApps: { + 'apps|3-5': [{'name|1':['Oracle', 'MySQL', 'ActiveMQ', 'Redis', 'Memcache', 'SQLServer'], 'num|1-20':10}], }, - }, - $body: postRule, + 'getTopNSlowService|10': [{'key|+1': 1, 'name': '@name', 'avgResponseTime|200-1000': 1}], + 'getTopNServerThroughput|10': [{'key|+1': 1, 'name': '@name', 'tps|100-10000': 1}], + } }, - 'GET /api/notices': getNotices, -}; +}); export default noProxy ? {} : delay(proxy, 1000); diff --git a/src/main/frontend/src/models/dashboard.js b/src/main/frontend/src/models/dashboard.js index 2e1190a..0796d23 100644 --- a/src/main/frontend/src/models/dashboard.js +++ b/src/main/frontend/src/models/dashboard.js @@ -1,15 +1,39 @@ -// import { xxx } from '../services/xxx'; +import { query } from '../services/graphql'; + export default { namespace: 'dashboard', - state: {}, + state: { + getClusterBrief: { + numOfApplication: 0, + numOfService: 0, + numOfDatabase: 0, + numOfCache: 0, + numOfMQ: 0, + }, + getAlarmTrend: { + numOfAlarmRate: [], + }, + getConjecturalApps: { + apps: [], + }, + getTopNSlowService: [], + getTopNServerThroughput: [], + }, effects: { *fetch({ payload }, { call, put }) { + const response = yield call(query, 'dashboard', payload); + yield put({ + type: 'save', + payload: response, + }); }, }, + reducers: { save(state, action) { return { ...state, + ...action.payload.data, }; }, }, diff --git a/src/main/frontend/src/routes/Dashboard/Dashboard.js b/src/main/frontend/src/routes/Dashboard/Dashboard.js index 896c9b6..3400e24 100644 --- a/src/main/frontend/src/routes/Dashboard/Dashboard.js +++ b/src/main/frontend/src/routes/Dashboard/Dashboard.js @@ -1,79 +1,53 @@ -import React, { PureComponent } from 'react'; +import React, { Component } from 'react'; import { connect } from 'dva'; -import { Row, Col, Icon, Tooltip, Card, Table } from 'antd'; -import moment from 'moment'; +import { Row, Col, Card, Table } from 'antd'; import { ChartCard, Pie, MiniArea, Field, } from '../../components/Charts'; +import { timeRange } from '../../utils/utils'; @connect(state => ({ dashboard: state.dashboard, + duration: state.global.duration, })) -export default class Dashboard extends PureComponent { +export default class Dashboard extends Component { + componentDidMount() { + this.props.dispatch({ + type: 'dashboard/fetch', + payload: {}, + }); + } + shouldComponentUpdate(nextProps) { + if (this.props.duration !== nextProps.duration) { + this.props.dispatch({ + type: 'dashboard/fetch', + payload: {}, + }); + } + return this.props.dashboard !== nextProps.dashboard; + } render() { const visitData = []; - const beginDay = new Date().getTime(); - - const fakeY = [7, 5, 4, 2, 4, 7, 5, 6, 5, 9, 6, 3, 1, 5, 3, 6, 5]; - for (let i = 0; i < fakeY.length; i += 1) { - visitData.push({ - x: moment(new Date(beginDay + (1000 * 60 * 60 * 24 * i))).format('YYYY-MM-DD'), - y: fakeY[i], + const { numOfAlarmRate } = this.props.dashboard.getAlarmTrend; + let avg = 0; + let max = 0; + let min = 0; + if (numOfAlarmRate && numOfAlarmRate.length > 0) { + timeRange(this.props.duration).forEach((v, i) => { + visitData.push({ x: v, y: numOfAlarmRate[i] }); }); + avg = numOfAlarmRate.reduce((acc, curr) => acc + curr) / numOfAlarmRate.length; + max = numOfAlarmRate.reduce((acc, curr) => { return acc < curr ? curr : acc; }); + min = numOfAlarmRate.reduce((acc, curr) => { return acc > curr ? curr : acc; }); } - const databasePieData = [ - { - x: 'MySQL', - y: 10, - }, - { - x: 'Oracle', - y: 7, - }, - { - x: 'SQLServer', - y: 3, - }, - ]; const tableColumns = [{ - title: 'Time', - dataIndex: 'time', - key: 'time', - }, { title: 'Name', dataIndex: 'name', key: 'name', }, { - title: 'Duration', - dataIndex: 'duration', - key: 'duration', - }]; - - const slowServiceData = [{ - key: '1', - name: 'ServiceA', - time: '2017/12/11 19:22:32', - duration: '5000ms', - }, { - key: '2', - name: 'ServiceA', - time: '2017/12/11 19:22:32', - duration: '5000ms', - }, { - key: '3', - name: 'ServiceA', - time: '2017/12/11 19:22:32', - duration: '5000ms', - }, { - key: '4', - name: 'ServiceA', - time: '2017/12/11 19:22:32', - duration: '5000ms', - }, { - key: '5', - name: 'ServiceA', - time: '2017/12/11 19:22:32', - duration: '5000ms', + title: 'Response Time', + dataIndex: 'avgResponseTime', + key: 'avgResponseTime', }]; const applicationThroughputColumns = [{ @@ -85,101 +59,59 @@ export default class Dashboard extends PureComponent { dataIndex: 'tps', key: 'tps', }]; - - const applicationThroughputData = [{ - key: '1', - name: 'App1', - tps: '500', - }, { - key: '2', - name: 'App1', - tps: '500', - }, { - key: '3', - name: 'App1', - tps: '500', - }, { - key: '4', - name: 'App1', - tps: '500', - }, { - key: '5', - name: 'App1', - tps: '500', - }]; - - const topColResponsiveProps = { - xs: 24, - sm: 12, - md: 12, - lg: 6, - xl: 6, - style: { marginBottom: 24 }, - }; - const middleColResponsiveProps = { - xs: 24, - sm: 24, - md: 24, - lg: 8, - xl: 8, - style: { marginBottom: 24, marginTop: 24 }, - }; return ( <div> <Row gutter={24}> - <Col {...topColResponsiveProps}> + <Col xs={24} sm={24} md={12} lg={6} xl={6}> <ChartCard - title="Total Application" + title="App" avatar={<img style={{ width: 56, height: 56 }} src="app.svg" alt="app" />} - action={<Tooltip title="Tip"><Icon type="info-circle-o" /></Tooltip>} - total={25} + total={this.props.dashboard.getClusterBrief.numOfApplication} /> </Col> - <Col {...topColResponsiveProps}> + <Col xs={24} sm={24} md={12} lg={6} xl={6}> <ChartCard - title="Total Service" + title="Service" avatar={<img style={{ width: 56, height: 56 }} src="service.svg" alt="service" />} - action={<Tooltip title="Tip"><Icon type="info-circle-o" /></Tooltip>} - total={525} + total={this.props.dashboard.getClusterBrief.numOfService} /> </Col> - <Col {...topColResponsiveProps}> + <Col xs={24} sm={24} md={12} lg={6} xl={6}> <ChartCard - title="Total Database" - avatar={<img style={{ width: 56, height: 56 }} src="database.svg" alt="database" />} - action={<Tooltip title="Tip"><Icon type="info-circle-o" /></Tooltip>} - total={18} + title="Store" + avatar={<img style={{ width: 48, height: 56 }} src="database.svg" alt="database" />} + total={this.props.dashboard.getClusterBrief.numOfDatabase + + this.props.dashboard.getClusterBrief.numOfCache} /> </Col> - <Col {...topColResponsiveProps}> + <Col xs={24} sm={24} md={12} lg={6} xl={6}> <ChartCard - title="Total Cache" + title="MQ" avatar={<img style={{ width: 56, height: 56 }} src="redis.svg" alt="redis" />} - action={<Tooltip title="Tip"><Icon type="info-circle-o" /></Tooltip>} - total={5} + total={this.props.dashboard.getClusterBrief.numOfMQ} /> </Col> </Row> <Card bordered={false} - bodyStyle={{ padding: 0 }} + bodyStyle={{ padding: 0, marginTop: 24 }} > <div style={{ height: 480 }}>Topoloy</div> </Card> <Row gutter={24}> - <Col xs={24} sm={24} md={24} lg={24} xl={24} style={{ marginTop: 24 }}> + <Col xs={24} sm={24} md={24} lg={12} xl={12} style={{ marginTop: 24 }}> <ChartCard - title="Avg Application Alert" + title="Avg Application Alarm" avatar={<img style={{ width: 56, height: 56 }} src="alert.svg" alt="app" />} - action={<Tooltip title="Tip"><Icon type="info-circle-o" /></Tooltip>} - total="5%" - footer={<div><Field label="Max" value="10%" /> <Field label="Min" value="2%" /></div>} + total={`${avg.toFixed(2)}%`} + footer={<div><Field label="Max" value={`${max}%`} /> <Field label="Min" value={`${min}%`} /></div>} > <MiniArea + animate={false} color="#D87093" borderColor="#B22222" line="true" - height={96} + height={143} data={visitData} yAxis={{ formatter(val) { @@ -189,49 +121,58 @@ export default class Dashboard extends PureComponent { /> </ChartCard> </Col> - </Row> - <Row gutter={24}> - <Col {...middleColResponsiveProps}> + <Col xs={24} sm={24} md={24} lg={12} xl={12} style={{ marginTop: 24 }}> <Card bordered={false} bodyStyle={{ padding: 0 }} > <Pie + animate={false} hasLegend title="Database" subTitle="Total" - total={databasePieData.reduce((pre, now) => now.y + pre, 0)} - data={databasePieData} + total={this.props.dashboard.getConjecturalApps.apps + .reduce((pre, now) => now.num + pre, 0)} + data={this.props.dashboard.getConjecturalApps.apps + .map((v) => { return { x: v.name, y: v.num }; })} height={300} lineWidth={4} /> </Card> </Col> - <Col {...middleColResponsiveProps}> + </Row> + <Row gutter={24}> + <Col xs={24} sm={24} md={24} lg={16} xl={16} style={{ marginTop: 24 }}> <Card title="Slow Service" bordered={false} bodyStyle={{ padding: 0 }} > <Table + size="small" columns={tableColumns} - dataSource={slowServiceData} + dataSource={this.props.dashboard.getTopNSlowService} pagination={{ style: { marginBottom: 0 }, - pageSize: 5, + pageSize: 10, }} /> </Card> </Col> - <Col {...middleColResponsiveProps}> + <Col xs={24} sm={24} md={24} lg={8} xl={8} style={{ marginTop: 24 }}> <Card title="Application Throughput" bordered={false} bodyStyle={{ padding: 0 }} > <Table + size="small" columns={applicationThroughputColumns} - dataSource={applicationThroughputData} + dataSource={this.props.dashboard.getTopNServerThroughput} + pagination={{ + style: { marginBottom: 0 }, + pageSize: 10, + }} /> </Card> </Col> diff --git a/src/main/frontend/src/services/graphql.js b/src/main/frontend/src/services/graphql.js new file mode 100644 index 0000000..1201019 --- /dev/null +++ b/src/main/frontend/src/services/graphql.js @@ -0,0 +1,8 @@ +import request from '../utils/request'; + +export async function query(namespace, playload) { + return request(`/api/${namespace}`, { + method: 'POST', + body: playload, + }); +} diff --git a/src/main/frontend/src/utils/request.js b/src/main/frontend/src/utils/request.js index 094f7fc..6f83dbf 100644 --- a/src/main/frontend/src/utils/request.js +++ b/src/main/frontend/src/utils/request.js @@ -6,7 +6,7 @@ function checkStatus(response) { return response; } notification.error({ - message: `请求错误 ${response.status}: ${response.url}`, + message: `Request error ${response.status}: ${response.url}`, description: response.statusText, }); const error = new Error(response.statusText); @@ -37,7 +37,9 @@ export default function request(url, options) { return fetch(url, newOptions) .then(checkStatus) - .then(response => response.json()) + .then((response) => { + return response.json(); + }) .catch((error) => { if (error.code) { notification.error({ @@ -47,7 +49,7 @@ export default function request(url, options) { } if ('stack' in error && 'message' in error) { notification.error({ - message: `请求错误: ${url}`, + message: `Request error: ${url}`, description: error.message, }); } diff --git a/src/main/frontend/src/utils/utils.js b/src/main/frontend/src/utils/utils.js index e0dfd71..e4060a1 100644 --- a/src/main/frontend/src/utils/utils.js +++ b/src/main/frontend/src/utils/utils.js @@ -92,3 +92,7 @@ export function digitUppercase(n) { return s.replace(/(零.)*零元/, '元').replace(/(零.)+/g, '零').replace(/^整$/, '零元整'); } + +export function timeRange(duration) { + return Array(15).fill(0).map((v, i) => moment().subtract(i, 'minutes').format('YYYY-MM-DD HH:mm:ss')); +} -- To stop receiving notification emails like this one, please contact "commits@skywalking.apache.org" <commits@skywalking.apache.org>.