Skip to content

Commit

Permalink
[UnifiedSearch] Allow editing ad-hoc data views without permissions (e…
Browse files Browse the repository at this point in the history
…lastic#142723)

* allow editing ad-hoc data views without permissions

* [CI] Auto-commit changed files from 'node scripts/build_plugin_list_docs'

* fxi tests

* fix test

* allow field editing from discover table

Co-authored-by: kibanamachine <[email protected]>
Co-authored-by: Stratoula Kalafateli <[email protected]>
  • Loading branch information
3 people authored and WafaaNasr committed Oct 11, 2022
1 parent f8ada69 commit d7d283c
Show file tree
Hide file tree
Showing 11 changed files with 118 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ const IndexPatternEditorFlyoutContentComponent = ({
services: { application, http, dataViews, uiSettings, overlays },
} = useKibana<DataViewEditorContext>();

const canSave = dataViews.getCanSaveSync();

const { form } = useForm<IndexPatternConfig, FormInternal>({
// Prefill with data if editData exists
defaultValue: {
Expand Down Expand Up @@ -447,6 +449,7 @@ const IndexPatternEditorFlyoutContentComponent = ({
isEdit={!!editData}
isPersisted={Boolean(editData && editData.isPersisted())}
allowAdHoc={allowAdHoc}
canSave={canSave}
/>
</FlyoutPanels.Item>
<FlyoutPanels.Item>
Expand Down
34 changes: 19 additions & 15 deletions src/plugins/data_view_editor/public/components/footer/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ interface FooterProps {
isEdit: boolean;
isPersisted: boolean;
allowAdHoc: boolean;
canSave: boolean;
}

const closeButtonLabel = i18n.translate('indexPatternEditor.editor.flyoutCloseButtonLabel', {
Expand Down Expand Up @@ -56,6 +57,7 @@ export const Footer = ({
isEdit,
allowAdHoc,
isPersisted,
canSave,
}: FooterProps) => {
const submitPersisted = () => {
onSubmit(false);
Expand Down Expand Up @@ -96,21 +98,23 @@ export const Footer = ({
</EuiFlexItem>
)}

<EuiFlexItem grow={false}>
<EuiButton
color="primary"
onClick={submitPersisted}
data-test-subj="saveIndexPatternButton"
fill
disabled={submitDisabled}
>
{isEdit
? isPersisted
? editButtonLabel
: editUnpersistedButtonLabel
: saveButtonLabel}
</EuiButton>
</EuiFlexItem>
{(canSave || (isEdit && !isPersisted)) && (
<EuiFlexItem grow={false}>
<EuiButton
color="primary"
onClick={submitPersisted}
data-test-subj="saveIndexPatternButton"
fill
disabled={submitDisabled}
>
{isEdit
? isPersisted
? editButtonLabel
: editUnpersistedButtonLabel
: saveButtonLabel}
</EuiButton>
</EuiFlexItem>
)}
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,8 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps)
const { dataViewFieldEditor, dataViewEditor } = services;
const { availableFields$ } = props;

const canEditDataView = Boolean(dataViewEditor?.userPermissions.editDataView());
const canEditDataView =
Boolean(dataViewEditor?.userPermissions.editDataView()) || !selectedDataView?.isPersisted();

useEffect(
() => {
Expand Down Expand Up @@ -241,25 +242,19 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps)
]
);

const createNewDataView = useMemo(
() =>
canEditDataView
? () => {
const ref = dataViewEditor.openEditor({
onSave: async (dataView) => {
onDataViewCreated(dataView);
},
});
if (setDataViewEditorRef) {
setDataViewEditorRef(ref);
}
if (closeFlyout) {
closeFlyout();
}
}
: undefined,
[canEditDataView, dataViewEditor, setDataViewEditorRef, closeFlyout, onDataViewCreated]
);
const createNewDataView = useCallback(() => {
const ref = dataViewEditor.openEditor({
onSave: async (dataView) => {
onDataViewCreated(dataView);
},
});
if (setDataViewEditorRef) {
setDataViewEditorRef(ref);
}
if (closeFlyout) {
closeFlyout();
}
}, [dataViewEditor, setDataViewEditorRef, closeFlyout, onDataViewCreated]);

if (!selectedDataView) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ export const DiscoverTopNav = ({
const services = useDiscoverServices();
const { dataViewEditor, navigation, dataViewFieldEditor, data, uiSettings, dataViews } = services;

const canEditDataView = Boolean(dataViewEditor?.userPermissions.editDataView());
const canEditDataView =
Boolean(dataViewEditor?.userPermissions.editDataView()) || !dataView.isPersisted();

const closeFieldEditor = useRef<() => void | undefined>();
const closeDataViewEditor = useRef<() => void | undefined>();
Expand Down Expand Up @@ -124,22 +125,16 @@ export const DiscoverTopNav = ({
[editField, canEditDataView]
);

const createNewDataView = useMemo(
() =>
canEditDataView
? () => {
closeDataViewEditor.current = dataViewEditor.openEditor({
onSave: async (dataViewToSave) => {
if (dataViewToSave.id) {
onChangeDataView(dataViewToSave.id);
}
},
allowAdHocDataView: true,
});
}
: undefined,
[canEditDataView, dataViewEditor, onChangeDataView]
);
const createNewDataView = useCallback(() => {
closeDataViewEditor.current = dataViewEditor.openEditor({
onSave: async (dataViewToSave) => {
if (dataViewToSave.id) {
onChangeDataView(dataViewToSave.id);
}
},
allowAdHocDataView: true,
});
}, [dataViewEditor, onChangeDataView]);

const onCreateDefaultAdHocDataView = useCallback(
async (pattern: string) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ export const buildEditFieldButton = ({
}

const { canEdit: canEditField } = getFieldCapabilities(dataView, field);
const canEditDataView = Boolean(services.dataViewEditor?.userPermissions?.editDataView());
const canEditDataView =
Boolean(services.dataViewEditor?.userPermissions?.editDataView()) || !dataView.isPersisted();

if (!canEditField || !canEditDataView) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { mountWithIntl as mount } from '@kbn/test-jest-helpers';
import { findTestSubject } from '@elastic/eui/lib/test';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
import { indexPatternEditorPluginMock as dataViewEditorPluginMock } from '@kbn/data-view-editor-plugin/public/mocks';
import { ChangeDataView } from './change_dataview';
import { DataViewPickerPropsExtended, TextBasedLanguages } from '.';

Expand Down Expand Up @@ -44,6 +45,8 @@ describe('DataView component', () => {
storageValue: boolean,
uiSettingValue: boolean = false
) {
const dataViewEditorMock = dataViewEditorPluginMock.createStartContract();
(dataViewEditorMock.userPermissions.editDataView as jest.Mock).mockReturnValue(true);
let dataMock = dataPluginMock.createStartContract();
dataMock = {
...dataMock,
Expand All @@ -56,6 +59,7 @@ describe('DataView component', () => {
const services = {
data: dataMock,
storage: getStorage(storageValue),
dataViewEditor: dataViewEditorMock,
uiSettings: {
get: jest.fn(() => uiSettingValue),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,31 +190,35 @@ export function ChangeDataView({
defaultMessage: 'Add a field to this data view',
})}
</EuiContextMenuItem>,
<EuiContextMenuItem
key="manage"
icon="indexSettings"
data-test-subj="indexPattern-manage-field"
onClick={async () => {
if (onEditDataView) {
const dataView = await dataViews.get(currentDataViewId!);
dataViewEditor.openEditor({
editData: dataView,
onSave: (updatedDataView) => {
onEditDataView(updatedDataView);
},
});
} else {
application.navigateToApp('management', {
path: `/kibana/indexPatterns/patterns/${currentDataViewId}`,
});
}
setPopoverIsOpen(false);
}}
>
{i18n.translate('unifiedSearch.query.queryBar.indexPattern.manageFieldButton', {
defaultMessage: 'Manage this data view',
})}
</EuiContextMenuItem>,
onEditDataView || dataViewEditor.userPermissions.editDataView() ? (
<EuiContextMenuItem
key="manage"
icon="indexSettings"
data-test-subj="indexPattern-manage-field"
onClick={async () => {
if (onEditDataView) {
const dataView = await dataViews.get(currentDataViewId!);
dataViewEditor.openEditor({
editData: dataView,
onSave: (updatedDataView) => {
onEditDataView(updatedDataView);
},
});
} else {
application.navigateToApp('management', {
path: `/kibana/indexPatterns/patterns/${currentDataViewId}`,
});
}
setPopoverIsOpen(false);
}}
>
{i18n.translate('unifiedSearch.query.queryBar.indexPattern.manageFieldButton', {
defaultMessage: 'Manage this data view',
})}
</EuiContextMenuItem>
) : (
<React.Fragment />
),
<EuiHorizontalRule margin="none" />
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import React from 'react';
import SearchBar from './search_bar';

import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { indexPatternEditorPluginMock as dataViewEditorPluginMock } from '@kbn/data-view-editor-plugin/public/mocks';
import { I18nProvider } from '@kbn/i18n-react';

import { coreMock } from '@kbn/core/public/mocks';
Expand Down Expand Up @@ -83,6 +84,9 @@ function wrapSearchBarInContext(testProps: any) {
intl: null as any,
};

const dataViewEditorMock = dataViewEditorPluginMock.createStartContract();
(dataViewEditorMock.userPermissions.editDataView as jest.Mock).mockReturnValue(true);

const services = {
uiSettings: startMock.uiSettings,
savedObjects: startMock.savedObjects,
Expand Down Expand Up @@ -111,6 +115,7 @@ function wrapSearchBarInContext(testProps: any) {
}),
},
},
dataViewEditor: dataViewEditorMock,
dataViews: {
getIdsWithTitle: jest.fn(() => []),
},
Expand Down
33 changes: 0 additions & 33 deletions x-pack/plugins/lens/public/app_plugin/app.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -405,39 +405,6 @@ describe('Lens App', () => {
});

describe('TopNavMenu#dataViewPickerProps', () => {
it('calls the nav component with the correct dataview picker props if no permissions are given', async () => {
const { instance, lensStore } = await mountWith({ preloadedState: {} });
const document = {
savedObjectId: defaultSavedObjectId,
state: {
query: 'fake query',
filters: [{ query: { match_phrase: { src: 'test' } } }],
},
references: [{ type: 'index-pattern', id: '1', name: 'index-pattern-0' }],
} as unknown as Document;

act(() => {
lensStore.dispatch(
setState({
query: 'fake query' as unknown as Query,
persistedDoc: document,
})
);
});
instance.update();
const props = instance
.find('[data-test-subj="lnsApp_topNav"]')
.prop('dataViewPickerComponentProps') as TopNavMenuData[];
expect(props).toEqual(
expect.objectContaining({
currentDataViewId: 'mockip',
onChangeDataView: expect.any(Function),
onDataViewCreated: undefined,
onAddField: undefined,
})
);
});

it('calls the nav component with the correct dataview picker props if permissions are given', async () => {
const { instance, lensStore, services } = await mountWith({ preloadedState: {} });
services.dataViewEditor.userPermissions.editDataView = () => true;
Expand Down
Loading

0 comments on commit d7d283c

Please sign in to comment.