diff --git a/heimdall-api/src/main/resources/liquibase/changelog/20181018114923-add-privileges-metrics-to-role-admin.xml b/heimdall-api/src/main/resources/liquibase/changelog/20181018114923-add-privileges-metrics-to-role-admin.xml
new file mode 100644
index 00000000..c922aed1
--- /dev/null
+++ b/heimdall-api/src/main/resources/liquibase/changelog/20181018114923-add-privileges-metrics-to-role-admin.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+ INSERT INTO roles_privileges (role_id, privilege_id) select rol.id, priv.id from roles rol, (select PRIVILEGES.id from PRIVILEGES except select privilege_id from roles_privileges where role_id = 1) priv where rol.name = 'ADMIN'
+
+
+
diff --git a/heimdall-api/src/main/resources/liquibase/master.xml b/heimdall-api/src/main/resources/liquibase/master.xml
index ec8dc14d..547b3ad4 100644
--- a/heimdall-api/src/main/resources/liquibase/master.xml
+++ b/heimdall-api/src/main/resources/liquibase/master.xml
@@ -27,8 +27,9 @@
+
-
+
diff --git a/heimdall-core/src/main/java/br/com/conductor/heimdall/core/util/MongoLogConnector.java b/heimdall-core/src/main/java/br/com/conductor/heimdall/core/util/MongoLogConnector.java
index d2b312c8..64e05047 100644
--- a/heimdall-core/src/main/java/br/com/conductor/heimdall/core/util/MongoLogConnector.java
+++ b/heimdall-core/src/main/java/br/com/conductor/heimdall/core/util/MongoLogConnector.java
@@ -194,9 +194,11 @@ private Query prepareRange(Query query, Periods date) {
switch(date) {
case TODAY: {
query.field(insertedOnDate).containsIgnoreCase(LocalDate.now().format(DateTimeFormatter.ISO_DATE));
+ break;
}
case YESTERDAY: {
query.field(insertedOnDate).containsIgnoreCase(LocalDate.now().minusDays(1).format(DateTimeFormatter.ISO_DATE));
+ break;
}
case THIS_WEEK: {
Map week = CalendarUtils.firstAndLastDaysOfWeek(LocalDate.now());
diff --git a/heimdall-frontend/package-lock.json b/heimdall-frontend/package-lock.json
index 06beb364..4858e55c 100644
--- a/heimdall-frontend/package-lock.json
+++ b/heimdall-frontend/package-lock.json
@@ -3121,6 +3121,30 @@
"jsbn": "~0.1.0"
}
},
+ "echarts": {
+ "version": "4.2.0-rc.2",
+ "resolved": "https://registry.npmjs.org/echarts/-/echarts-4.2.0-rc.2.tgz",
+ "integrity": "sha512-5Y4Kyi4eNsRM9Cnl7Q8C6PFVjznBJv1VIiMm/VSQ9zyqeo+ce1695GqUd9v4zfVx+Ow1gnwMJX67h0FNvarScw==",
+ "requires": {
+ "zrender": "4.0.5"
+ }
+ },
+ "echarts-for-react": {
+ "version": "2.0.15-beta.0",
+ "resolved": "https://registry.npmjs.org/echarts-for-react/-/echarts-for-react-2.0.15-beta.0.tgz",
+ "integrity": "sha512-brgogznteCirkvOvl/ZOozkpvBt612LBtDiSGE/pANTIO2+YyGpNfFXAIjEIDAY6yTvgdj+Ei7JISAfflXEn5Q==",
+ "requires": {
+ "fast-deep-equal": "^2.0.1",
+ "size-sensor": "^0.2.0"
+ },
+ "dependencies": {
+ "fast-deep-equal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
+ }
+ }
+ },
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -4267,8 +4291,8 @@
"bundled": true,
"optional": true,
"requires": {
- "co": "4.6.0",
- "json-stable-stringify": "1.0.1"
+ "co": "^4.6.0",
+ "json-stable-stringify": "^1.0.1"
}
},
"ansi-regex": {
@@ -4323,7 +4347,7 @@
"bundled": true,
"optional": true,
"requires": {
- "tweetnacl": "0.14.5"
+ "tweetnacl": "^0.14.3"
}
},
"block-stream": {
@@ -4559,10 +4583,10 @@
"version": "3.1.3",
"bundled": true,
"requires": {
- "boom": "2.10.1",
- "cryptiles": "2.0.5",
- "hoek": "2.16.3",
- "sntp": "1.0.9"
+ "boom": "2.x.x",
+ "cryptiles": "2.x.x",
+ "hoek": "2.x.x",
+ "sntp": "1.x.x"
}
},
"hoek": {
@@ -4640,7 +4664,7 @@
"bundled": true,
"optional": true,
"requires": {
- "jsonify": "0.0.0"
+ "jsonify": "~0.0.0"
}
},
"json-stringify-safe": {
@@ -4811,10 +4835,10 @@
"bundled": true,
"optional": true,
"requires": {
- "deep-extend": "0.4.2",
- "ini": "1.3.4",
- "minimist": "1.2.0",
- "strip-json-comments": "2.0.1"
+ "deep-extend": "~0.4.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
},
"dependencies": {
"minimist": {
@@ -4896,7 +4920,7 @@
"version": "1.0.9",
"bundled": true,
"requires": {
- "hoek": "2.16.3"
+ "hoek": "2.x.x"
}
},
"sshpk": {
@@ -4904,15 +4928,15 @@
"bundled": true,
"optional": true,
"requires": {
- "asn1": "0.2.3",
- "assert-plus": "1.0.0",
- "bcrypt-pbkdf": "1.0.1",
- "dashdash": "1.14.1",
- "ecc-jsbn": "0.1.1",
- "getpass": "0.1.7",
- "jodid25519": "1.0.2",
- "jsbn": "0.1.1",
- "tweetnacl": "0.14.5"
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jodid25519": "^1.0.0",
+ "jsbn": "~0.1.0",
+ "tweetnacl": "~0.14.0"
},
"dependencies": {
"assert-plus": {
@@ -4926,16 +4950,16 @@
"version": "1.0.2",
"bundled": true,
"requires": {
- "code-point-at": "1.1.0",
- "is-fullwidth-code-point": "1.0.0",
- "strip-ansi": "3.0.1"
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
}
},
"string_decoder": {
"version": "1.0.1",
"bundled": true,
"requires": {
- "safe-buffer": "5.0.1"
+ "safe-buffer": "^5.0.1"
}
},
"stringstream": {
@@ -4947,7 +4971,7 @@
"version": "3.0.1",
"bundled": true,
"requires": {
- "ansi-regex": "2.1.1"
+ "ansi-regex": "^2.0.0"
}
},
"strip-json-comments": {
@@ -4984,7 +5008,7 @@
"bundled": true,
"optional": true,
"requires": {
- "punycode": "1.4.1"
+ "punycode": "^1.4.1"
}
},
"tunnel-agent": {
@@ -4992,7 +5016,7 @@
"bundled": true,
"optional": true,
"requires": {
- "safe-buffer": "5.0.1"
+ "safe-buffer": "^5.0.1"
}
},
"tweetnacl": {
@@ -10196,7 +10220,7 @@
"resolved": "https://registry.npmjs.org/warning/-/warning-2.1.0.tgz",
"integrity": "sha1-ISINnGOvx3qMkhEeARr3Bc4MaQE=",
"requires": {
- "loose-envify": "1.3.1"
+ "loose-envify": "^1.0.0"
}
}
}
@@ -10217,7 +10241,7 @@
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-0.2.2.tgz",
"integrity": "sha1-HjL9W8q2rWiKSBLLDMBO/HXHAU4=",
"requires": {
- "lodash.keys": "3.1.2"
+ "lodash.keys": "^3.1.2"
}
}
}
@@ -11509,6 +11533,11 @@
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
},
+ "size-sensor": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/size-sensor/-/size-sensor-0.2.2.tgz",
+ "integrity": "sha512-dL/IdBhGDvCHlxQgxryqJNEnSWQz4xvKntsW028CgaJLBLSw8rpi7oUVSM4+xnaHbH+BFkXz6H5aMStfH8v2Pg=="
+ },
"slash": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
@@ -13288,6 +13317,11 @@
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo="
}
}
+ },
+ "zrender": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/zrender/-/zrender-4.0.5.tgz",
+ "integrity": "sha512-SintgipGEJPT9Sz2ABRoE4ZD7Yzy7oR7j7KP6H+C9FlbHWnLUfGVK7E8UV27pGwlxAMB0EsnrqhXx5XjAfv/KA=="
}
}
}
diff --git a/heimdall-frontend/package.json b/heimdall-frontend/package.json
index 1e8d070e..adbf1101 100644
--- a/heimdall-frontend/package.json
+++ b/heimdall-frontend/package.json
@@ -11,6 +11,8 @@
"cross-fetch": "^1.1.1",
"css-animation": "^1.4.1",
"dotenv": "^5.0.1",
+ "echarts": "^4.2.0-rc.2",
+ "echarts-for-react": "^2.0.15-beta.0",
"less": "2.7.3",
"lodash.flow": "^3.5.0",
"moment": "^2.22.1",
diff --git a/heimdall-frontend/src/actions/analytics.js b/heimdall-frontend/src/actions/analytics.js
new file mode 100644
index 00000000..5ca668bb
--- /dev/null
+++ b/heimdall-frontend/src/actions/analytics.js
@@ -0,0 +1,62 @@
+import { AnalyticsConstants } from '../constants/actions-types'
+import { analyticsService } from '../services/AnalyticsService'
+
+export const initLoading = () => dispatch => {
+ dispatch({ type: AnalyticsConstants.ANALYTICS_LOADING })
+}
+
+export const finishLoading = () => dispatch => {
+ dispatch({ type: AnalyticsConstants.ANALYTICS_LOADING_FINISH })
+}
+
+export const sendNotification = notification => dispatch => {
+ dispatch({ type: AnalyticsConstants.ANALYTICS_NOTIFICATION, notification })
+}
+
+export const getTopApps = (limit, period) => dispatch => {
+ analyticsService.getAppsTop(limit, period)
+ .then(data => {
+ dispatch({ type: AnalyticsConstants.ANALYTICS_TOP_APPS, topApps: data })
+ dispatch(finishLoading())
+ })
+ .catch(error => {
+ console.log(error)
+ dispatch(finishLoading())
+ })
+}
+
+export const getTopApis = (limit, period) => dispatch => {
+ analyticsService.getApisTop(limit, period)
+ .then(data => {
+ dispatch({ type: AnalyticsConstants.ANALYTICS_TOP_APIS, topApis: data })
+ dispatch(finishLoading())
+ })
+ .catch(error => {
+ console.log(error)
+ dispatch(finishLoading())
+ })
+}
+
+export const getTopAccessTokens = (limit, period) => dispatch => {
+ analyticsService.getAccessTokensTop(limit, period)
+ .then(data => {
+ dispatch({ type: AnalyticsConstants.ANALYTICS_TOP_ACCESS_TOKENS, topAccessTokens: data })
+ dispatch(finishLoading())
+ })
+ .catch(error => {
+ console.log(error)
+ dispatch(finishLoading())
+ })
+}
+
+export const getTopResultStatus = (limit, period) => dispatch => {
+ analyticsService.getResultStatusTop(limit, period)
+ .then(data => {
+ dispatch({ type: AnalyticsConstants.ANALYTICS_TOP_RESULT_STATUS, topResultStatus: data })
+ dispatch(finishLoading())
+ })
+ .catch(error => {
+ console.log(error)
+ dispatch(finishLoading())
+ })
+}
\ No newline at end of file
diff --git a/heimdall-frontend/src/components/ui/Chart.js b/heimdall-frontend/src/components/ui/Chart.js
new file mode 100644
index 00000000..72f3fac5
--- /dev/null
+++ b/heimdall-frontend/src/components/ui/Chart.js
@@ -0,0 +1,73 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import ReactEcharts from 'echarts-for-react'
+
+import Loading from "./Loading"
+
+class Chart extends React.Component {
+
+ state = {
+ color: 'red'
+ }
+
+ componentDidMount() {
+ this.setState({...this.state, color: this.props.color})
+ }
+
+ getOption = () => {
+ const { metrics } = this.props
+
+ let dataMetrics = []
+ let dataMetricsValues = []
+
+ if (metrics && Array.isArray(metrics)) {
+
+ dataMetrics = metrics.map(metric => {
+ return metric.metric
+ })
+
+ dataMetricsValues = metrics.map(metric => {
+ return metric.value
+ })
+
+ }
+
+ return {
+ title: {
+ text: this.props.title,
+ },
+ tooltip: {},
+ xAxis: {
+ data: dataMetrics
+ },
+ yAxis: {},
+ series: [{
+ name: 'requests',
+ type: 'bar',
+ data: dataMetricsValues
+ }],
+ color: this.state.color
+
+ }
+ }
+
+ render() {
+
+ const { metrics } = this.props
+
+ if (!metrics) {
+ return
+ }
+
+ return (
+
+ )
+ }
+}
+
+Chart.propTypes = {
+ metrics: PropTypes.array,
+ title: PropTypes.string.isRequired,
+}
+
+export default Chart
diff --git a/heimdall-frontend/src/components/ui/SideBar.js b/heimdall-frontend/src/components/ui/SideBar.js
index eaef1480..d5fe47c6 100644
--- a/heimdall-frontend/src/components/ui/SideBar.js
+++ b/heimdall-frontend/src/components/ui/SideBar.js
@@ -56,6 +56,10 @@ class SideBar extends Component {
+
+
+
+
diff --git a/heimdall-frontend/src/constants/actions-types.js b/heimdall-frontend/src/constants/actions-types.js
index 568168f8..948655d3 100644
--- a/heimdall-frontend/src/constants/actions-types.js
+++ b/heimdall-frontend/src/constants/actions-types.js
@@ -178,4 +178,14 @@ export const CacheConstants = {
CACHE_LOADING: 'CACHE_LOADING',
CACHE_LOADING_FINISH: 'CACHE_LOADING_FINISH',
CACHE_NOTIFICATION: 'CACHE_NOTIFICATION'
+}
+
+export const AnalyticsConstants = {
+ ANALYTICS_LOADING: 'ANALYTICS_LOADING',
+ ANALYTICS_LOADING_FINISH: 'ANALYTICS_LOADING_FINISH',
+ ANALYTICS_TOP_APPS: 'ANALYTICS_TOP_APPS',
+ ANALYTICS_TOP_APIS: 'ANALYTICS_TOP_APIS',
+ ANALYTICS_TOP_ACCESS_TOKENS: 'ANALYTICS_TOP_ACCESS_TOKENS',
+ ANALYTICS_TOP_RESULT_STATUS: 'ANALYTICS_TOP_RESULT_STATUS',
+ ANALYTICS_NOTIFICATION: 'ANALYTICS_NOTIFICATION'
}
\ No newline at end of file
diff --git a/heimdall-frontend/src/containers/Analytics.js b/heimdall-frontend/src/containers/Analytics.js
new file mode 100644
index 00000000..e6e6eb97
--- /dev/null
+++ b/heimdall-frontend/src/containers/Analytics.js
@@ -0,0 +1,142 @@
+import React from 'react'
+import { connect } from 'react-redux'
+import {Button, Card, Col, Form, Row, InputNumber, Select, notification} from 'antd'
+
+import Chart from "../components/ui/Chart"
+import PageHeader from "../components/ui/PageHeader"
+import Loading from "../components/ui/Loading"
+import { getTopAccessTokens, getTopApis, getTopApps, getTopResultStatus, sendNotification} from "../actions/analytics"
+
+class Analytics extends React.Component {
+
+ state = {
+ limit: 5,
+ period: "THIS_MONTH",
+ topApps: [],
+ topApis: [],
+ topResultStatus: [],
+ topAccessTokens: []
+ }
+
+ componentDidMount() {
+
+ const { limit, period } = this.state
+ this.getCharts(limit, period)
+ }
+
+ componentWillUpdate(nextProps, nextState) {
+ const { limit, period } = this.state
+
+ if (nextState.limit !== limit || nextState.period !== period) {
+ this.getCharts(nextState.limit, nextState.period)
+ }
+ }
+
+ componentWillReceiveProps(newProps) {
+ if (newProps.notification && newProps.notification !== this.props.notification) {
+ const { type, message, description } = newProps.notification
+ notification[type]({ message, description })
+ }
+ }
+
+ onSearchForm = () => {
+ this.props.form.validateFieldsAndScroll((err, payload) => {
+ if (!err) {
+ this.updateLimitAndPeriod(payload.limit, payload.period)
+ }
+ });
+ }
+
+ updateLimitAndPeriod = (limit, period) => {
+ this.setState({...this.state, limit: limit, period: period })
+ }
+
+ getCharts = (limit, period) => {
+ this.props.dispatch(getTopApps(limit, period))
+ this.props.dispatch(getTopApis(limit, period))
+ this.props.dispatch(getTopAccessTokens(limit, period))
+ this.props.dispatch(getTopResultStatus(limit, period))
+ this.props.dispatch(sendNotification({ type: 'success', message: 'Metrics update!' }))
+ }
+
+ render() {
+ const { topApps, topResultStatus, topAccessTokens, topApis, loading } = this.props
+
+ const {getFieldDecorator} = this.props.form
+
+ if (loading) {
+ return
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ }
+}
+
+const AnalyticsWrapped = Form.create({})(Analytics)
+
+const mapStateToProps = state => {
+ return {
+ topApps: state.analytics.topApps,
+ topApis: state.analytics.topApis,
+ topAccessTokens: state.analytics.topAccessTokens,
+ topResultStatus: state.analytics.topResultStatus,
+ loading: state.analytics.loading,
+ notification: state.analytics.notification
+ }
+}
+
+export default connect(mapStateToProps)(AnalyticsWrapped)
\ No newline at end of file
diff --git a/heimdall-frontend/src/reducers/Analytics.js b/heimdall-frontend/src/reducers/Analytics.js
new file mode 100644
index 00000000..7a409e3b
--- /dev/null
+++ b/heimdall-frontend/src/reducers/Analytics.js
@@ -0,0 +1,22 @@
+import {AnalyticsConstants} from "../constants/actions-types"
+
+export default (state = {}, action) => {
+ switch (action.type) {
+ case AnalyticsConstants.ANALYTICS_LOADING:
+ return { ...state, loading: true }
+ case AnalyticsConstants.ANALYTICS_LOADING_FINISH:
+ return { ...state, loading: false }
+ case AnalyticsConstants.ANALYTICS_TOP_APPS:
+ return { ...state, topApps: action.topApps }
+ case AnalyticsConstants.ANALYTICS_TOP_APIS:
+ return { ...state, topApis: action.topApis }
+ case AnalyticsConstants.ANALYTICS_TOP_ACCESS_TOKENS:
+ return { ...state, topAccessTokens: action.topAccessTokens }
+ case AnalyticsConstants.ANALYTICS_TOP_RESULT_STATUS:
+ return { ...state, topResultStatus: action.topResultStatus }
+ case AnalyticsConstants.ANALYTICS_NOTIFICATION:
+ return { ...state, notification: action.notification }
+ default:
+ return state
+ }
+}
\ No newline at end of file
diff --git a/heimdall-frontend/src/reducers/index.js b/heimdall-frontend/src/reducers/index.js
index fe92d6b1..449ca230 100644
--- a/heimdall-frontend/src/reducers/index.js
+++ b/heimdall-frontend/src/reducers/index.js
@@ -16,6 +16,7 @@ import roles from './Roles'
import caches from './Caches'
import middlewares from './middlewares'
import traces from './Traces'
+import analytics from './Analytics'
export default combineReducers({
apis,
@@ -33,5 +34,6 @@ export default combineReducers({
roles,
middlewares,
caches,
- traces
+ traces,
+ analytics,
})
\ No newline at end of file
diff --git a/heimdall-frontend/src/routes/index.js b/heimdall-frontend/src/routes/index.js
index 20782fe4..73371fce 100644
--- a/heimdall-frontend/src/routes/index.js
+++ b/heimdall-frontend/src/routes/index.js
@@ -37,6 +37,7 @@ import Users from '../containers/Users';
import SingleUser from '../containers/SingleUser';
import Traces from "../containers/Traces";
import SingleTrace from "../containers/SingleTrace";
+import Analytics from '../containers/Analytics';
const routes = ({ history }) => (
@@ -69,6 +70,7 @@ const routes = ({ history }) => (
+
{/* routes not finded or 404 */}
diff --git a/heimdall-frontend/src/services/AnalyticsService.js b/heimdall-frontend/src/services/AnalyticsService.js
new file mode 100644
index 00000000..89916c35
--- /dev/null
+++ b/heimdall-frontend/src/services/AnalyticsService.js
@@ -0,0 +1,76 @@
+import { HTTPv1 } from "../utils/Http"
+
+const getAppsTop = (limit, period) => {
+ const params = {
+ params: {
+ limit: limit,
+ period: period
+ }
+ }
+ return HTTPv1.get('/metrics/apps/top', params)
+ .then(result => {
+ return Promise.resolve(result.data)
+ })
+ .catch(error => {
+ console.log(error)
+ return Promise.reject(error)
+ })
+}
+
+const getApisTop = (limit, period) => {
+ const params = {
+ params: {
+ limit: limit,
+ period: period
+ }
+ }
+ return HTTPv1.get('/metrics/apis/top', params)
+ .then(result => {
+ return Promise.resolve(result.data)
+ })
+ .catch(error => {
+ console.log(error)
+ return Promise.reject(error)
+ })
+}
+
+const getAccessTokensTop = (limit, period) => {
+ const params = {
+ params: {
+ limit: limit,
+ period: period
+ }
+ }
+ return HTTPv1.get('/metrics/access-tokens/top', params)
+ .then(result => {
+ return Promise.resolve(result.data)
+ })
+ .catch(error => {
+ console.log(error)
+ return Promise.reject(error)
+ })
+}
+
+const getResultStatusTop = (limit, period) => {
+ const params = {
+ params: {
+ limit: limit,
+ period: period
+ }
+ }
+ return HTTPv1.get('/metrics/result-status/top', params)
+ .then(result => {
+ return Promise.resolve(result.data)
+ })
+ .catch(error => {
+ console.log(error)
+ return Promise.reject(error)
+ })
+}
+
+export const analyticsService = {
+ getAppsTop,
+ getApisTop,
+ getAccessTokensTop,
+ getResultStatusTop
+}
\ No newline at end of file
diff --git a/heimdall-frontend/src/utils/ColorUtils.js b/heimdall-frontend/src/utils/ColorUtils.js
index d2445700..486a3232 100644
--- a/heimdall-frontend/src/utils/ColorUtils.js
+++ b/heimdall-frontend/src/utils/ColorUtils.js
@@ -61,9 +61,14 @@ const getColorActivate = (active) => {
}
}
+const getRandomColor = () => {
+ return '#' + ("000000" + Math.random().toString(16).slice(2, 8).toUpperCase()).slice(-6);
+}
+
export default {
getColorMethod,
getColorLevel,
getColorStatus,
- getColorActivate
+ getColorActivate,
+ getRandomColor
}