diff --git a/ui/cypress/e2e/explorer.test.ts b/ui/cypress/e2e/explorer.test.ts index e8e2a5f9edd..d092a005043 100644 --- a/ui/cypress/e2e/explorer.test.ts +++ b/ui/cypress/e2e/explorer.test.ts @@ -456,11 +456,11 @@ describe('DataExplorer', () => { .click() .focused() .type( - `from(bucket: "defbuck") + `from(bucket: "defbuck") |> range(start: -10s) |> filter(fn: (r) => r._measurement == "no exist")`, - {force: true, delay: TYPE_DELAY} - ) + {force: true, delay: TYPE_DELAY} + ) cy.getByTestID('time-machine-submit-button').click() }) @@ -475,11 +475,11 @@ describe('DataExplorer', () => { .click() .focused() .type( - `from(bucket: "defbuck") + `from(bucket: "defbuck") |> range(start: -15m, stop: now()) |> filter(fn: (r) => r._measurement == `, - {force: true, delay: TYPE_DELAY} - ) + {force: true, delay: TYPE_DELAY} + ) }) cy.getByTestID('toolbar-tab').click() diff --git a/ui/cypress/e2e/tasks.test.ts b/ui/cypress/e2e/tasks.test.ts index 6e86d063a11..c7b3e8131a3 100644 --- a/ui/cypress/e2e/tasks.test.ts +++ b/ui/cypress/e2e/tasks.test.ts @@ -319,9 +319,9 @@ http.post( cy.get('@bucket').then(bucket => { cy.getByTestID('flux-editor').within(() => { cy.get('.react-monaco-editor-container') - .click() - .focused() - .type(flux(bucket), {force: true, delay: 2}) + .click() + .focused() + .type(flux(bucket), {force: true, delay: 2}) }) }) cy.getByTestID('task-save-btn').click() diff --git a/ui/src/dashboards/actions/index.ts b/ui/src/dashboards/actions/index.ts index ca2efd4a28d..68c336082c4 100644 --- a/ui/src/dashboards/actions/index.ts +++ b/ui/src/dashboards/actions/index.ts @@ -15,7 +15,16 @@ import { getView as getViewAJAX, updateView as updateViewAJAX, } from 'src/dashboards/apis' -import {getVariables as apiGetVariables} from 'src/client' +import { + getVariables as apiGetVariables, + getDashboard as apiGetDashboard, + postDashboard as apiPostDashboard, + postDashboardsCell as apiPostDashboardsCell, + postDashboardsLabel as apiPostDashboardsLabel, + deleteDashboardsLabel as apiDeleteDashboardsLabel, + patchDashboardsCellsView as apiPatchDashboardsCellsView, + getDashboardsCellsView as apiGetDashboardsCellsView, +} from 'src/client' import {createDashboardFromTemplate as createDashboardFromTemplateAJAX} from 'src/templates/api' // Actions @@ -50,20 +59,18 @@ import { getClonedDashboardCell, } from 'src/dashboards/utils/cellGetters' import {dashboardToTemplate} from 'src/shared/utils/resourceToTemplate' -import {client} from 'src/utils/api' import {exportVariables} from 'src/variables/utils/exportVariables' import {getSaveableView} from 'src/timeMachine/selectors' import {incrementCloneName} from 'src/utils/naming' import {isLimitError} from 'src/cloud/utils/limits' import {getOrg} from 'src/organizations/selectors' +import {addLabelDefaults} from 'src/labels/utils' // Constants import * as copy from 'src/shared/copy/notifications' import {DEFAULT_DASHBOARD_NAME} from 'src/dashboards/constants/index' // Types -import {RemoteDataState} from 'src/types' -import {CreateCell} from '@influxdata/influx' import { Dashboard, NewView, @@ -72,7 +79,36 @@ import { View, DashboardTemplate, Label, + RemoteDataState, + NewCell, } from 'src/types' +import { + Dashboard as IDashboard, + Cell as ICell, + DashboardWithViewProperties, +} from 'src/client' + +export const addDashboardIDToCells = ( + cells: ICell[], + dashboardID: string +): Cell[] => { + return cells.map(c => { + return {...c, dashboardID} + }) +} + +export const addDashboardDefaults = ( + dashboard: IDashboard | DashboardWithViewProperties +): Dashboard => { + return { + ...dashboard, + cells: addDashboardIDToCells(dashboard.cells, dashboard.id) || [], + id: dashboard.id || '', + labels: (dashboard.labels || []).map(addLabelDefaults), + name: dashboard.name || '', + orgID: dashboard.orgID || '', + } +} export enum ActionTypes { SetDashboards = 'SET_DASHBOARDS', @@ -81,8 +117,8 @@ export enum ActionTypes { DeleteDashboardFailed = 'DELETE_DASHBOARD_FAILED', EditDashboard = 'EDIT_DASHBOARD', RemoveCell = 'REMOVE_CELL', - AddDashboardLabels = 'ADD_DASHBOARD_LABELS', - RemoveDashboardLabels = 'REMOVE_DASHBOARD_LABELS', + AddDashboardLabel = 'ADD_DASHBOARD_LABEL', + RemoveDashboardLabel = 'REMOVE_DASHBOARD_LABEL', } export type Action = @@ -94,8 +130,8 @@ export type Action = | SetViewAction | DeleteTimeRangeAction | DeleteDashboardFailedAction - | AddDashboardLabelsAction - | RemoveDashboardLabelsAction + | AddDashboardLabelAction + | RemoveDashboardLabelAction interface RemoveCellAction { type: ActionTypes.RemoveCell @@ -141,19 +177,19 @@ interface SetDashboardAction { } } -interface AddDashboardLabelsAction { - type: ActionTypes.AddDashboardLabels +interface AddDashboardLabelAction { + type: ActionTypes.AddDashboardLabel payload: { dashboardID: string - labels: Label[] + label: Label } } -interface RemoveDashboardLabelsAction { - type: ActionTypes.RemoveDashboardLabels +interface RemoveDashboardLabelAction { + type: ActionTypes.RemoveDashboardLabel payload: { dashboardID: string - labels: Label[] + label: Label } } @@ -217,20 +253,20 @@ export const removeCell = ( payload: {dashboard, cell}, }) -export const addDashboardLabels = ( +export const addDashboardLabel = ( dashboardID: string, - labels: Label[] -): AddDashboardLabelsAction => ({ - type: ActionTypes.AddDashboardLabels, - payload: {dashboardID, labels}, + label: Label +): AddDashboardLabelAction => ({ + type: ActionTypes.AddDashboardLabel, + payload: {dashboardID, label}, }) -export const removeDashboardLabels = ( +export const removeDashboardLabel = ( dashboardID: string, - labels: Label[] -): RemoveDashboardLabelsAction => ({ - type: ActionTypes.RemoveDashboardLabels, - payload: {dashboardID, labels}, + label: Label +): RemoveDashboardLabelAction => ({ + type: ActionTypes.RemoveDashboardLabel, + payload: {dashboardID, label}, }) // Thunks @@ -262,6 +298,46 @@ export const createDashboard = () => async ( } } +export const cloneUtilFunc = async (dash: Dashboard, id: string) => { + const cells = dash.cells + const pendingViews = cells.map(cell => + apiGetDashboardsCellsView({ + dashboardID: dash.id, + cellID: cell.id, + }).then(res => { + return { + ...res, + cellID: cell.id, + } + }) + ) + const views = await Promise.all(pendingViews) + + if (views.length > 0 && views.some(v => v.status !== 200)) { + throw new Error('An error occurred cloning the dashboard') + } + + return views.map(async v => { + const view = v.data as View + const cell = cells.find(c => c.id === view.id) + + if (cell && id) { + const newCell = await apiPostDashboardsCell({ + dashboardID: id, + data: cell, + }) + if (newCell.status !== 201) { + throw new Error('An error occurred cloning the dashboard') + } + return apiPatchDashboardsCellsView({ + dashboardID: id, + cellID: newCell.data.id, + data: view, + }) + } + }) +} + export const cloneDashboard = (dashboard: Dashboard) => async ( dispatch, getState: GetState @@ -274,10 +350,49 @@ export const cloneDashboard = (dashboard: Dashboard) => async ( const clonedName = incrementCloneName(allDashboardNames, dashboard.name) - const data = await client.dashboards.clone(dashboard.id, clonedName, org.id) + const getResp = await apiGetDashboard({dashboardID: dashboard.id}) + + if (getResp.status !== 200) { + throw new Error(getResp.data.message) + } + + const dash = addDashboardDefaults(getResp.data) + + const postResp = await apiPostDashboard({ + data: { + orgID: org.id, + name: clonedName, + description: dash.description || '', + }, + }) + + if (postResp.status !== 201) { + throw new Error(postResp.data.message) + } + + const pendingLabels = dash.labels.map(l => + apiPostDashboardsLabel({ + dashboardID: postResp.data.id, + data: {labelID: l.id}, + }) + ) + + const mappedLabels = await Promise.all(pendingLabels) + + if (mappedLabels.length > 0 && mappedLabels.some(l => l.status !== 201)) { + throw new Error('An error occurred cloning the labels for this dashboard') + } + + const clonedViews = await cloneUtilFunc(dash, postResp.data.id) + + const newViews = await Promise.all(clonedViews) + + if (newViews.length > 0 && newViews.some(v => v.status !== 200)) { + throw new Error('An error occurred cloning the dashboard') + } dispatch(checkDashboardLimits()) - dispatch(push(`/orgs/${org.id}/dashboards/${data.id}`)) + dispatch(push(`/orgs/${org.id}/dashboards/${postResp.data.id}`)) } catch (error) { console.error(error) if (isLimitError(error)) { @@ -414,7 +529,7 @@ export const createCellWithView = ( dashboard = await getDashboardAJAX(dashboardID) } - const cell: CreateCell = getNewDashboardCell(dashboard, clonedCell) + const cell: NewCell = getNewDashboardCell(dashboard, clonedCell) // Create the cell const createdCell = await addCellAJAX(dashboardID, cell) @@ -519,31 +634,44 @@ export const copyDashboardCellAsync = (dashboard: Dashboard, cell: Cell) => ( } } -export const addDashboardLabelsAsync = ( +export const addDashboardLabelAsync = ( dashboardID: string, - labels: Label[] + label: Label ) => async (dispatch: Dispatch) => { try { - const newLabels = await client.dashboards.addLabels( + const resp = await apiPostDashboardsLabel({ dashboardID, - labels.map(l => l.id) - ) + data: {labelID: label.id}, + }) + + if (resp.status !== 201) { + throw new Error(resp.data.message) + } + + const lab = addLabelDefaults(resp.data.label) - dispatch(addDashboardLabels(dashboardID, newLabels)) + dispatch(addDashboardLabel(dashboardID, lab)) } catch (error) { console.error(error) dispatch(notify(copy.addDashboardLabelFailed())) } } -export const removeDashboardLabelsAsync = ( +export const removeDashboardLabelAsync = ( dashboardID: string, - labels: Label[] + label: Label ) => async (dispatch: Dispatch) => { try { - await client.dashboards.removeLabels(dashboardID, labels.map(l => l.id)) + const resp = await apiDeleteDashboardsLabel({ + dashboardID, + labelID: label.id, + }) + + if (resp.status !== 204) { + throw new Error(resp.data.message) + } - dispatch(removeDashboardLabels(dashboardID, labels)) + dispatch(removeDashboardLabel(dashboardID, label)) } catch (error) { console.error(error) dispatch(notify(copy.removedDashboardLabelFailed())) diff --git a/ui/src/dashboards/apis/index.ts b/ui/src/dashboards/apis/index.ts index 17c9ffeac8f..d8b67e90520 100644 --- a/ui/src/dashboards/apis/index.ts +++ b/ui/src/dashboards/apis/index.ts @@ -2,67 +2,62 @@ import _ from 'lodash' // APIs -import * as api from 'src/client' +import { + getDashboards as apiGetDashboards, + getDashboard as apiGetDashboard, + postDashboard as apiPostDashboard, + deleteDashboard as apiDeleteDashboard, + patchDashboard as apiPatchDashboard, + postDashboardsCell as apiPostDashboardsCell, + putDashboardsCells as apiPutDashboardsCells, + deleteDashboardsCell as apiDeleteDashboardsCell, + getDashboardsCellsView as apiGetDashboardsCellsView, + patchDashboardsCellsView as apiPatchDashboardsCellsView, +} from 'src/client' // Types import {Cell, NewCell, Dashboard, View, CreateDashboardRequest} from 'src/types' -export const addDashboardIDToCells = ( - cells: Cell[], - dashboardID: string -): Cell[] => { - return cells.map(c => { - return {...c, dashboardID} - }) -} +// Utils +import { + addDashboardDefaults, + addDashboardIDToCells, +} from 'src/dashboards/actions' export const getDashboards = async (orgID: string): Promise => { - const resp = await api.getDashboards({query: {orgID}}) + const resp = await apiGetDashboards({query: {orgID}}) if (resp.status !== 200) { throw new Error(resp.data.message) } - return resp.data.dashboards.map(d => ({ - ...d, - cells: addDashboardIDToCells(d.cells as Cell[], d.id), - })) + return resp.data.dashboards.map(d => addDashboardDefaults(d)) } export const getDashboard = async (id: string): Promise => { - const resp = await api.getDashboard({dashboardID: id}) + const resp = await apiGetDashboard({dashboardID: id}) if (resp.status !== 200) { throw new Error(resp.data.message) } - const dashboard = resp.data - - return { - ...dashboard, - cells: addDashboardIDToCells(dashboard.cells as Cell[], dashboard.id), - } + return addDashboardDefaults(resp.data) } export const createDashboard = async ( props: CreateDashboardRequest ): Promise => { - const resp = await api.postDashboard({data: props}) + const resp = await apiPostDashboard({data: props}) if (resp.status !== 201) { throw new Error(resp.data.message) } - const dashboard = resp.data - - return { - ...dashboard, - cells: addDashboardIDToCells(dashboard.cells as Cell[], dashboard.id), - } + return addDashboardDefaults(resp.data) } export const deleteDashboard = async (dashboard: Dashboard): Promise => { - const resp = await api.deleteDashboard({dashboardID: dashboard.id}) + const resp = await apiDeleteDashboard({dashboardID: dashboard.id}) if (resp.status !== 204) { throw new Error(resp.data.message) @@ -72,7 +67,7 @@ export const deleteDashboard = async (dashboard: Dashboard): Promise => { export const updateDashboard = async ( dashboard: Dashboard ): Promise => { - const resp = await api.patchDashboard({ + const resp = await apiPatchDashboard({ dashboardID: dashboard.id, data: dashboard, }) @@ -81,19 +76,14 @@ export const updateDashboard = async ( throw new Error(resp.data.message) } - const updated = resp.data - - return { - ...updated, - cells: addDashboardIDToCells(updated.cells as Cell[], updated.id), - } + return addDashboardDefaults(resp.data) } export const addCell = async ( dashboardID: string, cell: NewCell ): Promise => { - const resp = await api.postDashboardsCell({dashboardID, data: cell}) + const resp = await apiPostDashboardsCell({dashboardID, data: cell}) if (resp.status !== 201) { throw new Error(resp.data.message) @@ -110,7 +100,7 @@ export const updateCells = async ( id: string, cells: Cell[] ): Promise => { - const resp = await api.putDashboardsCells({dashboardID: id, data: cells}) + const resp = await apiPutDashboardsCells({dashboardID: id, data: cells}) if (resp.status !== 200) { throw new Error(resp.data.message) @@ -118,14 +108,14 @@ export const updateCells = async ( const result = resp.data.cells - return addDashboardIDToCells(result as Cell[], id) + return addDashboardIDToCells(result, id) } export const deleteCell = async ( dashboardID: string, cell: Cell ): Promise => { - const resp = await api.deleteDashboardsCell({dashboardID, cellID: cell.id}) + const resp = await apiDeleteDashboardsCell({dashboardID, cellID: cell.id}) if (resp.status !== 204) { throw new Error(resp.data.message) @@ -136,15 +126,13 @@ export const getView = async ( dashboardID: string, cellID: string ): Promise => { - const resp = await api.getDashboardsCellsView({dashboardID, cellID}) + const resp = await apiGetDashboardsCellsView({dashboardID, cellID}) if (resp.status !== 200) { throw new Error(resp.data.message) } - const view: View = {...resp.data, dashboardID, cellID} - - return view + return {...resp.data, dashboardID, cellID} } export const updateView = async ( @@ -152,7 +140,7 @@ export const updateView = async ( cellID: string, view: Partial ): Promise => { - const resp = await api.patchDashboardsCellsView({ + const resp = await apiPatchDashboardsCellsView({ dashboardID, cellID, data: view as View, diff --git a/ui/src/dashboards/components/dashboard_index/DashboardCard.tsx b/ui/src/dashboards/components/dashboard_index/DashboardCard.tsx index 8161a8ab403..ba981271728 100644 --- a/ui/src/dashboards/components/dashboard_index/DashboardCard.tsx +++ b/ui/src/dashboards/components/dashboard_index/DashboardCard.tsx @@ -10,8 +10,8 @@ import InlineLabels from 'src/shared/components/inlineLabels/InlineLabels' // Actions import { - addDashboardLabelsAsync, - removeDashboardLabelsAsync, + addDashboardLabelAsync, + removeDashboardLabelAsync, } from 'src/dashboards/actions' import {createLabel as createLabelAsync} from 'src/labels/actions' @@ -41,8 +41,8 @@ interface StateProps { } interface DispatchProps { - onAddDashboardLabels: typeof addDashboardLabelsAsync - onRemoveDashboardLabels: typeof removeDashboardLabelsAsync + onAddDashboardLabel: typeof addDashboardLabelAsync + onRemoveDashboardLabel: typeof removeDashboardLabelAsync onCreateLabel: typeof createLabelAsync onResetViews: typeof resetViews } @@ -78,7 +78,7 @@ class DashboardCard extends PureComponent { } labels={ { } private handleAddLabel = (label: Label) => { - const {dashboard, onAddDashboardLabels} = this.props + const {dashboard, onAddDashboardLabel} = this.props - onAddDashboardLabels(dashboard.id, [label]) + onAddDashboardLabel(dashboard.id, label) } private handleRemoveLabel = (label: Label) => { - const {dashboard, onRemoveDashboardLabels} = this.props + const {dashboard, onRemoveDashboardLabel} = this.props - onRemoveDashboardLabels(dashboard.id, [label]) + onRemoveDashboardLabel(dashboard.id, label) } private handleCreateLabel = async (label: Label) => { @@ -196,8 +196,8 @@ const mstp = ({labels}: AppState): StateProps => { const mdtp: DispatchProps = { onCreateLabel: createLabelAsync, - onAddDashboardLabels: addDashboardLabelsAsync, - onRemoveDashboardLabels: removeDashboardLabelsAsync, + onAddDashboardLabel: addDashboardLabelAsync, + onRemoveDashboardLabel: removeDashboardLabelAsync, onResetViews: resetViews, } diff --git a/ui/src/dashboards/reducers/dashboards.test.ts b/ui/src/dashboards/reducers/dashboards.test.ts index 375a73fa9e6..634c8971e84 100644 --- a/ui/src/dashboards/reducers/dashboards.test.ts +++ b/ui/src/dashboards/reducers/dashboards.test.ts @@ -8,8 +8,8 @@ import { removeDashboard, editDashboard, removeCell, - addDashboardLabels, - removeDashboardLabels, + addDashboardLabel, + removeDashboardLabel, } from 'src/dashboards/actions/' // Resources @@ -69,10 +69,10 @@ describe('dashboards reducer', () => { it('can add labels to a dashboard', () => { const dashboardWithoutLabels = {...dashboard, labels: []} - const expected = {status, list: [{...dashboard, labels}]} + const expected = {status, list: [{...dashboard, labels: [labels[0]]}]} const actual = reducer( {status, list: [dashboardWithoutLabels]}, - addDashboardLabels(dashboardWithoutLabels.id, labels) + addDashboardLabel(dashboardWithoutLabels.id, labels[0]) ) expect(actual).toEqual(expected) @@ -82,12 +82,12 @@ describe('dashboards reducer', () => { const leftOverLabel = {...labels[0], name: 'wowowowo', id: '3'} const dashboardWithLabels = { ...dashboard, - labels: [...labels, leftOverLabel], + labels: [labels[0], leftOverLabel], } const expected = {status, list: [{...dashboard, labels: [leftOverLabel]}]} const actual = reducer( {status, list: [dashboardWithLabels]}, - removeDashboardLabels(dashboardWithLabels.id, labels) + removeDashboardLabel(dashboardWithLabels.id, labels[0]) ) expect(actual).toEqual(expected) diff --git a/ui/src/dashboards/reducers/dashboards.ts b/ui/src/dashboards/reducers/dashboards.ts index 432684456cb..38159b1969b 100644 --- a/ui/src/dashboards/reducers/dashboards.ts +++ b/ui/src/dashboards/reducers/dashboards.ts @@ -74,12 +74,12 @@ export const dashboardsReducer = ( return } - case ActionTypes.AddDashboardLabels: { - const {dashboardID, labels} = action.payload + case ActionTypes.AddDashboardLabel: { + const {dashboardID, label} = action.payload draftState.list = draftState.list.map(d => { if (d.id === dashboardID) { - d.labels = [...d.labels, ...labels] + d.labels = [...d.labels, label] } return d @@ -88,15 +88,11 @@ export const dashboardsReducer = ( return } - case ActionTypes.RemoveDashboardLabels: { - const {dashboardID, labels} = action.payload + case ActionTypes.RemoveDashboardLabel: { + const {dashboardID, label} = action.payload draftState.list = draftState.list.map(d => { if (d.id === dashboardID) { - const updatedLabels = d.labels.filter(el => { - const labelToRemove = labels.find(l => l.id === el.id) - - return !labelToRemove - }) + const updatedLabels = d.labels.filter(el => !(label.id === el.id)) d.labels = updatedLabels } diff --git a/ui/src/dashboards/utils/dashboardSwitcherLinks.ts b/ui/src/dashboards/utils/dashboardSwitcherLinks.ts index 3fb9e02fe18..c4f6f3386da 100644 --- a/ui/src/dashboards/utils/dashboardSwitcherLinks.ts +++ b/ui/src/dashboards/utils/dashboardSwitcherLinks.ts @@ -1,5 +1,4 @@ -import {Dashboard} from '@influxdata/influx' -import {DashboardSwitcherLinks} from 'src/types/dashboards' +import {Dashboard, DashboardSwitcherLinks} from 'src/types' export const EMPTY_LINKS = { links: [], diff --git a/ui/src/onboarding/components/CompletionQuickStartButton.tsx b/ui/src/onboarding/components/CompletionQuickStartButton.tsx index 3f6ef2fff6f..90977e57609 100644 --- a/ui/src/onboarding/components/CompletionQuickStartButton.tsx +++ b/ui/src/onboarding/components/CompletionQuickStartButton.tsx @@ -8,7 +8,7 @@ import {Button, ComponentColor, ComponentSize} from '@influxdata/clockface' import {ErrorHandling} from 'src/shared/decorators/errors' // Types -import {Dashboard} from '@influxdata/influx' +import {Dashboard} from 'src/types' interface OwnProps { dashboards: Dashboard[] diff --git a/ui/src/onboarding/components/CompletionStep.tsx b/ui/src/onboarding/components/CompletionStep.tsx index a2d3e897f69..3fcac670f11 100644 --- a/ui/src/onboarding/components/CompletionStep.tsx +++ b/ui/src/onboarding/components/CompletionStep.tsx @@ -31,8 +31,8 @@ import { Columns, Grid, } from '@influxdata/clockface' -import {Organization} from 'src/types' -import {Dashboard, ScraperTargetRequest} from '@influxdata/influx' +import {Dashboard, Organization} from 'src/types' +import {ScraperTargetRequest} from '@influxdata/influx' import {OnboardingStepProps} from 'src/onboarding/containers/OnboardingWizard' import {QUICKSTART_SCRAPER_TARGET_URL} from 'src/dataLoaders/constants/pluginConfigs' diff --git a/ui/src/organizations/apis/index.ts b/ui/src/organizations/apis/index.ts index b2deb0677ae..b97419c58fe 100644 --- a/ui/src/organizations/apis/index.ts +++ b/ui/src/organizations/apis/index.ts @@ -1,8 +1,12 @@ -import {client} from 'src/utils/api' +// API +import {getDashboards as apiGetDashboards} from 'src/client' // Types import {Dashboard, Organization} from 'src/types' +// Utils +import {addDashboardDefaults} from 'src/dashboards/actions' + // CRUD APIs for Organizations and Organization resources // i.e. Organization Members, Buckets, Dashboards etc @@ -12,12 +16,18 @@ export const getDashboards = async ( try { let result if (org) { - result = await client.dashboards.getAll(org.id) + result = await apiGetDashboards({query: {orgID: org.id}}) } else { - result = await client.dashboards.getAll() + result = await apiGetDashboards({}) + } + + if (result.status !== 200) { + throw new Error(result.data.message) } - return result + const dashboards = result.data.map(d => addDashboardDefaults(d)) + + return dashboards } catch (error) { console.error('Could not get buckets for org', error) throw error diff --git a/ui/src/shared/utils/resourceToTemplate.ts b/ui/src/shared/utils/resourceToTemplate.ts index de18f442c08..d2acd7cdf5e 100644 --- a/ui/src/shared/utils/resourceToTemplate.ts +++ b/ui/src/shared/utils/resourceToTemplate.ts @@ -4,9 +4,16 @@ import {getDeep} from 'src/utils/wrappers' import {defaultBuilderConfig} from 'src/shared/utils/view' import {viewableLabels} from 'src/labels/selectors' -import {Task, Label, Dashboard, Cell, View, Variable} from 'src/types' +import { + Task, + Label, + Dashboard, + DashboardQuery, + Cell, + View, + Variable, +} from 'src/types' import {TemplateType, DocumentCreate, ITemplate} from '@influxdata/influx' -import {DashboardQuery} from 'src/types/dashboards' const CURRENT_TEMPLATE_VERSION = '1' diff --git a/ui/src/templates/api/index.ts b/ui/src/templates/api/index.ts index 2c3306c1573..c924b6b90be 100644 --- a/ui/src/templates/api/index.ts +++ b/ui/src/templates/api/index.ts @@ -20,6 +20,7 @@ import {addLabelDefaults} from 'src/labels/utils' // API import { + getDashboard as apiGetDashboard, getTask as apiGetTask, postTask as apiPostTask, postTasksLabel as apiPostTasksLabel, @@ -29,30 +30,36 @@ import { getVariables as apiGetVariables, postVariable as apiPostVariable, postVariablesLabel as apiPostVariablesLabel, + postDashboard as apiPostDashboard, + postDashboardsLabel as apiPostDashboardsLabel, + postDashboardsCell as apiPostDashboardsCell, + patchDashboardsCellsView as apiPatchDashboardsCellsView, } from 'src/client' -import {client} from 'src/utils/api' +import {addDashboardDefaults} from 'src/dashboards/actions' +// Create Dashboard Templates // Types import { + TaskEntities, DashboardTemplate, + Dashboard, TemplateType, + Cell, CellIncluded, LabelIncluded, ViewIncluded, TaskTemplate, TemplateBase, - TaskEntities, Task, VariableTemplate, Variable, } from 'src/types' -import {IDashboard, Cell} from '@influxdata/influx' // Create Dashboard Templates export const createDashboardFromTemplate = async ( template: DashboardTemplate, orgID: string -): Promise => { +): Promise => { const {content} = template if ( @@ -62,15 +69,19 @@ export const createDashboardFromTemplate = async ( throw new Error('Cannot create dashboard from this template') } - const createdDashboard = await client.dashboards.create({ - ...content.data.attributes, - orgID, + const resp = await apiPostDashboard({ + data: { + orgID, + ...content.data.attributes, + }, }) - if (!createdDashboard || !createdDashboard.id) { - throw new Error('Failed to create dashboard from template') + if (resp.status !== 201) { + throw new Error(resp.data.message) } + const createdDashboard = addDashboardDefaults(resp.data) + // associate imported label id with new label const labelMap = await createLabelsFromTemplate(template, orgID) @@ -81,19 +92,36 @@ export const createDashboardFromTemplate = async ( await createVariablesFromTemplate(template, labelMap, orgID) - const dashboard = await client.dashboards.get(createdDashboard.id) + const getResp = await apiGetDashboard({dashboardID: resp.data.id}) + + if (getResp.status !== 200) { + throw new Error(getResp.data.message) + } + + const dashboard = addDashboardDefaults(getResp.data) return dashboard } const addDashboardLabelsFromTemplate = async ( template: DashboardTemplate, labelMap: LabelMap, - dashboard: IDashboard + dashboard: Dashboard ) => { - const labelRelationships = getLabelRelationships(template.content.data) - const labelIDs = labelRelationships.map(l => labelMap[l.id] || '') - - await client.dashboards.addLabels(dashboard.id, labelIDs) + try { + const labelRelationships = getLabelRelationships(template.content.data) + const labelIDs = labelRelationships.map(l => labelMap[l.id] || '') + const pending = labelIDs.map(labelID => + apiPostDashboardsLabel({dashboardID: dashboard.id, data: {labelID}}) + ) + const resolved = await Promise.all(pending) + if (resolved.length > 0 && resolved.some(r => r.status !== 201)) { + throw new Error( + 'An error occurred adding dashboard labels from the template' + ) + } + } catch (e) { + console.error(e) + } } type LabelMap = {[importedID: string]: CreatedLabelID} @@ -166,7 +194,7 @@ const createLabelsFromTemplate = async ( const createCellsFromTemplate = async ( template: DashboardTemplate, - createdDashboard: IDashboard + createdDashboard: Dashboard ) => { const { content: {data, included}, @@ -187,14 +215,23 @@ const createCellsFromTemplate = async ( const { attributes: {x, y, w, h}, } = c - return client.dashboards.createCell(createdDashboard.id, {x, y, w, h}) + return apiPostDashboardsCell({ + dashboardID: createdDashboard.id, + data: {x, y, w, h}, + }) }) const cellResponses = await Promise.all(pendingCells) + if (cellResponses.length > 0 && cellResponses.some(r => r.status !== 201)) { + throw new Error('An error occurred creating cells from the templates') + } + + const responses = cellResponses.map(resp => resp.data as Cell) + createViewsFromTemplate( template, - cellResponses, + responses, cellsToCreate, createdDashboard.id ) @@ -220,11 +257,11 @@ const createViewsFromTemplate = async ( }) const pendingViews = viewsToCreate.map((v, i) => { - return client.dashboards.updateView( + return apiPatchDashboardsCellsView({ dashboardID, - cellResponses[i].id, - v.attributes - ) + cellID: cellResponses[i].id, + data: v.attributes, + }) }) await Promise.all(pendingViews) diff --git a/ui/src/types/dashboards.ts b/ui/src/types/dashboards.ts index 52707a37d54..82f8bf374ff 100644 --- a/ui/src/types/dashboards.ts +++ b/ui/src/types/dashboards.ts @@ -9,6 +9,7 @@ import { RenamableField, BuilderConfig, } from 'src/client' +import {Label} from 'src/types' export type FieldOption = RenamableField @@ -38,6 +39,7 @@ export type NewCell = Omit export interface Dashboard extends Omit { cells: Cell[] + labels: Label[] } export type Base = Axis['base'] diff --git a/ui/src/types/templates.ts b/ui/src/types/templates.ts index 26685844797..b2473128374 100644 --- a/ui/src/types/templates.ts +++ b/ui/src/types/templates.ts @@ -1,11 +1,10 @@ import { - IDashboard, + ILabel, DocumentListEntry, Document, DocumentMeta, } from '@influxdata/influx' -import {View, Cell, Label, Variable} from 'src/types' -import {ILabel} from '@influxdata/influx' +import {Dashboard, View, Cell, Label, Variable} from 'src/types' export enum TemplateType { Label = 'label', @@ -129,7 +128,7 @@ interface TaskTemplateData extends TemplateData { interface DashboardTemplateData extends TemplateData { type: TemplateType.Dashboard - attributes: IDashboard + attributes: Dashboard relationships: { [TemplateType.Label]: {data: LabelRelationship[]} [TemplateType.Cell]: {data: CellRelationship[]}