diff --git a/src/plugins/visualizations/common/content_management/v1/types.ts b/src/plugins/visualizations/common/content_management/v1/types.ts index a1e5bd6a1aba5..80159165d97d9 100644 --- a/src/plugins/visualizations/common/content_management/v1/types.ts +++ b/src/plugins/visualizations/common/content_management/v1/types.ts @@ -57,6 +57,7 @@ export interface VisualizationSavedObject { statusCode: number; metadata?: Record; }; + managed?: boolean; } export type PartialVisualizationSavedObject = Omit< diff --git a/src/plugins/visualizations/public/types.ts b/src/plugins/visualizations/public/types.ts index c23f40dc85bae..f00777c5ef957 100644 --- a/src/plugins/visualizations/public/types.ts +++ b/src/plugins/visualizations/public/types.ts @@ -55,6 +55,7 @@ export interface VisSavedObject extends ISavedVis { searchSource?: ISearchSource; version?: string; tags?: string[]; + managed: boolean; } export interface SaveVisOptions { diff --git a/src/plugins/visualizations/public/utils/saved_visualize_utils.ts b/src/plugins/visualizations/public/utils/saved_visualize_utils.ts index 85932c09729c3..7ef70fb8fc9b6 100644 --- a/src/plugins/visualizations/public/utils/saved_visualize_utils.ts +++ b/src/plugins/visualizations/public/utils/saved_visualize_utils.ts @@ -227,6 +227,7 @@ export async function getSavedVisualization( getEsType: () => SAVED_VIS_TYPE, getDisplayName: () => SAVED_VIS_TYPE, searchSource: opts.searchSource ? services.search.searchSource.createEmpty() : undefined, + managed: false, } as VisSavedObject; const defaultsProps = getDefaults(opts); @@ -255,6 +256,7 @@ export async function getSavedVisualization( Object.assign(savedObject, attributes); savedObject.lastSavedTitle = savedObject.title; + savedObject.managed = Boolean(resp.managed); savedObject.sharingSavedObjectProps = { aliasTargetId, diff --git a/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx b/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx index 4fc81d760d31b..17600cfd23146 100644 --- a/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx +++ b/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx @@ -14,6 +14,7 @@ import useLocalStorage from 'react-use/lib/useLocalStorage'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; import { switchMap } from 'rxjs'; +import { getManagedContentBadge } from '@kbn/managed-content-badge'; import type { VisualizeServices, VisualizeAppState, @@ -63,7 +64,11 @@ const TopNav = ({ const { services } = useKibana(); const { TopNavMenu } = services.navigation.ui; const { setHeaderActionMenu, visualizeCapabilities } = services; - const { embeddableHandler, vis } = visInstance; + const { + embeddableHandler, + vis, + savedVis: { managed }, + } = visInstance; const [inspectorSession, setInspectorSession] = useState(); const [navigateToLens, setNavigateToLens] = useState(false); const [displayEditInLensItem, setDisplayEditInLensItem] = useState(false); @@ -315,6 +320,18 @@ const TopNav = ({ showDatePicker={showDatePicker()} showFilterBar={showFilterBar} showQueryInput={showQueryInput} + badges={ + managed + ? [ + getManagedContentBadge( + i18n.translate('visualizations.managedBadgeTooltip', { + defaultMessage: + 'This visualization is managed by Elastic. Changes made here must be saved to a new visualization.', + }) + ), + ] + : undefined + } saveQueryMenuVisibility={ services.visualizeCapabilities.saveQuery ? 'allowed_by_app_privilege' : 'globally_managed' } diff --git a/src/plugins/visualizations/public/visualize_app/utils/get_top_nav_config.tsx b/src/plugins/visualizations/public/visualize_app/utils/get_top_nav_config.tsx index ce12d23ad0c28..955c3f6111762 100644 --- a/src/plugins/visualizations/public/visualize_app/utils/get_top_nav_config.tsx +++ b/src/plugins/visualizations/public/visualize_app/utils/get_top_nav_config.tsx @@ -605,6 +605,14 @@ export const getTopNavConfig = ( } )} onClose={() => {}} + mustCopyOnSaveMessage={ + savedVis.managed + ? i18n.translate('visualizations.topNavMenu.mustCopyOnSave', { + defaultMessage: + 'This visualization is managed by Elastic. Changes here must be saved to a new visualization.', + }) + : undefined + } /> ); } diff --git a/src/plugins/visualizations/public/visualize_app/utils/get_visualization_instance.ts b/src/plugins/visualizations/public/visualize_app/utils/get_visualization_instance.ts index 5ad857c13c4ea..443d83541640a 100644 --- a/src/plugins/visualizations/public/visualize_app/utils/get_visualization_instance.ts +++ b/src/plugins/visualizations/public/visualize_app/utils/get_visualization_instance.ts @@ -19,7 +19,7 @@ import { VisualizeEmbeddableContract, VisualizeInput, } from '../..'; -import type { VisualizeServices } from '../types'; +import type { VisInstance, VisualizeServices } from '../types'; function isErrorRelatedToRuntimeFields(error: ExpressionValueError['error']) { const originalError = error.original || error; @@ -120,7 +120,7 @@ export const getVisualizationInstance = async ( * Both come from url search query */ opts?: Record | string -) => { +): Promise => { const { data, spaces, savedObjectsTagging } = visualizeServices; const savedVis: VisSavedObject = await getSavedVisualization( diff --git a/src/plugins/visualizations/tsconfig.json b/src/plugins/visualizations/tsconfig.json index 5b1a718d551c8..adeaf7ae280c2 100644 --- a/src/plugins/visualizations/tsconfig.json +++ b/src/plugins/visualizations/tsconfig.json @@ -68,6 +68,7 @@ "@kbn/content-management-table-list-view-common", "@kbn/chart-expressions-common", "@kbn/shared-ux-utility", + "@kbn/managed-content-badge", "@kbn/presentation-publishing" ], "exclude": [ diff --git a/test/functional/fixtures/kbn_archiver/managed_content.json b/test/functional/fixtures/kbn_archiver/managed_content.json index 3bd33217c4e58..9e46c515d6b8b 100644 --- a/test/functional/fixtures/kbn_archiver/managed_content.json +++ b/test/functional/fixtures/kbn_archiver/managed_content.json @@ -8,6 +8,10 @@ {"attributes":{"columns":["@tags","clientip"],"description":"","grid":{},"hideChart":false,"isTextBasedQuery":false,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"agent.raw:\\\"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)\\\" \",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["@timestamp","desc"]],"timeRestore":false,"title":"Saved search","usesAdHocDataView":false},"coreMigrationVersion":"8.8.0","created_at":"2024-01-22T18:11:05.016Z","id":"unmanaged-3d62-4113-ac7c-de2e20a68fbc","managed":false,"references":[{"id":"5f863f70-4728-4e8d-b441-db08f8c33b28","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"search","typeMigrationVersion":"8.0.0","updated_at":"2024-01-22T18:11:05.016Z","version":"WzIzLDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"title":"Legacy visualization","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Legacy visualization\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"id\":\"1a14d0ad-0d74-4470-a189-8f040cddc1a1\",\"type\":\"timeseries\",\"series\":[{\"id\":\"daa8bbf7-86cc-4394-b249-be48da9f7351\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"palette\":{\"type\":\"palette\",\"name\":\"default\"},\"metrics\":[{\"id\":\"795375d9-1aa6-454d-9b23-687e69f3382c\",\"type\":\"count\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"default\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"override_index_pattern\":0,\"series_drop_last_bucket\":0}],\"time_field\":\"\",\"use_kibana_indexes\":true,\"interval\":\"\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"truncate_legend\":1,\"max_lines_legend\":1,\"show_grid\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":0,\"index_pattern_ref_name\":\"metrics_0_index_pattern\"}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-24T18:53:06.818Z","id":"managed-feb9-4ba6-9538-1b8f67fb4f57","managed":true,"references":[{"id":"5f863f70-4728-4e8d-b441-db08f8c33b28","name":"metrics_0_index_pattern","type":"index-pattern"}],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-24T18:53:06.818Z","version":"WzEzLDFd"} + +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"title":"Legacy visualization","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Legacy visualization\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"id\":\"1a14d0ad-0d74-4470-a189-8f040cddc1a1\",\"type\":\"timeseries\",\"series\":[{\"id\":\"daa8bbf7-86cc-4394-b249-be48da9f7351\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"palette\":{\"type\":\"palette\",\"name\":\"default\"},\"metrics\":[{\"id\":\"795375d9-1aa6-454d-9b23-687e69f3382c\",\"type\":\"count\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"default\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"override_index_pattern\":0,\"series_drop_last_bucket\":0}],\"time_field\":\"\",\"use_kibana_indexes\":true,\"interval\":\"\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"truncate_legend\":1,\"max_lines_legend\":1,\"show_grid\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":0,\"index_pattern_ref_name\":\"metrics_0_index_pattern\"}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-24T18:53:06.818Z","id":"unmanaged-feb9-4ba6-9538-1b8f67fb4f57","managed":false,"references":[{"id":"5f863f70-4728-4e8d-b441-db08f8c33b28","name":"metrics_0_index_pattern","type":"index-pattern"}],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-24T18:53:06.818Z","version":"WzEzLDFd"} + {"attributes":{"description":"","layerListJSON":"[{\"locale\":\"autoselect\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"isAutoSelect\":true,\"lightModeDefault\":\"road_map_desaturated\"},\"id\":\"5ff9c98e-e0d3-4aff-ac98-b33c191496b4\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"style\":{\"type\":\"EMS_VECTOR_TILE\",\"color\":\"\"},\"includeInFitToBounds\":true,\"type\":\"EMS_VECTOR_TILE\"}]","mapStateJSON":"{\"adHocDataViews\":[],\"zoom\":1.4,\"center\":{\"lon\":0,\"lat\":19.94277},\"timeFilters\":{\"from\":\"now-15m\",\"to\":\"now\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":60000},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[],\"settings\":{\"autoFitToDataBounds\":false,\"backgroundColor\":\"#ffffff\",\"customIcons\":[],\"disableInteractive\":false,\"disableTooltipControl\":false,\"hideToolbarOverlay\":false,\"hideLayerControl\":false,\"hideViewControl\":false,\"initialLocation\":\"LAST_SAVED_LOCATION\",\"fixedLocation\":{\"lat\":0,\"lon\":0,\"zoom\":2},\"browserLocation\":{\"zoom\":2},\"keydownScrollZoom\":false,\"maxZoom\":24,\"minZoom\":0,\"showScaleControl\":false,\"showSpatialFilters\":true,\"showTimesliderToggleButton\":true,\"spatialFiltersAlpa\":0.3,\"spatialFiltersFillColor\":\"#DA8B45\",\"spatialFiltersLineColor\":\"#DA8B45\"}}","title":"Map","uiStateJSON":"{\"isLayerTOCOpen\":true,\"openTOCDetails\":[]}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-24T18:22:40.360Z","id":"managed-d7ab-46eb-a807-8fed28ed8566","managed":true,"references":[],"type":"map","typeMigrationVersion":"8.4.0","updated_at":"2024-01-24T18:23:07.090Z","version":"WzEyLDFd"} {"attributes":{"description":"","layerListJSON":"[{\"locale\":\"autoselect\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"isAutoSelect\":true,\"lightModeDefault\":\"road_map_desaturated\"},\"id\":\"5ff9c98e-e0d3-4aff-ac98-b33c191496b4\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"style\":{\"type\":\"EMS_VECTOR_TILE\",\"color\":\"\"},\"includeInFitToBounds\":true,\"type\":\"EMS_VECTOR_TILE\"}]","mapStateJSON":"{\"adHocDataViews\":[],\"zoom\":1.4,\"center\":{\"lon\":0,\"lat\":19.94277},\"timeFilters\":{\"from\":\"now-15m\",\"to\":\"now\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":60000},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[],\"settings\":{\"autoFitToDataBounds\":false,\"backgroundColor\":\"#ffffff\",\"customIcons\":[],\"disableInteractive\":false,\"disableTooltipControl\":false,\"hideToolbarOverlay\":false,\"hideLayerControl\":false,\"hideViewControl\":false,\"initialLocation\":\"LAST_SAVED_LOCATION\",\"fixedLocation\":{\"lat\":0,\"lon\":0,\"zoom\":2},\"browserLocation\":{\"zoom\":2},\"keydownScrollZoom\":false,\"maxZoom\":24,\"minZoom\":0,\"showScaleControl\":false,\"showSpatialFilters\":true,\"showTimesliderToggleButton\":true,\"spatialFiltersAlpa\":0.3,\"spatialFiltersFillColor\":\"#DA8B45\",\"spatialFiltersLineColor\":\"#DA8B45\"}}","title":"Map","uiStateJSON":"{\"isLayerTOCOpen\":true,\"openTOCDetails\":[]}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-24T18:22:40.360Z","id":"unmanaged-d7ab-46eb-a807-8fed28ed8566","managed":false,"references":[],"type":"map","typeMigrationVersion":"8.4.0","updated_at":"2024-01-24T18:23:07.090Z","version":"WzEyLDFd"} diff --git a/x-pack/test/functional/apps/managed_content/managed_content.ts b/x-pack/test/functional/apps/managed_content/managed_content.ts index 05d30c248f0ea..a42d5b5a0c94c 100644 --- a/x-pack/test/functional/apps/managed_content/managed_content.ts +++ b/x-pack/test/functional/apps/managed_content/managed_content.ts @@ -9,7 +9,14 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { - const PageObjects = getPageObjects(['timePicker', 'lens', 'common', 'discover', 'maps']); + const PageObjects = getPageObjects([ + 'visChart', + 'timePicker', + 'lens', + 'common', + 'discover', + 'maps', + ]); const kibanaServer = getService('kibanaServer'); const esArchiver = getService('esArchiver'); const testSubjects = getService('testSubjects'); @@ -77,6 +84,24 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await expectManagedContentSignifiers(false, 'discoverSaveButton'); }); + it('visualize', async () => { + await PageObjects.common.navigateToActualUrl( + 'visualize', + 'edit/managed-feb9-4ba6-9538-1b8f67fb4f57' + ); + await PageObjects.visChart.waitForVisualization(); + + await expectManagedContentSignifiers(true, 'visualizeSaveButton'); + + await PageObjects.common.navigateToActualUrl( + 'visualize', + 'edit/unmanaged-feb9-4ba6-9538-1b8f67fb4f57' + ); + await PageObjects.visChart.waitForVisualization(); + + await expectManagedContentSignifiers(false, 'visualizeSaveButton'); + }); + it('maps', async () => { await PageObjects.common.navigateToActualUrl( 'maps',