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 {
linksFromDashboards,
updateActiveDashboardLink,
} from 'src/dashboards/utils/dashboardSwitcherLinks'
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 = () => {
return AJAX<DashboardsResponse>({
@ -10,6 +21,20 @@ export const getDashboards: GetDashboards = () => {
}) 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 => {
try {
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 {millisecondTimeRange} from 'src/dashboards/utils/time'
import {getDeep} from 'src/utils/wrappers'
import * as dashboardSwitcher from 'src/dashboards/utils/dashboardSwitcherLinks'
// APIs
import {loadDashboardLinks} from 'src/dashboards/apis'
// Constants
import {
@ -34,6 +36,7 @@ import {
TEMP_VAR_UPPER_DASHBOARD_TIME,
} from 'src/shared/constants'
import {FORMAT_INFLUXQL, defaultTimeRange} from 'src/shared/data/timeRanges'
import {EMPTY_LINKS} from 'src/dashboards/constants/dashboardHeader'
// Types
import {WithRouterProps} from 'react-router'
@ -125,7 +128,7 @@ class DashboardPage extends Component<Props, State> {
selectedCell: null,
scrollTop: 0,
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> => {
const {source, dashboard} = this.props
const {source, dashboard: activeDashboard} = this.props
try {
const links = await dashboardSwitcher.loadDashboardLinks(source)
const dashboardLinks = dashboardSwitcher.updateActiveDashboardLink(
links,
dashboard
)
const dashboardLinks = await loadDashboardLinks(source, {activeDashboard})
this.setState({
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 {Dashboard, DashboardSwitcherLinks} from 'src/types/dashboards'
@ -9,18 +6,7 @@ export const EMPTY_LINKS = {
active: null,
}
export const loadDashboardLinks = async (
source: Source,
dashboardsAJAX: GetDashboards = getDashboards
): Promise<DashboardSwitcherLinks> => {
const {
data: {dashboards},
} = await dashboardsAJAX()
return linksFromDashboards(dashboards, source)
}
const linksFromDashboards = (
export const linksFromDashboards = (
dashboards: Dashboard[],
source: Source
): DashboardSwitcherLinks => {

View File

@ -1,6 +1,10 @@
import {proxy} from 'utils/queryUrlGenerator'
import replaceTemplate from 'src/tempVars/utils/replace'
import AJAX from 'utils/ajax'
import {
linksFromHosts,
updateActiveHostLink,
} from 'src/hosts/utils/hostsSwitcherLinks'
import _ from 'lodash'
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 {
const resp = await proxy({
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 = () =>
AJAX({
method: 'GET',

View File

@ -1,7 +1,6 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import _ from 'lodash'
import classnames from 'classnames'
import LayoutRenderer from 'shared/components/LayoutRenderer'
@ -15,8 +14,9 @@ import {
getLayouts,
getAppsForHost,
getMeasurementsForHost,
getAllHosts,
loadHostsLinks,
} from 'src/hosts/apis'
import {EMPTY_LINKS} from 'src/dashboards/constants/dashboardHeader'
import {setAutoRefresh, delayEnablePresentationMode} from 'shared/actions/app'
import {ErrorHandling} from 'src/shared/decorators/errors'
@ -27,7 +27,7 @@ class HostPage extends Component {
super(props)
this.state = {
layouts: [],
hosts: {},
hostLinks: EMPTY_LINKS,
timeRange: timeRanges.find(tr => tr.lower === 'now() - 1h'),
dygraphs: [],
}
@ -36,7 +36,6 @@ class HostPage extends Component {
async fetchHostsAndMeasurements(layouts) {
const {source, params} = this.props
const hosts = await getAllHosts(source.links.proxy, source.telegraf)
const host = await getAppsForHost(
source.links.proxy,
params.hostID,
@ -46,7 +45,7 @@ class HostPage extends Component {
const measurements = await getMeasurementsForHost(source, params.hostID)
return {host, hosts, measurements}
return {host, measurements}
}
async componentDidMount() {
@ -56,9 +55,7 @@ class HostPage extends Component {
const {location} = this.props
// fetching layouts and mappings can be done at the same time
const {hosts, host, measurements} = await this.fetchHostsAndMeasurements(
layouts
)
const {host, measurements} = await this.fetchHostsAndMeasurements(layouts)
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
let filteredHosts = hosts
if (focusedApp) {
filteredHosts = _.pickBy(hosts, (val, __, ___) => {
return _.get(val, 'apps', []).includes(focusedApp)
})
}
const hostLinks = await this.getHostLinks()
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}) => {
@ -173,7 +164,7 @@ class HostPage extends Component {
handleChooseAutoRefresh,
handleClickPresentationButton,
} = this.props
const {timeRange} = this.state
const {timeRange, hostLinks} = this.state
return (
<div className="page">
@ -186,8 +177,7 @@ class HostPage extends Component {
handleChooseAutoRefresh={handleChooseAutoRefresh}
handleChooseTimeRange={this.handleChooseTimeRange}
handleClickPresentationButton={handleClickPresentationButton}
dashboardLinks={this.dashboardLinks}
activeDashboardLink={this.activeDashboardLink}
dashboardLinks={hostLinks}
/>
<FancyScrollbar
className={classnames({
@ -203,30 +193,16 @@ class HostPage extends Component {
)
}
get dashboardLinks() {
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() {
getHostLinks = async () => {
const {
source,
params: {hostID},
} = 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 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 {
loadDashboardLinks,
linksFromDashboards,
updateActiveDashboardLink,
} from 'src/dashboards/utils/dashboardSwitcherLinks'
import {dashboard, source} from 'test/resources'
describe('dashboards.utils.dashboardSwitcherLinks', () => {
describe('loadDashboardLinks', () => {
describe('linksFromDashboards', () => {
const socure = {...source, id: '897'}
const dashboards = [
@ -16,22 +16,8 @@ describe('dashboards.utils.dashboardSwitcherLinks', () => {
},
]
const data = {
dashboards,
}
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)
it('can build dashboard links for source', () => {
const actualLinks = linksFromDashboards(dashboards, socure)
const expectedLinks = {
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)
})
})
})