From 3fcf8b32635fc9cbe76443d6b22624fc1f2f6ebb Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Thu, 31 Dec 2020 11:14:57 +0100 Subject: [PATCH 1/4] disabling buttons when user does not permission --- .../CustomLink/CreateCustomLinkButton.tsx | 11 +- .../CustomLink/CustomLinkTable.tsx | 39 ++++--- .../CustomizeUI/CustomLink/index.test.tsx | 100 ++++++++++++++++-- 3 files changed, 122 insertions(+), 28 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx index 56b3eaf425af7f..24630d6633735b 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx @@ -6,10 +6,19 @@ import React from 'react'; import { EuiButton } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { useApmPluginContext } from '../../../../../context/apm_plugin/use_apm_plugin_context'; export function CreateCustomLinkButton({ onClick }: { onClick: () => void }) { + const { core } = useApmPluginContext(); + const canSave = core.application.capabilities.apm.save; return ( - + {i18n.translate( 'xpack.apm.settings.customizeUI.customLink.createCustomLink', { defaultMessage: 'Create custom link' } diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkTable.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkTable.tsx index d512ea19c78925..4bc1adee04bf4f 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkTable.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkTable.tsx @@ -13,6 +13,7 @@ import { EuiSpacer, } from '@elastic/eui'; import { isEmpty } from 'lodash'; +import { useApmPluginContext } from '../../../../../context/apm_plugin/use_apm_plugin_context'; import { CustomLink } from '../../../../../../common/custom_link/custom_link_types'; import { units, px } from '../../../../../style/variables'; import { ManagedTable } from '../../../../shared/ManagedTable'; @@ -26,6 +27,8 @@ interface Props { export function CustomLinkTable({ items = [], onCustomLinkSelected }: Props) { const [searchTerm, setSearchTerm] = useState(''); + const { core } = useApmPluginContext(); + const canSave = core.application.capabilities.apm.save; const columns = [ { @@ -61,22 +64,26 @@ export function CustomLinkTable({ items = [], onCustomLinkSelected }: Props) { width: px(units.triple), name: '', actions: [ - { - name: i18n.translate( - 'xpack.apm.settings.customizeUI.customLink.table.editButtonLabel', - { defaultMessage: 'Edit' } - ), - description: i18n.translate( - 'xpack.apm.settings.customizeUI.customLink.table.editButtonDescription', - { defaultMessage: 'Edit this custom link' } - ), - icon: 'pencil', - color: 'primary', - type: 'icon', - onClick: (customLink: CustomLink) => { - onCustomLinkSelected(customLink); - }, - }, + ...(canSave + ? [ + { + name: i18n.translate( + 'xpack.apm.settings.customizeUI.customLink.table.editButtonLabel', + { defaultMessage: 'Edit' } + ), + description: i18n.translate( + 'xpack.apm.settings.customizeUI.customLink.table.editButtonDescription', + { defaultMessage: 'Edit this custom link' } + ), + icon: 'pencil', + color: 'primary', + type: 'icon', + onClick: (customLink: CustomLink) => { + onCustomLinkSelected(customLink); + }, + }, + ] + : []), ], }, ]; diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx index 1da7d415b56609..4477ee5a99be3f 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx @@ -7,22 +7,26 @@ import { fireEvent, render, - waitFor, RenderResult, + waitFor, } from '@testing-library/react'; import React from 'react'; import { act } from 'react-dom/test-utils'; -import * as apmApi from '../../../../../services/rest/createCallApmApi'; +import { CustomLinkOverview } from '.'; import { License } from '../../../../../../../licensing/common/license'; -import * as hooks from '../../../../../hooks/use_fetcher'; +import { ApmPluginContextValue } from '../../../../../context/apm_plugin/apm_plugin_context'; +import { + mockApmPluginContextValue, + MockApmPluginContextWrapper, +} from '../../../../../context/apm_plugin/mock_apm_plugin_context'; import { LicenseContext } from '../../../../../context/license/license_context'; -import { CustomLinkOverview } from '.'; +import * as hooks from '../../../../../hooks/use_fetcher'; +import * as apmApi from '../../../../../services/rest/createCallApmApi'; import { expectTextsInDocument, expectTextsNotInDocument, } from '../../../../../utils/testHelpers'; import * as saveCustomLink from './CreateEditCustomLinkFlyout/saveCustomLink'; -import { MockApmPluginContextWrapper } from '../../../../../context/apm_plugin/mock_apm_plugin_context'; const data = [ { @@ -39,6 +43,16 @@ const data = [ }, ]; +function getMockAPMContext({ canSave }: { canSave: boolean }) { + return ({ + ...mockApmPluginContextValue, + core: { + ...mockApmPluginContextValue.core, + application: { capabilities: { apm: { save: canSave }, ml: {} } }, + }, + } as unknown) as ApmPluginContextValue; +} + describe('CustomLink', () => { beforeAll(() => { jest.spyOn(apmApi, 'callApmApi').mockResolvedValue({}); @@ -70,9 +84,11 @@ describe('CustomLink', () => { }); it('shows when no link is available', () => { const component = render( - - - + + + + + ); expectTextsInDocument(component, ['No links found.']); }); @@ -91,6 +107,34 @@ describe('CustomLink', () => { jest.clearAllMocks(); }); + it('enables create button when user has writte privileges', () => { + const mockContext = getMockAPMContext({ canSave: true }); + + const { getByTestId } = render( + + + + + + ); + const createButton = getByTestId('createButton') as HTMLButtonElement; + expect(createButton.disabled).toBeFalsy(); + }); + + it('enables edit button on custom link table when user has writte privileges', () => { + const mockContext = getMockAPMContext({ canSave: true }); + + const { getAllByText } = render( + + + + + + ); + + expect(getAllByText('Edit').length).toEqual(2); + }); + it('shows a table with all custom link', () => { const component = render( @@ -108,9 +152,11 @@ describe('CustomLink', () => { }); it('checks if create custom link button is available and working', () => { + const mockContext = getMockAPMContext({ canSave: true }); + const { queryByText, getByText } = render( - + @@ -137,9 +183,10 @@ describe('CustomLink', () => { }); const openFlyout = () => { + const mockContext = getMockAPMContext({ canSave: true }); const component = render( - + @@ -173,9 +220,10 @@ describe('CustomLink', () => { }); it('deletes a custom link', async () => { + const mockContext = getMockAPMContext({ canSave: true }); const component = render( - + @@ -356,4 +404,34 @@ describe('CustomLink', () => { expectTextsNotInDocument(component, ['Start free 30-day trial']); }); }); + + describe('with read-only user', () => { + it('disables create custom link button', () => { + const mockContext = getMockAPMContext({ canSave: false }); + + const { getByTestId } = render( + + + + + + ); + const createButton = getByTestId('createButton') as HTMLButtonElement; + expect(createButton.disabled).toBeTruthy(); + }); + + it('removes edit button on custom link table', () => { + const mockContext = getMockAPMContext({ canSave: false }); + + const { queryAllByText } = render( + + + + + + ); + + expect(queryAllByText('Edit').length).toEqual(0); + }); + }); }); From bef7bd843a1b8b6812a520de6d3f44ac49a58c8b Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Thu, 31 Dec 2020 13:23:12 +0100 Subject: [PATCH 2/4] fixing test --- .../Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx index 24630d6633735b..8b41a81693a502 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx @@ -18,6 +18,7 @@ export function CreateCustomLinkButton({ onClick }: { onClick: () => void }) { iconType="plusInCircle" onClick={onClick} isDisabled={!canSave} + data-test-subj="createButton" > {i18n.translate( 'xpack.apm.settings.customizeUI.customLink.createCustomLink', From 01f190fff75426a88b3365bd366e336384f8b50e Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Mon, 4 Jan 2021 10:20:30 +0100 Subject: [PATCH 3/4] disabling create/edit button when user does not have write permission --- .../AgentConfigurations/List/index.tsx | 53 +++++++++++-------- .../Settings/AgentConfigurations/index.tsx | 9 +++- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/List/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/List/index.tsx index be4edbe2ea270f..9f3a65583ddb79 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/List/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/List/index.tsx @@ -41,6 +41,7 @@ interface Props { export function AgentConfigurationList({ status, data, refetch }: Props) { const { core } = useApmPluginContext(); + const canSave = core.application.capabilities.apm.save; const { basePath } = core.http; const { search } = useLocation(); const theme = useTheme(); @@ -180,28 +181,36 @@ export function AgentConfigurationList({ status, data, refetch }: Props) { ), }, - { - width: px(units.double), - name: '', - render: (config: Config) => ( - - ), - }, - { - width: px(units.double), - name: '', - render: (config: Config) => ( - setConfigToBeDeleted(config)} - /> - ), - }, + ...(canSave + ? [ + { + width: px(units.double), + name: '', + render: (config: Config) => ( + + ), + }, + { + width: px(units.double), + name: '', + render: (config: Config) => ( + setConfigToBeDeleted(config)} + /> + ), + }, + ] + : []), ]; return ( diff --git a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx index c408d5e960cf3b..02e0362cabb074 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx @@ -73,11 +73,18 @@ function CreateConfigurationButton() { const { basePath } = core.http; const { search } = useLocation(); const href = createAgentConfigurationHref(search, basePath); + const canSave = core.application.capabilities.apm.save; return ( - + {i18n.translate('xpack.apm.agentConfig.createConfigButtonLabel', { defaultMessage: 'Create configuration', })} From f82ebf07f8d91c5857e9b0a232bc764ba4ef217b Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Mon, 4 Jan 2021 11:31:08 +0100 Subject: [PATCH 4/4] addressing PR comments --- .../Settings/AgentConfigurations/index.tsx | 34 ++++++++++----- .../CustomLink/CreateCustomLinkButton.tsx | 41 ++++++++++++------- .../apm/server/routes/settings/custom_link.ts | 2 +- 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx index 02e0362cabb074..c1f5ec154792d0 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { EuiToolTip } from '@elastic/eui'; import { EuiButton, EuiFlexGroup, @@ -78,17 +79,30 @@ function CreateConfigurationButton() { - - {i18n.translate('xpack.apm.agentConfig.createConfigButtonLabel', { - defaultMessage: 'Create configuration', - })} - + + {i18n.translate('xpack.apm.agentConfig.createConfigButtonLabel', { + defaultMessage: 'Create configuration', + })} + + diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx index 8b41a81693a502..3b4c127aab1e5c 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx @@ -3,27 +3,40 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { EuiButton } from '@elastic/eui'; +import { EuiButton, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import React from 'react'; import { useApmPluginContext } from '../../../../../context/apm_plugin/use_apm_plugin_context'; export function CreateCustomLinkButton({ onClick }: { onClick: () => void }) { const { core } = useApmPluginContext(); const canSave = core.application.capabilities.apm.save; return ( - - {i18n.translate( - 'xpack.apm.settings.customizeUI.customLink.createCustomLink', - { defaultMessage: 'Create custom link' } - )} - + + {i18n.translate( + 'xpack.apm.settings.customizeUI.customLink.createCustomLink', + { defaultMessage: 'Create custom link' } + )} + + ); } diff --git a/x-pack/plugins/apm/server/routes/settings/custom_link.ts b/x-pack/plugins/apm/server/routes/settings/custom_link.ts index fdf2fe3521d7e1..70755540721dde 100644 --- a/x-pack/plugins/apm/server/routes/settings/custom_link.ts +++ b/x-pack/plugins/apm/server/routes/settings/custom_link.ts @@ -64,7 +64,7 @@ export const createCustomLinkRoute = createRoute({ params: t.type({ body: payloadRt, }), - options: { tags: ['access:apm'] }, + options: { tags: ['access:apm', 'access:apm_write'] }, handler: async ({ context, request }) => { if (!isActiveGoldLicense(context.licensing.license)) { throw Boom.forbidden(INVALID_LICENSE);