Port dashboard from template function to ui from client

pull/12837/head
Deniz Kusefoglu 2019-03-21 16:50:11 -07:00
parent 7ae219ed35
commit dcc7f93edb
9 changed files with 483 additions and 13 deletions

6
ui/package-lock.json generated
View File

@ -985,9 +985,9 @@
}
},
"@influxdata/influx": {
"version": "0.2.47",
"resolved": "https://registry.npmjs.org/@influxdata/influx/-/influx-0.2.47.tgz",
"integrity": "sha512-yvq/aiU1EG7jHbU+k1cBLg5qRAa8Q5tyaqkiLEcIzhFsbzk7LBJ1WVUrnkwO+fRBIIRs6E/Jm7Cfd1+XVd+uKA==",
"version": "0.2.48",
"resolved": "https://registry.npmjs.org/@influxdata/influx/-/influx-0.2.48.tgz",
"integrity": "sha512-CIqUd3hdKa6qQrsqQR3x2+r4r0guETnfTrMWXZ6EDOU5rWwyc2kdrgCJGRviy3RCvtV7+wtrQ1FYd8KSHUR82w==",
"requires": {
"axios": "^0.18.0"
}

View File

@ -137,7 +137,7 @@
},
"dependencies": {
"@influxdata/clockface": "0.0.5",
"@influxdata/influx": "0.2.47",
"@influxdata/influx": "0.2.48",
"@influxdata/react-custom-scrollbars": "4.3.8",
"axios": "^0.18.0",
"babel-polyfill": "^6.26.0",

View File

@ -17,8 +17,7 @@ import {
getView as getViewAJAX,
updateView as updateViewAJAX,
} from 'src/dashboards/apis'
import {client} from 'src/utils/api'
import {createDashboardFromTemplate as createDashboardFromTemplateAJAX} from 'src/templates/api'
// Actions
import {notify} from 'src/shared/actions/notifications'
@ -48,6 +47,7 @@ import {
getClonedDashboardCell,
} from 'src/dashboards/utils/cellGetters'
import {dashboardToTemplate} from 'src/shared/utils/resourceToTemplate'
import {client} from 'src/utils/api'
// Constants
import * as copy from 'src/shared/copy/notifications'
@ -55,8 +55,15 @@ import * as copy from 'src/shared/copy/notifications'
// Types
import {RemoteDataState} from 'src/types'
import {PublishNotificationAction} from 'src/types/actions/notifications'
import {CreateCell, IDashboardTemplate, ILabel} from '@influxdata/influx'
import {Dashboard, NewView, Cell, GetState, View} from 'src/types/v2'
import {
Dashboard,
NewView,
Cell,
GetState,
View,
DashboardTemplate,
} from 'src/types/v2'
import {CreateCell, ILabel} from '@influxdata/influx'
export enum ActionTypes {
LoadDashboards = 'LOAD_DASHBOARDS',
@ -218,11 +225,11 @@ export const getDashboardsAsync = () => async (
}
export const createDashboardFromTemplate = (
template: IDashboardTemplate,
template: DashboardTemplate,
orgID: string
) => async dispatch => {
try {
await client.dashboards.createFromTemplate(template, orgID)
await createDashboardFromTemplateAJAX(template, orgID)
dispatch(notify(importDashboardSucceeded()))
} catch (error) {

View File

@ -24,6 +24,9 @@ import {
import {createDashboardsForPlugins as createDashboardsForPluginsAction} from 'src/protos/actions'
import {notify as notifyAction} from 'src/shared/actions/notifications'
// APIs
import {createDashboardFromTemplate as createDashboardFromTemplateAJAX} from 'src/templates/api'
// Constants
import {
TelegrafDashboardCreated,
@ -31,10 +34,9 @@ import {
} from 'src/shared/copy/notifications'
// Types
import {AppState} from 'src/types/v2/index'
import {AppState, DashboardTemplate} from 'src/types/v2/index'
import {TelegrafPlugin, ConfigurationState} from 'src/types/v2/dataLoaders'
import {client} from 'src/utils/api'
import {IDashboardTemplate} from '@influxdata/influx'
interface DispatchProps {
onSetTelegrafConfigName: typeof setTelegrafConfigName
@ -165,7 +167,7 @@ export class TelegrafPluginInstructions extends PureComponent<Props> {
const templates = await Promise.all(pendingTemplates)
const pendingDashboards = templates.map(t =>
client.dashboards.createFromTemplate(t as IDashboardTemplate, orgID)
createDashboardFromTemplateAJAX(t as DashboardTemplate, orgID)
)
const dashboards = await Promise.all(pendingDashboards)

View File

@ -0,0 +1,184 @@
import _ from 'lodash'
import {
DashboardTemplate,
TemplateType,
CellIncluded,
LabelIncluded,
ViewIncluded,
} from 'src/types/v2'
import {IDashboard, Cell} from '@influxdata/influx'
import {client} from 'src/utils/api'
import {
findIncludedsFromRelationships,
findLabelIDsToAdd,
findLabelsToCreate,
findIncludedFromRelationship,
findVariablesToCreate,
findIncludedVariables,
} from 'src/templates/utils/'
// Create Dashboard Templates
export const createDashboardFromTemplate = async (
template: DashboardTemplate,
orgID: string
): Promise<IDashboard> => {
const {content} = template
if (
content.data.type !== TemplateType.Dashboard ||
template.meta.version !== '1'
) {
throw new Error('Can not create dashboard from this template')
}
const createdDashboard = await client.dashboards.create({
...content.data.attributes,
orgID,
})
if (!createdDashboard || !createdDashboard.id) {
throw new Error('Failed to create dashboard from template')
}
await Promise.all([
await createLabelsFromTemplate(template, createdDashboard),
await createCellsFromTemplate(template, createdDashboard),
])
createVariablesFromTemplate(template, orgID)
const dashboard = await client.dashboards.get(createdDashboard.id)
return dashboard
}
const createLabelsFromTemplate = async (
template: DashboardTemplate,
dashboard: IDashboard
) => {
const {
content: {data, included},
} = template
if (!data.relationships || !data.relationships[TemplateType.Label]) {
return
}
const labelRelationships = data.relationships[TemplateType.Label].data
const labelsIncluded = findIncludedsFromRelationships<LabelIncluded>(
included,
labelRelationships
)
const existingLabels = await client.labels.getAll()
const labelsToCreate = findLabelsToCreate(existingLabels, labelsIncluded).map(
l => ({
name: _.get(l, 'attributes.name', ''),
properties: _.get(l, 'attributes.properties', {}),
orgID: dashboard.orgID,
})
)
const createdLabels = await client.labels.createAll(labelsToCreate)
// IDs of newly created labels that should be added to dashboard
const createdLabelIDs = createdLabels.map(l => l.id || '')
// IDs of existing labels that should be added to dashboard
const existingLabelIDs = findLabelIDsToAdd(existingLabels, labelsIncluded)
await client.dashboards.addLabels(dashboard.id, [
...createdLabelIDs,
...existingLabelIDs,
])
}
const createCellsFromTemplate = async (
template: DashboardTemplate,
createdDashboard: IDashboard
) => {
const {
content: {data, included},
} = template
if (!data.relationships || !data.relationships[TemplateType.Cell]) {
return
}
const cellRelationships = data.relationships[TemplateType.Cell].data
const cellsToCreate = findIncludedsFromRelationships<CellIncluded>(
included,
cellRelationships
)
const pendingCells = cellsToCreate.map(c => {
const {
attributes: {x, y, w, h},
} = c
return client.dashboards.createCell(createdDashboard.id, {x, y, w, h})
})
const cellResponses = await Promise.all(pendingCells)
createViewsFromTemplate(
template,
cellResponses,
cellsToCreate,
createdDashboard.id
)
}
const createViewsFromTemplate = async (
template: DashboardTemplate,
cellResponses: Cell[],
cellsToCreate: CellIncluded[],
dashboardID: string
) => {
const viewsToCreate = cellsToCreate.map(c => {
const {
content: {included},
} = template
const viewRelationship = c.relationships[TemplateType.View].data
return findIncludedFromRelationship<ViewIncluded>(
included,
viewRelationship
)
})
const pendingViews = viewsToCreate.map((v, i) => {
return client.dashboards.updateView(
dashboardID,
cellResponses[i].id,
v.attributes
)
})
await Promise.all(pendingViews)
}
const createVariablesFromTemplate = async (
template: DashboardTemplate,
orgID: string
) => {
const {
content: {data, included},
} = template
if (!data.relationships || !data.relationships[TemplateType.Variable]) {
return
}
const variablesIncluded = findIncludedVariables(included)
const existingVariables = await client.variables.getAll()
const variablesToCreate = findVariablesToCreate(
existingVariables,
variablesIncluded
).map(v => ({...v.attributes, orgID}))
await client.variables.createAll(variablesToCreate)
}

View File

@ -0,0 +1,139 @@
import {
ILabel,
Variable,
IDashboard,
DocumentListEntry,
Document,
} from '@influxdata/influx'
import {View, Cell} from 'src/types/v2'
export enum TemplateType {
Label = 'label',
Task = 'task',
Dashboard = 'dashboard',
View = 'view',
Cell = 'cell',
Variable = 'variable',
}
interface KeyValuePairs {
[key: string]: any
}
// Templates
interface TemplateBase extends Document {
content: {data: TemplateData; included: TemplateIncluded[]}
labels?: ILabel[]
}
// TODO: be more specific about what attributes can be
interface TemplateData {
type: TemplateType
attributes: KeyValuePairs
relationships: {[key in TemplateType]?: {data: IRelationship[]}}
}
interface TemplateIncluded {
type: TemplateType
id: string
attributes: KeyValuePairs
}
// Template Relationships
type IRelationship =
| CellRelationship
| LabelRelationship
| ViewRelationship
| VariableRelationship
export interface CellRelationship {
type: TemplateType.Cell
id: string
}
export interface LabelRelationship {
type: TemplateType.Label
id: string
}
export interface VariableRelationship {
type: TemplateType.Variable
id: string
}
interface ViewRelationship {
type: TemplateType.View
id: string
}
// Template Includeds
export interface ViewIncluded extends TemplateIncluded {
type: TemplateType.View
attributes: View
}
export interface CellIncluded extends TemplateIncluded {
type: TemplateType.Cell
attributes: Cell
relationships: {
[TemplateType.View]: {data: ViewRelationship}
}
}
export interface LabelIncluded extends TemplateIncluded {
type: TemplateType.Label
attributes: ILabel
}
export interface VariableIncluded extends TemplateIncluded {
type: TemplateType.Variable
attributes: Variable
}
export type TaskTemplateIncluded = LabelIncluded
export type DashboardTemplateIncluded =
| CellIncluded
| ViewIncluded
| LabelIncluded
| VariableIncluded
// Template Datas
interface TaskTemplateData extends TemplateData {
type: TemplateType.Task
attributes: {name: string; flux: string}
relationships: {
[TemplateType.Label]: {data: LabelRelationship[]}
}
}
interface DashboardTemplateData extends TemplateData {
type: TemplateType.Dashboard
attributes: IDashboard
relationships: {
[TemplateType.Label]: {data: LabelRelationship[]}
[TemplateType.Cell]: {data: CellRelationship[]}
[TemplateType.Variable]: {data: VariableRelationship[]}
}
}
// Templates
export interface TaskTemplate extends TemplateBase {
content: {
data: TaskTemplateData
included: TaskTemplateIncluded[]
}
}
export interface DashboardTemplate extends TemplateBase {
content: {
data: DashboardTemplateData
included: DashboardTemplateIncluded[]
}
}
export type Template = TaskTemplate | DashboardTemplate
export interface TemplateSummary extends DocumentListEntry {
labels: ILabel[]
}

View File

@ -0,0 +1,48 @@
import {
findIncludedsFromRelationships,
findIncludedFromRelationship,
findIncludedVariables,
} from 'src/templates/utils/'
import {TemplateType} from 'src/types/v2'
const includeds = [
{type: TemplateType.Cell, id: '1', attributes: {id: 'a'}},
{type: TemplateType.View, id: '3'},
{type: TemplateType.Variable, id: '3'},
{type: TemplateType.Variable, id: '1'},
]
const relationships = [{type: TemplateType.Cell, id: '1'}]
describe('Templates utils', () => {
describe('findIncludedsFromRelationships', () => {
it('finds item in included that matches relationship', () => {
const actual = findIncludedsFromRelationships(includeds, relationships)
const expected = [
{type: TemplateType.Cell, id: '1', attributes: {id: 'a'}},
]
expect(actual).toEqual(expected)
})
})
describe('findIncludedFromRelationship', () => {
it('finds included that matches relationship', () => {
const actual = findIncludedFromRelationship(includeds, relationships[0])
const expected = {type: TemplateType.Cell, id: '1', attributes: {id: 'a'}}
expect(actual).toEqual(expected)
})
})
describe('findIncludedVariables', () => {
it('finds included that matches relationship', () => {
const actual = findIncludedVariables(includeds)
const expected = [
{type: TemplateType.Variable, id: '3'},
{type: TemplateType.Variable, id: '1'},
]
expect(actual).toEqual(expected)
})
})
})

View File

@ -0,0 +1,60 @@
import {TemplateType, LabelIncluded, VariableIncluded} from 'src/types/v2'
import {ILabel, Variable} from '@influxdata/influx'
export function findIncludedsFromRelationships<
T extends {id: string; type: TemplateType}
>(
includeds: {id: string; type: TemplateType}[],
relationships: {id: string; type: TemplateType}[]
): T[] {
let intersection = []
relationships.forEach(r => {
const included = findIncludedFromRelationship<T>(includeds, r)
if (included) {
intersection = [...intersection, included]
}
})
return intersection
}
export function findIncludedFromRelationship<
T extends {id: string; type: TemplateType}
>(
includeds: {id: string; type: TemplateType}[],
r: {id: string; type: TemplateType}
): T {
return includeds.find((i): i is T => i.id === r.id && i.type === r.type) as T
}
export const findLabelIDsToAdd = (
existingLabels: ILabel[],
incomingLabels: LabelIncluded[]
): string[] => {
return existingLabels
.filter(el => !!incomingLabels.find(l => el.name === l.attributes.name))
.map(l => l.id || '')
}
export const findLabelsToCreate = (
currentLabels: ILabel[],
labels: LabelIncluded[]
): LabelIncluded[] => {
return labels.filter(
l => !currentLabels.find(el => el.name === l.attributes.name)
)
}
export const findIncludedVariables = (included: {type: TemplateType}[]) => {
return included.filter(
(r): r is VariableIncluded => r.type === TemplateType.Variable
)
}
export const findVariablesToCreate = (
existingVariables: Variable[],
incomingVariables: VariableIncluded[]
): VariableIncluded[] => {
return incomingVariables.filter(
v => !existingVariables.find(ev => ev.name === v.attributes.name)
)
}

View File

@ -43,6 +43,22 @@ import {TelegrafsState} from 'src/telegrafs/reducers'
import {TemplatesState} from 'src/templates/reducers'
import {AuthorizationsState} from 'src/authorizations/reducers'
import {ScrapersState} from 'src/scrapers/reducers'
import {
TemplateType,
LabelRelationship,
VariableRelationship,
CellRelationship,
ViewIncluded,
CellIncluded,
LabelIncluded,
VariableIncluded,
TaskTemplateIncluded,
DashboardTemplateIncluded,
TaskTemplate,
DashboardTemplate,
TemplateSummary,
Template,
} from 'src/templates/types'
export interface AppState {
VERSION: string
@ -101,4 +117,18 @@ export {
TaskStatus,
MeState,
Label,
TemplateType,
LabelRelationship,
VariableRelationship,
CellRelationship,
ViewIncluded,
CellIncluded,
LabelIncluded,
VariableIncluded,
TaskTemplateIncluded,
DashboardTemplateIncluded,
TaskTemplate,
DashboardTemplate,
TemplateSummary,
Template,
}