fix(ui/dashboard): repair import to remap sources in variables
parent
92553b4e8b
commit
9bd4c82293
|
@ -9,9 +9,9 @@ import DragAndDrop from 'src/shared/components/DragAndDrop'
|
|||
import ImportDashboardMappings from 'src/dashboards/components/import_dashboard_mappings/ImportDashboardMappings'
|
||||
import {notifyDashboardImportFailed} from 'src/shared/copy/notifications'
|
||||
|
||||
import {Dashboard, Cell, Source} from 'src/types'
|
||||
import {Dashboard, Cell, Source, Template} from 'src/types'
|
||||
import {Notification} from 'src/types/notifications'
|
||||
import {ImportedSources} from 'src/types/dashboards'
|
||||
import {ImportedSources, SourceMappings} from 'src/types/dashboards'
|
||||
|
||||
interface Props {
|
||||
source: Source
|
||||
|
@ -59,7 +59,7 @@ class ImportDashboardOverlay extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
private get renderStep(): JSX.Element {
|
||||
const {step, importedSources, cells} = this.state
|
||||
const {step, importedSources, cells, dashboard} = this.state
|
||||
const {source, sources} = this.props
|
||||
|
||||
switch (step) {
|
||||
|
@ -78,6 +78,7 @@ class ImportDashboardOverlay extends PureComponent<Props, State> {
|
|||
source={source}
|
||||
sources={sources}
|
||||
importedSources={importedSources}
|
||||
variables={dashboard.templates || []}
|
||||
onSubmit={this.handleUploadDashboard}
|
||||
/>
|
||||
)
|
||||
|
@ -117,6 +118,19 @@ class ImportDashboardOverlay extends PureComponent<Props, State> {
|
|||
if (!_.isEmpty(dashboard)) {
|
||||
const cells = getDeep<Cell[]>(dashboard, 'cells', [])
|
||||
const importedSources = getDeep<ImportedSources>(meta, 'sources', {})
|
||||
const templates = (dashboard.templates || []) as Template[]
|
||||
templates.forEach(t => {
|
||||
if (
|
||||
t.sourceID &&
|
||||
t.sourceID !== 'dynamic' &&
|
||||
!importedSources[t.sourceID]
|
||||
) {
|
||||
importedSources[t.sourceID] = {
|
||||
name: `Variable source ${t.sourceID}`,
|
||||
link: `/chronograf/v1/sources/${t.sourceID}`,
|
||||
}
|
||||
}
|
||||
})
|
||||
this.setState({
|
||||
cells,
|
||||
dashboard,
|
||||
|
@ -133,12 +147,23 @@ class ImportDashboardOverlay extends PureComponent<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
private handleUploadDashboard = (cells: Cell[]): void => {
|
||||
private handleUploadDashboard = (
|
||||
cells: Cell[],
|
||||
mappings: SourceMappings
|
||||
): void => {
|
||||
const {dashboard} = this.state
|
||||
|
||||
const {onImportDashboard, onDismissOverlay} = this.props
|
||||
const templates = (dashboard.templates || []).map(x => {
|
||||
if (!x.sourceID) {
|
||||
return x
|
||||
} else {
|
||||
const mapping = mappings[x.sourceID]
|
||||
return {...x, sourceID: mapping ? mapping.id : undefined}
|
||||
}
|
||||
})
|
||||
|
||||
onImportDashboard({...dashboard, cells})
|
||||
onImportDashboard({...dashboard, cells, templates})
|
||||
onDismissOverlay()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
import {DYNAMIC_SOURCE, DYNAMIC_SOURCE_ITEM} from 'src/dashboards/constants'
|
||||
|
||||
// Types
|
||||
import {Source, Cell} from 'src/types'
|
||||
import {Source, Cell, Template} from 'src/types'
|
||||
import {
|
||||
SourcesCells,
|
||||
SourceMappings,
|
||||
|
@ -30,7 +30,8 @@ interface Props {
|
|||
source: Source
|
||||
sources: Source[]
|
||||
importedSources: ImportedSources
|
||||
onSubmit: (cells: Cell[]) => void
|
||||
variables: Template[]
|
||||
onSubmit: (cells: Cell[], mappings: SourceMappings) => void
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -45,7 +46,7 @@ class ImportDashboardMappings extends Component<Props, State> {
|
|||
}
|
||||
|
||||
public componentDidMount() {
|
||||
const {cells, importedSources, source} = this.props
|
||||
const {cells, importedSources, source, variables} = this.props
|
||||
|
||||
if (_.isEmpty(cells)) {
|
||||
return
|
||||
|
@ -54,7 +55,8 @@ class ImportDashboardMappings extends Component<Props, State> {
|
|||
const {sourcesCells, sourceMappings} = createSourceMappings(
|
||||
source,
|
||||
cells,
|
||||
importedSources
|
||||
importedSources,
|
||||
variables
|
||||
)
|
||||
|
||||
this.setState({sourcesCells, sourceMappings})
|
||||
|
@ -154,10 +156,8 @@ class ImportDashboardMappings extends Component<Props, State> {
|
|||
|
||||
private getRow(sourceName: string, sourceID: string): JSX.Element {
|
||||
let sourceLabel = `${sourceName} (${sourceID})`
|
||||
let description = 'Cells that use this Source:'
|
||||
if (sourceID === DYNAMIC_SOURCE) {
|
||||
sourceLabel = sourceName
|
||||
description = 'Cells using Dynamic Source:'
|
||||
}
|
||||
return (
|
||||
<tr key={sourceID}>
|
||||
|
@ -165,8 +165,8 @@ class ImportDashboardMappings extends Component<Props, State> {
|
|||
<div className="dash-map--source" data-test="source-label">
|
||||
{sourceLabel}
|
||||
</div>
|
||||
<div className="dash-map--header">{description}</div>
|
||||
{this.getCellsForSource(sourceID)}
|
||||
{this.getVariablesForSource(sourceID)}
|
||||
</td>
|
||||
<td className="dash-map--table-cell dash-map--table-center">
|
||||
{this.arrow}
|
||||
|
@ -225,16 +225,45 @@ class ImportDashboardMappings extends Component<Props, State> {
|
|||
return sources[0].name
|
||||
}
|
||||
|
||||
private getCellsForSource(sourceID: string): JSX.Element[] {
|
||||
private getCellsForSource(sourceID: string): JSX.Element {
|
||||
const {sourcesCells} = this.state
|
||||
|
||||
return _.map(sourcesCells[sourceID], c => {
|
||||
if (sourcesCells[sourceID].length) {
|
||||
return (
|
||||
<div className="dash-map--cell" key={c.id}>
|
||||
{c.name}
|
||||
</div>
|
||||
<>
|
||||
<div className="dash-map--header">Cells that use this Source:</div>
|
||||
{_.map(sourcesCells[sourceID], c => {
|
||||
return (
|
||||
<div className="dash-map--cell" key={c.id}>
|
||||
{c.name}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
private getVariablesForSource(sourceID: string): JSX.Element | undefined {
|
||||
const {variables} = this.props
|
||||
const sourceVariables = variables.filter(x => x.sourceID === sourceID)
|
||||
|
||||
if (sourceVariables.length) {
|
||||
return (
|
||||
<>
|
||||
<div className="dash-map--header">
|
||||
Variables that use this source:
|
||||
</div>
|
||||
|
||||
{_.map(sourceVariables, v => {
|
||||
return (
|
||||
<div className="dash-map--cell" key={`var-${v.id}`}>
|
||||
{v.tempVar}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private handleChooseDropdown = (item: SourceItemValue): void => {
|
||||
|
@ -250,7 +279,7 @@ class ImportDashboardMappings extends Component<Props, State> {
|
|||
|
||||
const mappedCells = mapCells(cells, sourceMappings, importedSources)
|
||||
|
||||
onSubmit(mappedCells)
|
||||
onSubmit(mappedCells, sourceMappings)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import {getDeep} from 'src/utils/wrappers'
|
|||
import {DYNAMIC_SOURCE, DYNAMIC_SOURCE_INFO} from 'src/dashboards/constants'
|
||||
|
||||
// Types
|
||||
import {CellQuery, Cell, Source} from 'src/types'
|
||||
import {CellQuery, Cell, Source, Template} from 'src/types'
|
||||
import {
|
||||
CellInfo,
|
||||
SourceInfo,
|
||||
|
@ -22,7 +22,8 @@ const REGEX_SOURCE_ID = /sources\/(\d+)/g
|
|||
export const createSourceMappings = (
|
||||
source,
|
||||
cells,
|
||||
importedSources
|
||||
importedSources,
|
||||
variables: Template[] = []
|
||||
): {sourcesCells: SourcesCells; sourceMappings: SourceMappings} => {
|
||||
let sourcesCells: SourcesCells = {}
|
||||
const sourceMappings: SourceMappings = {}
|
||||
|
@ -66,6 +67,13 @@ export const createSourceMappings = (
|
|||
},
|
||||
sourcesCells
|
||||
)
|
||||
// add sources also for variables
|
||||
variables.forEach(v => {
|
||||
if (v.sourceID && !sourceMappings[v.sourceID]) {
|
||||
sourceMappings[v.sourceID] = sourceInfo
|
||||
sourcesCells[v.sourceID] = []
|
||||
}
|
||||
})
|
||||
|
||||
if (cellsWithNoSource.length) {
|
||||
sourcesCells[DYNAMIC_SOURCE] = cellsWithNoSource
|
||||
|
|
|
@ -113,8 +113,9 @@ class TemplateVariableEditor extends PureComponent<Props, State> {
|
|||
sourceID = props.template.sourceID
|
||||
selectedSource = props.sources.find(source => source.id === sourceID)
|
||||
if (!selectedSource) {
|
||||
const v = props.template.tempVar
|
||||
console.error(
|
||||
`Template for tempVar '${props.template.tempVar}' uses source '${sourceID}' that does not exist. Using dynamic source.`
|
||||
`Variable '${v}' uses source '${sourceID}' that does not exist.`
|
||||
)
|
||||
sourceID = DYNAMIC_SOURCE_DATABASE_ID
|
||||
}
|
||||
|
@ -482,11 +483,11 @@ class TemplateVariableEditor extends PureComponent<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
const mdtp = {
|
||||
notify: notifyActionCreator,
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const mstp = state => {
|
||||
const {sources} = state
|
||||
|
||||
return {
|
||||
|
@ -494,7 +495,4 @@ const mapStateToProps = state => {
|
|||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(withRouter(TemplateVariableEditor))
|
||||
export default connect(mstp, mdtp)(withRouter(TemplateVariableEditor))
|
||||
|
|
|
@ -11,6 +11,7 @@ const setup = (override = {}) => {
|
|||
sources: [source],
|
||||
importedSources: {},
|
||||
onSubmit: () => {},
|
||||
variables: [],
|
||||
...override,
|
||||
}
|
||||
|
||||
|
|
|
@ -7,10 +7,11 @@ import {
|
|||
} from 'src/dashboards/utils/importDashboardMappings'
|
||||
|
||||
// fixtures
|
||||
import {source, cell, query} from 'test/fixtures'
|
||||
import {source, cell, query, template} from 'test/fixtures'
|
||||
|
||||
// constants
|
||||
import {DYNAMIC_SOURCE, DYNAMIC_SOURCE_INFO} from 'src/dashboards/constants'
|
||||
import {Template} from 'src/types'
|
||||
|
||||
describe('Dashboards.Utils.importDashboardMappings', () => {
|
||||
describe('createSourceMappings', () => {
|
||||
|
@ -150,6 +151,53 @@ describe('Dashboards.Utils.importDashboardMappings', () => {
|
|||
)
|
||||
expect(sourceMappings).toEqual(expected)
|
||||
})
|
||||
it('maps also imported variables', () => {
|
||||
const currentSource = {...source, id: 11, name: 'MY SOURCE'}
|
||||
const sourceLink1 = '/chronograf/v1/sources/1'
|
||||
const cellName1 = 'Cell 1'
|
||||
const cellID1 = '1'
|
||||
const queryWithSource1 = {...query, source: sourceLink1}
|
||||
const cellWithSource1 = {
|
||||
...cell,
|
||||
name: cellName1,
|
||||
queries: [queryWithSource1],
|
||||
i: cellID1,
|
||||
}
|
||||
const cells = [cellWithSource1]
|
||||
const importedSources = {
|
||||
1: {name: 'Source 1', link: sourceLink1},
|
||||
}
|
||||
const sourceInfo = {
|
||||
name: currentSource.name,
|
||||
id: currentSource.id,
|
||||
link: currentSource.links.self,
|
||||
}
|
||||
const variables: Template[] = [
|
||||
{
|
||||
...template,
|
||||
sourceID: undefined,
|
||||
},
|
||||
{
|
||||
...template,
|
||||
id: 'var2',
|
||||
sourceID: '2',
|
||||
},
|
||||
]
|
||||
|
||||
const expectedMapping = {
|
||||
1: sourceInfo,
|
||||
2: sourceInfo,
|
||||
}
|
||||
|
||||
const {sourcesCells, sourceMappings} = createSourceMappings(
|
||||
currentSource,
|
||||
cells,
|
||||
importedSources,
|
||||
variables
|
||||
)
|
||||
expect(sourceMappings).toEqual(expectedMapping)
|
||||
expect(sourcesCells[2]).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('mapQueriesInCell', () => {
|
||||
|
|
|
@ -54,6 +54,14 @@ export const source: Source = {
|
|||
authentication: SourceAuthenticationMethod.Basic,
|
||||
}
|
||||
|
||||
export const template: Template = {
|
||||
id: '1',
|
||||
label: 'var',
|
||||
tempVar: ':var:',
|
||||
type: TemplateType.Constant,
|
||||
values: [],
|
||||
}
|
||||
|
||||
export const service: Service = {
|
||||
id: '1',
|
||||
sourceID: '1',
|
||||
|
|
Loading…
Reference in New Issue