Skip to content

Commit

Permalink
[Visualize] Prevent overwriting managed content (#175274)
Browse files Browse the repository at this point in the history
## Summary

Close #172380

I marked this a breaking change since it is preventing users from doing
something they have been able to do before. They can no longer save
changes to managed visualizations. Instead, they have to save changes to
a new visualization.


This is how the UI should look for the managed visualization.

<img width="970" alt="Screenshot 2024-01-24 at 12 57 53 PM"
src="https://github.com/elastic/kibana/assets/315764/7f16d4dd-b03f-4d63-9741-08522f82ac3b">


### Checklist

Delete any items that are not applicable to this PR.

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials — going to
happen in #175150
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed —
https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/4967
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))

---------

Co-authored-by: kibanamachine <[email protected]>
Co-authored-by: Stratoula Kalafateli <[email protected]>
  • Loading branch information
3 people authored Jan 31, 2024
1 parent 9ee7b79 commit 29d1a0a
Show file tree
Hide file tree
Showing 9 changed files with 63 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export interface VisualizationSavedObject {
statusCode: number;
metadata?: Record<string, unknown>;
};
managed?: boolean;
}

export type PartialVisualizationSavedObject = Omit<
Expand Down
1 change: 1 addition & 0 deletions src/plugins/visualizations/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export interface VisSavedObject extends ISavedVis {
searchSource?: ISearchSource;
version?: string;
tags?: string[];
managed: boolean;
}

export interface SaveVisOptions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -255,6 +256,7 @@ export async function getSavedVisualization(

Object.assign(savedObject, attributes);
savedObject.lastSavedTitle = savedObject.title;
savedObject.managed = Boolean(resp.managed);

savedObject.sharingSavedObjectProps = {
aliasTargetId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -63,7 +64,11 @@ const TopNav = ({
const { services } = useKibana<VisualizeServices>();
const { TopNavMenu } = services.navigation.ui;
const { setHeaderActionMenu, visualizeCapabilities } = services;
const { embeddableHandler, vis } = visInstance;
const {
embeddableHandler,
vis,
savedVis: { managed },
} = visInstance;
const [inspectorSession, setInspectorSession] = useState<OverlayRef>();
const [navigateToLens, setNavigateToLens] = useState(false);
const [displayEditInLensItem, setDisplayEditInLensItem] = useState(false);
Expand Down Expand Up @@ -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'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -120,7 +120,7 @@ export const getVisualizationInstance = async (
* Both come from url search query
*/
opts?: Record<string, unknown> | string
) => {
): Promise<VisInstance> => {
const { data, spaces, savedObjectsTagging } = visualizeServices;

const savedVis: VisSavedObject = await getSavedVisualization(
Expand Down
1 change: 1 addition & 0 deletions src/plugins/visualizations/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
4 changes: 4 additions & 0 deletions test/functional/fixtures/kbn_archiver/managed_content.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
27 changes: 26 additions & 1 deletion x-pack/test/functional/apps/managed_content/managed_content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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',
Expand Down

0 comments on commit 29d1a0a

Please sign in to comment.