-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Dashboards] Add getSerializedState method to Dashboard API #204140
Changes from 5 commits
cca4f65
a9159a4
6b6818f
b58c33d
ed68550
457f24d
3bcba56
3dc3af4
535ffd2
16e29ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the "Elastic License | ||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
* Public License v 1"; you may not use this file except in compliance with, at | ||
* your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
* License v3.0 only", or the "Server Side Public License, v 1". | ||
*/ | ||
|
||
import { | ||
dataService, | ||
embeddableService, | ||
savedObjectsTaggingService, | ||
} from '../services/kibana_services'; | ||
import { getSampleDashboardState } from '../mocks'; | ||
import { DashboardState } from './types'; | ||
import { getDashboardState } from './get_dashboard_state'; | ||
|
||
dataService.search.searchSource.create = jest.fn().mockResolvedValue({ | ||
setField: jest.fn(), | ||
getSerializedFields: jest.fn().mockReturnValue({}), | ||
}); | ||
|
||
dataService.query.timefilter.timefilter.getTime = jest | ||
.fn() | ||
.mockReturnValue({ from: 'now-15m', to: 'now' }); | ||
|
||
dataService.query.timefilter.timefilter.getRefreshInterval = jest | ||
.fn() | ||
.mockReturnValue({ pause: true, value: 0 }); | ||
|
||
embeddableService.extract = jest | ||
.fn() | ||
.mockImplementation((attributes) => ({ state: attributes, references: [] })); | ||
|
||
if (savedObjectsTaggingService) { | ||
savedObjectsTaggingService.getTaggingApi = jest.fn().mockReturnValue({ | ||
ui: { | ||
updateTagsReferences: jest.fn((references, tags) => references), | ||
}, | ||
}); | ||
} | ||
|
||
describe('getDashboardState', () => { | ||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('should return the current state attributes and references', async () => { | ||
const dashboardState = getSampleDashboardState(); | ||
const result = await getDashboardState({ | ||
controlGroupReferences: [], | ||
generateNewIds: false, | ||
dashboardState, | ||
panelReferences: [], | ||
}); | ||
|
||
expect(result.attributes.panels).toEqual([]); | ||
expect(result.references).toEqual([]); | ||
}); | ||
|
||
it('should generate new IDs for panels and references when generateNewIds is true', async () => { | ||
const dashboardState = { | ||
...getSampleDashboardState(), | ||
panels: { oldPanelId: { type: 'visualization' } }, | ||
} as unknown as DashboardState; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you move the cast from the entire dashboard state to just panel
|
||
const result = await getDashboardState({ | ||
controlGroupReferences: [], | ||
generateNewIds: true, | ||
dashboardState, | ||
panelReferences: [ | ||
{ | ||
name: 'oldPanelId:indexpattern_foobar', | ||
type: 'index-pattern', | ||
id: 'bizzbuzz', | ||
}, | ||
], | ||
}); | ||
|
||
expect(result.attributes.panels).toEqual( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why don't you mock
|
||
expect.arrayContaining([ | ||
expect.objectContaining({ | ||
panelIndex: expect.not.stringMatching('oldPanelId'), | ||
type: 'visualization', | ||
}), | ||
]) | ||
); | ||
expect(result.references).toEqual( | ||
expect.arrayContaining([ | ||
{ | ||
name: expect.not.stringMatching(/^oldPanelId:/), | ||
id: 'bizzbuzz', | ||
type: 'index-pattern', | ||
}, | ||
]) | ||
); | ||
}); | ||
|
||
it('should include control group references', async () => { | ||
const dashboardState = getSampleDashboardState(); | ||
const controlGroupReferences = [ | ||
{ name: 'control1:indexpattern', type: 'index-pattern', id: 'foobar' }, | ||
]; | ||
const result = await getDashboardState({ | ||
controlGroupReferences, | ||
generateNewIds: false, | ||
dashboardState, | ||
panelReferences: [], | ||
}); | ||
|
||
expect(result.references).toEqual(controlGroupReferences); | ||
}); | ||
|
||
it('should include panel references', async () => { | ||
const dashboardState = getSampleDashboardState(); | ||
const panelReferences = [ | ||
{ name: 'panel1:boogiewoogie', type: 'index-pattern', id: 'fizzbuzz' }, | ||
]; | ||
const result = await getDashboardState({ | ||
controlGroupReferences: [], | ||
generateNewIds: false, | ||
dashboardState, | ||
panelReferences, | ||
}); | ||
|
||
expect(result.references).toEqual(panelReferences); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the "Elastic License | ||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
* Public License v 1"; you may not use this file except in compliance with, at | ||
* your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
* License v3.0 only", or the "Server Side Public License, v 1". | ||
*/ | ||
|
||
import { pick } from 'lodash'; | ||
import moment, { Moment } from 'moment'; | ||
import { RefreshInterval } from '@kbn/data-plugin/public'; | ||
|
||
import { convertPanelMapToPanelsArray, extractReferences, generateNewPanelIds } from '../../common'; | ||
import type { DashboardAttributes } from '../../server'; | ||
|
||
import { convertDashboardVersionToNumber } from '../services/dashboard_content_management_service/lib/dashboard_versioning'; | ||
import { GetDashboardStateProps } from '../services/dashboard_content_management_service/types'; | ||
import { | ||
dataService, | ||
embeddableService, | ||
savedObjectsTaggingService, | ||
} from '../services/kibana_services'; | ||
import { LATEST_DASHBOARD_CONTAINER_VERSION } from '../dashboard_container'; | ||
|
||
export const convertTimeToUTCString = (time?: string | Moment): undefined | string => { | ||
if (moment(time).isValid()) { | ||
return moment(time).utc().format('YYYY-MM-DDTHH:mm:ss.SSS[Z]'); | ||
} else { | ||
// If it's not a valid moment date, then it should be a string representing a relative time | ||
// like 'now' or 'now-15m'. | ||
return time as string; | ||
} | ||
}; | ||
|
||
export const getDashboardState = ({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this be renamed to |
||
controlGroupReferences, | ||
generateNewIds, | ||
dashboardState, | ||
panelReferences, | ||
searchSourceReferences, | ||
}: GetDashboardStateProps) => { | ||
const { | ||
query: { | ||
timefilter: { timefilter }, | ||
}, | ||
} = dataService; | ||
|
||
const { | ||
tags, | ||
query, | ||
title, | ||
filters, | ||
timeRestore, | ||
description, | ||
|
||
// Dashboard options | ||
useMargins, | ||
syncColors, | ||
syncCursor, | ||
syncTooltips, | ||
hidePanelTitles, | ||
controlGroupInput, | ||
} = dashboardState; | ||
|
||
let { panels } = dashboardState; | ||
let prefixedPanelReferences = panelReferences; | ||
if (generateNewIds) { | ||
const { panels: newPanels, references: newPanelReferences } = generateNewPanelIds( | ||
panels, | ||
panelReferences | ||
); | ||
panels = newPanels; | ||
prefixedPanelReferences = newPanelReferences; | ||
// | ||
// do not need to generate new ids for controls. | ||
// ControlGroup Component is keyed on dashboard id so changing dashboard id mounts new ControlGroup Component. | ||
// | ||
} | ||
|
||
const searchSource = { filter: filters, query }; | ||
const options = { | ||
useMargins, | ||
syncColors, | ||
syncCursor, | ||
syncTooltips, | ||
hidePanelTitles, | ||
}; | ||
const savedPanels = convertPanelMapToPanelsArray(panels, true); | ||
|
||
/** | ||
* Parse global time filter settings | ||
*/ | ||
const { from, to } = timefilter.getTime(); | ||
const timeFrom = timeRestore ? convertTimeToUTCString(from) : undefined; | ||
const timeTo = timeRestore ? convertTimeToUTCString(to) : undefined; | ||
const refreshInterval = timeRestore | ||
? (pick(timefilter.getRefreshInterval(), [ | ||
'display', | ||
'pause', | ||
'section', | ||
'value', | ||
]) as RefreshInterval) | ||
: undefined; | ||
|
||
const rawDashboardAttributes: DashboardAttributes = { | ||
version: convertDashboardVersionToNumber(LATEST_DASHBOARD_CONTAINER_VERSION), | ||
controlGroupInput: controlGroupInput as DashboardAttributes['controlGroupInput'], | ||
kibanaSavedObjectMeta: { searchSource }, | ||
description: description ?? '', | ||
refreshInterval, | ||
timeRestore, | ||
options, | ||
panels: savedPanels, | ||
timeFrom, | ||
title, | ||
timeTo, | ||
}; | ||
|
||
/** | ||
* Extract references from raw attributes and tags into the references array. | ||
*/ | ||
const { attributes, references: dashboardReferences } = extractReferences( | ||
{ | ||
attributes: rawDashboardAttributes, | ||
references: searchSourceReferences ?? [], | ||
}, | ||
{ embeddablePersistableStateService: embeddableService } | ||
); | ||
|
||
const savedObjectsTaggingApi = savedObjectsTaggingService?.getTaggingApi(); | ||
const references = savedObjectsTaggingApi?.ui.updateTagsReferences | ||
? savedObjectsTaggingApi?.ui.updateTagsReferences(dashboardReferences, tags) | ||
: dashboardReferences; | ||
|
||
const allReferences = [ | ||
...references, | ||
...(prefixedPanelReferences ?? []), | ||
...(controlGroupReferences ?? []), | ||
...(searchSourceReferences ?? []), | ||
]; | ||
return { attributes, references: allReferences }; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -102,7 +102,7 @@ export async function openSaveModal({ | |
controlGroupReferences, | ||
panelReferences, | ||
saveOptions, | ||
currentState: dashboardStateToSave, | ||
dashboardState: dashboardStateToSave, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
lastSavedId, | ||
}); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about asserting the entire
result.attributes