Merge pull request #3866 from influxdata/fix/hosts-dashboard-header

Fix hosts links rendering
pull/10616/head
Delmer 2018-07-11 14:56:55 -04:00 committed by GitHub
commit 4dab2232a3
13 changed files with 337 additions and 82 deletions

View File

@ -1,7 +1,18 @@
import AJAX from 'src/utils/ajax' import AJAX from 'src/utils/ajax'
import {
linksFromDashboards,
updateActiveDashboardLink,
} from 'src/dashboards/utils/dashboardSwitcherLinks'
import {AxiosResponse} from 'axios' import {AxiosResponse} from 'axios'
import {DashboardsResponse, GetDashboards} from 'src/types/apis/dashboards' import {
DashboardsResponse,
GetDashboards,
LoadLinksOptions,
} from 'src/types/apis/dashboards'
import {DashboardSwitcherLinks} from 'src/types/dashboards'
import {Source} from 'src/types/sources'
export const getDashboards: GetDashboards = () => { export const getDashboards: GetDashboards = () => {
return AJAX<DashboardsResponse>({ return AJAX<DashboardsResponse>({
@ -10,6 +21,20 @@ export const getDashboards: GetDashboards = () => {
}) as Promise<AxiosResponse<DashboardsResponse>> }) as Promise<AxiosResponse<DashboardsResponse>>
} }
export const loadDashboardLinks = async (
source: Source,
{activeDashboard, dashboardsAJAX = getDashboards}: LoadLinksOptions
): Promise<DashboardSwitcherLinks> => {
const {
data: {dashboards},
} = await dashboardsAJAX()
const links = linksFromDashboards(dashboards, source)
const dashboardLinks = updateActiveDashboardLink(links, activeDashboard)
return dashboardLinks
}
export const getDashboard = async dashboardID => { export const getDashboard = async dashboardID => {
try { try {
return await AJAX({ return await AJAX({

View File

@ -0,0 +1,4 @@
export const EMPTY_LINKS = {
links: [],
active: null,
}

View File

@ -24,7 +24,9 @@ import * as notifyActions from 'src/shared/actions/notifications'
import idNormalizer, {TYPE_ID} from 'src/normalizers/id' import idNormalizer, {TYPE_ID} from 'src/normalizers/id'
import {millisecondTimeRange} from 'src/dashboards/utils/time' import {millisecondTimeRange} from 'src/dashboards/utils/time'
import {getDeep} from 'src/utils/wrappers' import {getDeep} from 'src/utils/wrappers'
import * as dashboardSwitcher from 'src/dashboards/utils/dashboardSwitcherLinks'
// APIs
import {loadDashboardLinks} from 'src/dashboards/apis'
// Constants // Constants
import { import {
@ -34,6 +36,7 @@ import {
TEMP_VAR_UPPER_DASHBOARD_TIME, TEMP_VAR_UPPER_DASHBOARD_TIME,
} from 'src/shared/constants' } from 'src/shared/constants'
import {FORMAT_INFLUXQL, defaultTimeRange} from 'src/shared/data/timeRanges' import {FORMAT_INFLUXQL, defaultTimeRange} from 'src/shared/data/timeRanges'
import {EMPTY_LINKS} from 'src/dashboards/constants/dashboardHeader'
// Types // Types
import {WithRouterProps} from 'react-router' import {WithRouterProps} from 'react-router'
@ -125,7 +128,7 @@ class DashboardPage extends Component<Props, State> {
selectedCell: null, selectedCell: null,
scrollTop: 0, scrollTop: 0,
windowHeight: window.innerHeight, windowHeight: window.innerHeight,
dashboardLinks: dashboardSwitcher.EMPTY_LINKS, dashboardLinks: EMPTY_LINKS,
} }
} }
@ -497,14 +500,10 @@ class DashboardPage extends Component<Props, State> {
} }
private getDashboardLinks = async (): Promise<void> => { private getDashboardLinks = async (): Promise<void> => {
const {source, dashboard} = this.props const {source, dashboard: activeDashboard} = this.props
try { try {
const links = await dashboardSwitcher.loadDashboardLinks(source) const dashboardLinks = await loadDashboardLinks(source, {activeDashboard})
const dashboardLinks = dashboardSwitcher.updateActiveDashboardLink(
links,
dashboard
)
this.setState({ this.setState({
dashboardLinks, dashboardLinks,

View File

@ -1,6 +1,3 @@
import {getDashboards} from 'src/dashboards/apis'
import {GetDashboards} from 'src/types/apis/dashboards'
import {Source} from 'src/types/sources' import {Source} from 'src/types/sources'
import {Dashboard, DashboardSwitcherLinks} from 'src/types/dashboards' import {Dashboard, DashboardSwitcherLinks} from 'src/types/dashboards'
@ -9,18 +6,7 @@ export const EMPTY_LINKS = {
active: null, active: null,
} }
export const loadDashboardLinks = async ( export const linksFromDashboards = (
source: Source,
dashboardsAJAX: GetDashboards = getDashboards
): Promise<DashboardSwitcherLinks> => {
const {
data: {dashboards},
} = await dashboardsAJAX()
return linksFromDashboards(dashboards, source)
}
const linksFromDashboards = (
dashboards: Dashboard[], dashboards: Dashboard[],
source: Source source: Source
): DashboardSwitcherLinks => { ): DashboardSwitcherLinks => {

View File

@ -1,6 +1,10 @@
import {proxy} from 'utils/queryUrlGenerator' import {proxy} from 'utils/queryUrlGenerator'
import replaceTemplate from 'src/tempVars/utils/replace' import replaceTemplate from 'src/tempVars/utils/replace'
import AJAX from 'utils/ajax' import AJAX from 'utils/ajax'
import {
linksFromHosts,
updateActiveHostLink,
} from 'src/hosts/utils/hostsSwitcherLinks'
import _ from 'lodash' import _ from 'lodash'
export const getCpuAndLoadForHosts = ( export const getCpuAndLoadForHosts = (
@ -95,7 +99,12 @@ export const getCpuAndLoadForHosts = (
}) })
} }
export async function getAllHosts(proxyLink, telegrafDB) { async function getAllHosts(source) {
const {
telegrafDB,
links: {proxy: proxyLink},
} = source
try { try {
const resp = await proxy({ const resp = await proxy({
source: proxyLink, source: proxyLink,
@ -122,6 +131,16 @@ export async function getAllHosts(proxyLink, telegrafDB) {
} }
} }
export const loadHostsLinks = async (
source,
{activeHost = {}, getHostNamesAJAX = getAllHosts} = {}
) => {
const hostNames = await getHostNamesAJAX(source)
const allLinks = linksFromHosts(hostNames, source)
return updateActiveHostLink(allLinks, activeHost)
}
export const getLayouts = () => export const getLayouts = () =>
AJAX({ AJAX({
method: 'GET', method: 'GET',

View File

@ -1,7 +1,6 @@
import React, {Component} from 'react' import React, {Component} from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import _ from 'lodash'
import classnames from 'classnames' import classnames from 'classnames'
import LayoutRenderer from 'shared/components/LayoutRenderer' import LayoutRenderer from 'shared/components/LayoutRenderer'
@ -15,8 +14,9 @@ import {
getLayouts, getLayouts,
getAppsForHost, getAppsForHost,
getMeasurementsForHost, getMeasurementsForHost,
getAllHosts, loadHostsLinks,
} from 'src/hosts/apis' } from 'src/hosts/apis'
import {EMPTY_LINKS} from 'src/dashboards/constants/dashboardHeader'
import {setAutoRefresh, delayEnablePresentationMode} from 'shared/actions/app' import {setAutoRefresh, delayEnablePresentationMode} from 'shared/actions/app'
import {ErrorHandling} from 'src/shared/decorators/errors' import {ErrorHandling} from 'src/shared/decorators/errors'
@ -27,7 +27,7 @@ class HostPage extends Component {
super(props) super(props)
this.state = { this.state = {
layouts: [], layouts: [],
hosts: {}, hostLinks: EMPTY_LINKS,
timeRange: timeRanges.find(tr => tr.lower === 'now() - 1h'), timeRange: timeRanges.find(tr => tr.lower === 'now() - 1h'),
dygraphs: [], dygraphs: [],
} }
@ -36,7 +36,6 @@ class HostPage extends Component {
async fetchHostsAndMeasurements(layouts) { async fetchHostsAndMeasurements(layouts) {
const {source, params} = this.props const {source, params} = this.props
const hosts = await getAllHosts(source.links.proxy, source.telegraf)
const host = await getAppsForHost( const host = await getAppsForHost(
source.links.proxy, source.links.proxy,
params.hostID, params.hostID,
@ -46,7 +45,7 @@ class HostPage extends Component {
const measurements = await getMeasurementsForHost(source, params.hostID) const measurements = await getMeasurementsForHost(source, params.hostID)
return {host, hosts, measurements} return {host, measurements}
} }
async componentDidMount() { async componentDidMount() {
@ -56,9 +55,7 @@ class HostPage extends Component {
const {location} = this.props const {location} = this.props
// fetching layouts and mappings can be done at the same time // fetching layouts and mappings can be done at the same time
const {hosts, host, measurements} = await this.fetchHostsAndMeasurements( const {host, measurements} = await this.fetchHostsAndMeasurements(layouts)
layouts
)
const focusedApp = location.query.app const focusedApp = location.query.app
@ -74,15 +71,9 @@ class HostPage extends Component {
) )
}) })
// only display hosts in the list if they match the current app const hostLinks = await this.getHostLinks()
let filteredHosts = hosts
if (focusedApp) {
filteredHosts = _.pickBy(hosts, (val, __, ___) => {
return _.get(val, 'apps', []).includes(focusedApp)
})
}
this.setState({layouts: filteredLayouts, hosts: filteredHosts}) // eslint-disable-line react/no-did-mount-set-state this.setState({layouts: filteredLayouts, hostLinks}) // eslint-disable-line react/no-did-mount-set-state
} }
handleChooseTimeRange = ({lower, upper}) => { handleChooseTimeRange = ({lower, upper}) => {
@ -173,7 +164,7 @@ class HostPage extends Component {
handleChooseAutoRefresh, handleChooseAutoRefresh,
handleClickPresentationButton, handleClickPresentationButton,
} = this.props } = this.props
const {timeRange} = this.state const {timeRange, hostLinks} = this.state
return ( return (
<div className="page"> <div className="page">
@ -186,8 +177,7 @@ class HostPage extends Component {
handleChooseAutoRefresh={handleChooseAutoRefresh} handleChooseAutoRefresh={handleChooseAutoRefresh}
handleChooseTimeRange={this.handleChooseTimeRange} handleChooseTimeRange={this.handleChooseTimeRange}
handleClickPresentationButton={handleClickPresentationButton} handleClickPresentationButton={handleClickPresentationButton}
dashboardLinks={this.dashboardLinks} dashboardLinks={hostLinks}
activeDashboardLink={this.activeDashboardLink}
/> />
<FancyScrollbar <FancyScrollbar
className={classnames({ className={classnames({
@ -203,30 +193,16 @@ class HostPage extends Component {
) )
} }
get dashboardLinks() { getHostLinks = async () => {
const {
params: {sourceID},
} = this.props
const {hosts} = this.state
if (!sourceID || !hosts) {
return []
}
return Object.values(hosts).map(({name}) => ({
key: name,
text: name,
to: `/sources/${sourceID}/hosts/${name}`,
}))
}
get activeDashboardLink() {
const { const {
source,
params: {hostID}, params: {hostID},
} = this.props } = this.props
const {dashboardLinks} = this
return dashboardLinks.find(d => d.key === hostID) const activeHost = {name: hostID}
const links = await loadHostsLinks(source, {activeHost})
return links
} }
} }

View File

@ -0,0 +1,32 @@
import {Source} from 'src/types/sources'
import {HostNames, HostName} from 'src/types/hosts'
import {DashboardSwitcherLinks} from 'src/types/dashboards'
export const EMPTY_LINKS = {
links: [],
active: null,
}
export const linksFromHosts = (
hostNames: HostNames,
source: Source
): DashboardSwitcherLinks => {
const links = Object.values(hostNames).map(h => {
return {
key: h.name,
text: h.name,
to: `/sources/${source.id}/hosts/${h.name}`,
}
})
return {links, active: null}
}
export const updateActiveHostLink = (
hostLinks: DashboardSwitcherLinks,
host: HostName
): DashboardSwitcherLinks => {
const active = hostLinks.links.find(link => link.key === host.name)
return {...hostLinks, active}
}

View File

@ -6,3 +6,7 @@ export interface DashboardsResponse {
} }
export type GetDashboards = () => Promise<AxiosResponse<DashboardsResponse>> export type GetDashboards = () => Promise<AxiosResponse<DashboardsResponse>>
export interface LoadLinksOptions {
activeDashboard: Dashboard
dashboardsAJAX?: GetDashboards
}

7
ui/src/types/hosts.ts Normal file
View File

@ -0,0 +1,7 @@
export interface HostNames {
[index: string]: HostName
}
export interface HostName {
name: string
}

View File

@ -0,0 +1,76 @@
import {loadDashboardLinks} from 'src/dashboards/apis'
import {dashboard, source} from 'test/resources'
describe('dashboards.apis.loadDashboardLinks', () => {
const socure = {...source, id: '897'}
const activeDashboard = {
...dashboard,
id: 9001,
name: 'Low Dash',
}
const dashboards = [
{
...dashboard,
id: 123,
name: 'Test Dashboard',
},
activeDashboard,
{
...dashboard,
id: 2282,
name: 'Sample Dash',
},
]
const data = {
dashboards,
}
const axiosResponse = {
data,
status: 200,
statusText: 'Okay',
headers: null,
config: null,
}
const getDashboards = async () => axiosResponse
const options = {
activeDashboard,
dashboardsAJAX: getDashboards,
}
it('can load dashboard links for source', async () => {
const actualLinks = await loadDashboardLinks(socure, options)
const expectedLinks = {
links: [
{
key: '123',
text: 'Test Dashboard',
to: '/sources/897/dashboards/123',
},
{
key: '9001',
text: 'Low Dash',
to: '/sources/897/dashboards/9001',
},
{
key: '2282',
text: 'Sample Dash',
to: '/sources/897/dashboards/2282',
},
],
active: {
key: '9001',
text: 'Low Dash',
to: '/sources/897/dashboards/9001',
},
}
expect(actualLinks).toEqual(expectedLinks)
})
})

View File

@ -1,11 +1,11 @@
import { import {
loadDashboardLinks, linksFromDashboards,
updateActiveDashboardLink, updateActiveDashboardLink,
} from 'src/dashboards/utils/dashboardSwitcherLinks' } from 'src/dashboards/utils/dashboardSwitcherLinks'
import {dashboard, source} from 'test/resources' import {dashboard, source} from 'test/resources'
describe('dashboards.utils.dashboardSwitcherLinks', () => { describe('dashboards.utils.dashboardSwitcherLinks', () => {
describe('loadDashboardLinks', () => { describe('linksFromDashboards', () => {
const socure = {...source, id: '897'} const socure = {...source, id: '897'}
const dashboards = [ const dashboards = [
@ -16,22 +16,8 @@ describe('dashboards.utils.dashboardSwitcherLinks', () => {
}, },
] ]
const data = { it('can build dashboard links for source', () => {
dashboards, const actualLinks = linksFromDashboards(dashboards, socure)
}
const axiosResponse = {
data,
status: 200,
statusText: 'Okay',
headers: null,
config: null,
}
const getDashboards = async () => axiosResponse
it('can load dashboard links for source', async () => {
const actualLinks = await loadDashboardLinks(socure, getDashboards)
const expectedLinks = { const expectedLinks = {
links: [ links: [

View File

@ -0,0 +1,61 @@
import {loadHostsLinks} from 'src/hosts/apis'
import {source} from 'test/resources'
import {HostNames} from 'src/types/hosts'
import {DashboardSwitcherLinks} from 'src/types/dashboards'
describe('hosts.apis.loadHostLinks', () => {
const socure = {...source, id: '897'}
const hostNames: HostNames = {
'zelda.local': {
name: 'zelda.local',
},
'gannon.local': {
name: 'gannon.local',
},
'korok.local': {
name: 'korok.local',
},
}
const hostNamesAJAX = async () => hostNames
const options = {
activeHost: {
name: 'korok.local',
},
getHostNamesAJAX: hostNamesAJAX,
}
it('can load the host links', async () => {
const hostLinks = await loadHostsLinks(socure, options)
const expectedLinks: DashboardSwitcherLinks = {
active: {
key: 'korok.local',
text: 'korok.local',
to: '/sources/897/hosts/korok.local',
},
links: [
{
key: 'zelda.local',
text: 'zelda.local',
to: '/sources/897/hosts/zelda.local',
},
{
key: 'gannon.local',
text: 'gannon.local',
to: '/sources/897/hosts/gannon.local',
},
{
key: 'korok.local',
text: 'korok.local',
to: '/sources/897/hosts/korok.local',
},
],
}
expect(hostLinks).toEqual(expectedLinks)
})
})

View File

@ -0,0 +1,80 @@
import {
updateActiveHostLink,
linksFromHosts,
} from 'src/hosts/utils/hostsSwitcherLinks'
import {source} from 'test/resources'
import {HostNames} from 'src/types/hosts'
describe('hosts.utils.hostSwitcherLinks', () => {
describe('linksFromHosts', () => {
const socure = {...source, id: '897'}
const hostNames: HostNames = {
'zelda.local': {
name: 'zelda.local',
},
'gannon.local': {
name: 'gannon.local',
},
}
it('can build host links for a given source', () => {
const actualLinks = linksFromHosts(hostNames, socure)
const expectedLinks = {
links: [
{
key: 'zelda.local',
text: 'zelda.local',
to: '/sources/897/hosts/zelda.local',
},
{
key: 'gannon.local',
text: 'gannon.local',
to: '/sources/897/hosts/gannon.local',
},
],
active: null,
}
expect(actualLinks).toEqual(expectedLinks)
})
})
describe('updateActiveHostLink', () => {
const link1 = {
key: 'korok.local',
text: 'korok.local',
to: '/sources/897/hosts/korok.local',
}
const link2 = {
key: 'deku.local',
text: 'deku.local',
to: '/sources/897/hosts/deku.local',
}
const activeLink = {
key: 'robbie.local',
text: 'robbie.local',
to: '/sources/897/hosts/robbie.local',
}
const activeHostName = {
name: 'robbie.local',
}
const links = [link1, activeLink, link2]
it('can set the active host link', () => {
const loadedLinks = {
links,
active: null,
}
const actualLinks = updateActiveHostLink(loadedLinks, activeHostName)
const expectedLinks = {links, active: activeLink}
expect(actualLinks).toEqual(expectedLinks)
})
})
})