[data view management] Fix set default data view permissions check (#124897)

* fix set default data view permissions

* fix fields table

* fix scripted field add button

* fix jest tests

* lint fixes

* fix test

* updte snapshot
This commit is contained in:
Matthew Kime 2022-02-09 10:32:20 -06:00 committed by GitHub
parent 67cd496daa
commit ded0478ed7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 72 additions and 57 deletions

View file

@ -70,7 +70,11 @@ export const CreateEditField = withRouter(
if (spec) {
return (
<>
<IndexHeader indexPattern={indexPattern} defaultIndex={uiSettings.get('defaultIndex')} />
<IndexHeader
indexPattern={indexPattern}
defaultIndex={uiSettings.get('defaultIndex')}
canSave={dataViews.getCanSaveSync()}
/>
<EuiSpacer size={'l'} />
<FieldEditor
indexPattern={indexPattern}

View file

@ -65,7 +65,7 @@ const securitySolution = 'security-solution';
export const EditIndexPattern = withRouter(
({ indexPattern, history, location }: EditIndexPatternProps) => {
const { application, uiSettings, overlays, chrome, dataViews } =
const { uiSettings, overlays, chrome, dataViews } =
useKibana<IndexPatternManagmentContext>().services;
const [fields, setFields] = useState<DataViewField[]>(indexPattern.getNonScriptedFields());
const [conflictedFields, setConflictedFields] = useState<DataViewField[]>(
@ -143,15 +143,16 @@ export const EditIndexPattern = withRouter(
const showTagsSection = Boolean(indexPattern.timeFieldName || (tags && tags.length > 0));
const kibana = useKibana();
const docsUrl = kibana.services.docLinks!.links.elasticsearch.mapping;
const userEditPermission = !!application?.capabilities?.indexPatterns?.save;
const userEditPermission = dataViews.getCanSaveSync();
return (
<div data-test-subj="editIndexPattern" role="region" aria-label={headingAriaLabel}>
<IndexHeader
indexPattern={indexPattern}
setDefault={setDefaultPattern}
{...(userEditPermission ? { deleteIndexPatternClick: removePattern } : {})}
deleteIndexPatternClick={removePattern}
defaultIndex={defaultIndex}
canSave={userEditPermission}
>
{showTagsSection && (
<EuiFlexGroup wrap gutterSize="s">

View file

@ -16,6 +16,7 @@ interface IndexHeaderProps {
defaultIndex?: string;
setDefault?: () => void;
deleteIndexPatternClick?: () => void;
canSave: boolean;
}
const setDefaultAriaLabel = i18n.translate('indexPatternManagement.editDataView.setDefaultAria', {
@ -40,12 +41,13 @@ export const IndexHeader: React.FC<IndexHeaderProps> = ({
setDefault,
deleteIndexPatternClick,
children,
canSave,
}) => {
return (
<EuiPageHeader
pageTitle={<span data-test-subj="indexPatternTitle">{indexPattern.title}</span>}
rightSideItems={[
defaultIndex !== indexPattern.id && setDefault && (
defaultIndex !== indexPattern.id && setDefault && canSave && (
<EuiToolTip content={setDefaultTooltip}>
<EuiButtonIcon
color="text"
@ -56,7 +58,7 @@ export const IndexHeader: React.FC<IndexHeaderProps> = ({
/>
</EuiToolTip>
),
deleteIndexPatternClick && (
canSave && (
<EuiToolTip content={removeTooltip}>
<EuiButtonIcon
color="danger"

View file

@ -161,6 +161,8 @@ exports[`IndexedFieldsTable IndexedFieldsTable with rollup index pattern should
},
]
}
openModal={[Function]}
theme={Object {}}
/>
</div>
`;
@ -196,6 +198,8 @@ exports[`IndexedFieldsTable should filter based on the query bar 1`] = `
},
]
}
openModal={[Function]}
theme={Object {}}
/>
</div>
`;
@ -233,6 +237,8 @@ exports[`IndexedFieldsTable should filter based on the schema filter 1`] = `
},
]
}
openModal={[Function]}
theme={Object {}}
/>
</div>
`;
@ -267,6 +273,8 @@ exports[`IndexedFieldsTable should filter based on the type filter 1`] = `
},
]
}
openModal={[Function]}
theme={Object {}}
/>
</div>
`;
@ -366,6 +374,8 @@ exports[`IndexedFieldsTable should render normally 1`] = `
},
]
}
openModal={[Function]}
theme={Object {}}
/>
</div>
`;

View file

@ -97,6 +97,12 @@ const fields = [
},
].map(mockFieldToIndexPatternField);
const mockedServices = {
userEditPermission: false,
openModal: () => ({ onClose: new Promise<void>(() => {}), close: async () => {} }),
theme: {} as any,
};
describe('IndexedFieldsTable', () => {
test('should render normally', async () => {
const component: ShallowWrapper<any, Readonly<{}>, React.Component<{}, {}, any>> = shallow(
@ -110,8 +116,9 @@ describe('IndexedFieldsTable', () => {
indexedFieldTypeFilter={[]}
schemaFieldTypeFilter={[]}
fieldFilter=""
{...mockedServices}
/>
).dive();
);
await new Promise((resolve) => process.nextTick(resolve));
component.update();
@ -131,8 +138,9 @@ describe('IndexedFieldsTable', () => {
indexedFieldTypeFilter={[]}
schemaFieldTypeFilter={[]}
fieldFilter=""
{...mockedServices}
/>
).dive();
);
await new Promise((resolve) => process.nextTick(resolve));
component.setProps({ fieldFilter: 'Elast' });
@ -153,8 +161,9 @@ describe('IndexedFieldsTable', () => {
indexedFieldTypeFilter={[]}
schemaFieldTypeFilter={[]}
fieldFilter=""
{...mockedServices}
/>
).dive();
);
await new Promise((resolve) => process.nextTick(resolve));
component.setProps({ indexedFieldTypeFilter: ['date'] });
@ -175,8 +184,9 @@ describe('IndexedFieldsTable', () => {
indexedFieldTypeFilter={[]}
schemaFieldTypeFilter={[]}
fieldFilter=""
{...mockedServices}
/>
).dive();
);
await new Promise((resolve) => process.nextTick(resolve));
component.setProps({ schemaFieldTypeFilter: ['runtime'] });
@ -198,8 +208,9 @@ describe('IndexedFieldsTable', () => {
indexedFieldTypeFilter={[]}
schemaFieldTypeFilter={[]}
fieldFilter=""
{...mockedServices}
/>
).dive();
);
await new Promise((resolve) => process.nextTick(resolve));
component.update();

View file

@ -10,10 +10,8 @@ import React, { Component } from 'react';
import { createSelector } from 'reselect';
import { OverlayStart, ThemeServiceStart } from 'src/core/public';
import { DataViewField, DataView } from '../../../../../../plugins/data_views/public';
import { useKibana } from '../../../../../../plugins/kibana_react/public';
import { Table } from './components/table';
import { IndexedFieldItem } from './types';
import { IndexPatternManagmentContext } from '../../../types';
interface IndexedFieldsTableProps {
fields: DataViewField[];
@ -36,16 +34,10 @@ interface IndexedFieldsTableState {
fields: IndexedFieldItem[];
}
const withHooks = (Comp: typeof Component) => {
return (props: any) => {
const { application } = useKibana<IndexPatternManagmentContext>().services;
const userEditPermission = !!application?.capabilities?.indexPatterns?.save;
return <Comp userEditPermission={userEditPermission} {...props} />;
};
};
class IndexedFields extends Component<IndexedFieldsTableProps, IndexedFieldsTableState> {
export class IndexedFieldsTable extends Component<
IndexedFieldsTableProps,
IndexedFieldsTableState
> {
constructor(props: IndexedFieldsTableProps) {
super(props);
@ -158,5 +150,3 @@ class IndexedFields extends Component<IndexedFieldsTableProps, IndexedFieldsTabl
);
}
}
export const IndexedFieldsTable = withHooks(IndexedFields);

View file

@ -22,9 +22,9 @@ interface HeaderProps extends RouteComponentProps {
}
export const Header = withRouter(({ indexPatternId, history }: HeaderProps) => {
const { application, docLinks } = useKibana<IndexPatternManagmentContext>().services;
const { dataViews, docLinks } = useKibana<IndexPatternManagmentContext>().services;
const links = docLinks?.links;
const userEditPermission = !!application?.capabilities?.indexPatterns?.save;
const userEditPermission = dataViews.getCanSaveSync();
return (
<EuiFlexGroup alignItems="center">
<EuiFlexItem>

View file

@ -68,9 +68,10 @@ describe('ScriptedFieldsTable', () => {
helpers={helpers}
painlessDocLink={'painlessDoc'}
saveIndexPattern={async () => {}}
userEditPermission={false}
scriptedFieldLanguageFilter={[]}
/>
).dive();
);
// Allow the componentWillMount code to execute
// https://github.com/airbnb/enzyme/issues/450
@ -87,9 +88,10 @@ describe('ScriptedFieldsTable', () => {
helpers={helpers}
painlessDocLink={'painlessDoc'}
saveIndexPattern={async () => {}}
userEditPermission={false}
scriptedFieldLanguageFilter={[]}
/>
).dive();
);
// Allow the componentWillMount code to execute
// https://github.com/airbnb/enzyme/issues/450
@ -119,9 +121,10 @@ describe('ScriptedFieldsTable', () => {
painlessDocLink={'painlessDoc'}
helpers={helpers}
saveIndexPattern={async () => {}}
userEditPermission={false}
scriptedFieldLanguageFilter={[]}
/>
).dive();
);
// Allow the componentWillMount code to execute
// https://github.com/airbnb/enzyme/issues/450
@ -145,9 +148,10 @@ describe('ScriptedFieldsTable', () => {
painlessDocLink={'painlessDoc'}
helpers={helpers}
saveIndexPattern={async () => {}}
userEditPermission={false}
scriptedFieldLanguageFilter={[]}
/>
).dive();
);
// Allow the componentWillMount code to execute
// https://github.com/airbnb/enzyme/issues/450
@ -166,9 +170,10 @@ describe('ScriptedFieldsTable', () => {
helpers={helpers}
painlessDocLink={'painlessDoc'}
saveIndexPattern={async () => {}}
userEditPermission={false}
scriptedFieldLanguageFilter={[]}
/>
).dive();
);
await component.update(); // Fire `componentWillMount()`
// @ts-expect-error lang is not valid
@ -194,9 +199,10 @@ describe('ScriptedFieldsTable', () => {
helpers={helpers}
painlessDocLink={'painlessDoc'}
saveIndexPattern={async () => {}}
userEditPermission={false}
scriptedFieldLanguageFilter={[]}
/>
).dive();
);
await component.update(); // Fire `componentWillMount()`
// @ts-expect-error

View file

@ -15,10 +15,8 @@ import {
import { Table, Header, CallOuts, DeleteScritpedFieldConfirmationModal } from './components';
import { ScriptedFieldItem } from './types';
import { IndexPatternManagmentContext } from '../../../types';
import { DataView, DataViewsPublicPluginStart } from '../../../../../../plugins/data_views/public';
import { useKibana } from '../../../../../../plugins/kibana_react/public';
interface ScriptedFieldsTableProps {
indexPattern: DataView;
@ -41,16 +39,10 @@ interface ScriptedFieldsTableState {
fields: ScriptedFieldItem[];
}
const withHooks = (Comp: typeof Component) => {
return (props: any) => {
const { application } = useKibana<IndexPatternManagmentContext>().services;
const userEditPermission = !!application?.capabilities?.indexPatterns?.save;
return <Comp userEditPermission={userEditPermission} {...props} />;
};
};
class ScriptedFields extends Component<ScriptedFieldsTableProps, ScriptedFieldsTableState> {
export class ScriptedFieldsTable extends Component<
ScriptedFieldsTableProps,
ScriptedFieldsTableState
> {
constructor(props: ScriptedFieldsTableProps) {
super(props);
@ -168,5 +160,3 @@ class ScriptedFields extends Component<ScriptedFieldsTableProps, ScriptedFieldsT
);
}
}
export const ScriptedFieldsTable = withHooks(ScriptedFields);

View file

@ -132,7 +132,7 @@ export function Tabs({
location,
refreshFields,
}: TabsProps) {
const { application, uiSettings, docLinks, dataViewFieldEditor, overlays, theme } =
const { uiSettings, docLinks, dataViewFieldEditor, overlays, theme, dataViews } =
useKibana<IndexPatternManagmentContext>().services;
const [fieldFilter, setFieldFilter] = useState<string>('');
const [syncingStateFunc, setSyncingStateFunc] = useState<any>({
@ -241,7 +241,7 @@ export function Tabs({
[uiSettings]
);
const userEditPermission = !!application?.capabilities?.indexPatterns?.save;
const userEditPermission = dataViews.getCanSaveSync();
const getFilterSection = useCallback(
(type: string) => {
return (
@ -448,7 +448,8 @@ export function Tabs({
getFieldInfo,
}}
openModal={overlays.openModal}
theme={theme}
theme={theme!}
userEditPermission={dataViews.getCanSaveSync()}
/>
)}
</DeleteRuntimeFieldProvider>
@ -472,6 +473,7 @@ export function Tabs({
}}
onRemoveField={refreshFilters}
painlessDocLink={docLinks.links.scriptedFields.painless}
userEditPermission={dataViews.getCanSaveSync()}
/>
</Fragment>
);
@ -510,6 +512,7 @@ export function Tabs({
refreshFields,
overlays,
theme,
dataViews,
]
);

View file

@ -39,7 +39,7 @@ export async function mountManagementSection(
params: ManagementAppMountParams
) {
const [
{ chrome, application, uiSettings, notifications, overlays, http, docLinks, theme },
{ chrome, uiSettings, notifications, overlays, http, docLinks, theme },
{ data, dataViewFieldEditor, dataViewEditor, dataViews, fieldFormats },
indexPatternManagementStart,
] = await getStartServices();
@ -51,7 +51,6 @@ export async function mountManagementSection(
const deps: IndexPatternManagmentContext = {
chrome,
application,
uiSettings,
notifications,
overlays,

View file

@ -13,6 +13,7 @@ import { urlForwardingPluginMock } from '../../url_forwarding/public/mocks';
import { dataPluginMock } from '../../data/public/mocks';
import { indexPatternFieldEditorPluginMock } from '../../data_view_field_editor/public/mocks';
import { indexPatternEditorPluginMock } from '../../data_view_editor/public/mocks';
import { dataViewPluginMocks } from '../../data_views/public/mocks';
import {
IndexPatternManagementSetup,
IndexPatternManagementStart,
@ -54,15 +55,14 @@ const docLinks = {
const createIndexPatternManagmentContext = (): {
[key in keyof IndexPatternManagmentContext]: any;
} => {
const { chrome, application, uiSettings, notifications, overlays } = coreMock.createStart();
const { chrome, uiSettings, notifications, overlays } = coreMock.createStart();
const { http } = coreMock.createSetup();
const data = dataPluginMock.createStartContract();
const dataViewFieldEditor = indexPatternFieldEditorPluginMock.createStartContract();
const dataViews = data.indexPatterns;
const dataViews = dataViewPluginMocks.createStartContract();
return {
chrome,
application,
uiSettings,
notifications,
overlays,

View file

@ -8,7 +8,6 @@
import {
ChromeStart,
ApplicationStart,
IUiSettingsClient,
OverlayStart,
NotificationsStart,
@ -26,7 +25,6 @@ import { FieldFormatsStart } from '../../field_formats/public';
export interface IndexPatternManagmentContext {
chrome: ChromeStart;
application: ApplicationStart;
uiSettings: IUiSettingsClient;
notifications: NotificationsStart;
overlays: OverlayStart;

View file

@ -27,6 +27,7 @@ const createStartContract = (): Start => {
}),
get: jest.fn().mockReturnValue(Promise.resolve({})),
clearCache: jest.fn(),
getCanSaveSync: jest.fn(),
} as unknown as jest.Mocked<DataViewsContract>;
};