mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Synthetics] Remove fleet permission requirement for private location monitor cruds (#159378)
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Dominique Belcher <dominique.clarke@elastic.co> Co-authored-by: florent-leborgne <florent.leborgne@elastic.co>
This commit is contained in:
parent
b7dd5ba601
commit
cf9f5bb807
51 changed files with 506 additions and 916 deletions
|
@ -51,7 +51,7 @@ import type {
|
|||
import { defaultFleetErrorHandler, AgentPolicyNotFoundError } from '../../errors';
|
||||
import { createAgentPolicyWithPackages } from '../../services/agent_policy_create';
|
||||
|
||||
async function populateAssignedAgentsCount(
|
||||
export async function populateAssignedAgentsCount(
|
||||
esClient: ElasticsearchClient,
|
||||
soClient: SavedObjectsClientContract,
|
||||
agentPolicies: AgentPolicy[]
|
||||
|
@ -81,7 +81,9 @@ export const getAgentPoliciesHandler: FleetRequestHandler<
|
|||
try {
|
||||
const { items, total, page, perPage } = await agentPolicyService.list(soClient, {
|
||||
withPackagePolicies,
|
||||
esClient,
|
||||
...restOfQuery,
|
||||
withAgentCount: !noAgentCount,
|
||||
});
|
||||
|
||||
const body: GetAgentPoliciesResponse = {
|
||||
|
@ -90,11 +92,6 @@ export const getAgentPoliciesHandler: FleetRequestHandler<
|
|||
page,
|
||||
perPage,
|
||||
};
|
||||
if (!noAgentCount) {
|
||||
await populateAssignedAgentsCount(esClient, soClient, items);
|
||||
} else {
|
||||
items.forEach((item) => (item.agents = 0));
|
||||
}
|
||||
return response.ok({ body });
|
||||
} catch (error) {
|
||||
return defaultFleetErrorHandler({ error, response });
|
||||
|
|
|
@ -22,6 +22,8 @@ import type { BulkResponseItem } from '@elastic/elasticsearch/lib/api/typesWithB
|
|||
|
||||
import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants';
|
||||
|
||||
import { populateAssignedAgentsCount } from '../routes/agent_policy/handlers';
|
||||
|
||||
import type { HTTPAuthorizationHeader } from '../../common/http_authorization_header';
|
||||
|
||||
import {
|
||||
|
@ -370,6 +372,8 @@ class AgentPolicyService {
|
|||
options: ListWithKuery & {
|
||||
withPackagePolicies?: boolean;
|
||||
fields?: string[];
|
||||
esClient?: ElasticsearchClient;
|
||||
withAgentCount?: boolean;
|
||||
}
|
||||
): Promise<{
|
||||
items: AgentPolicy[];
|
||||
|
@ -385,6 +389,8 @@ class AgentPolicyService {
|
|||
kuery,
|
||||
withPackagePolicies = false,
|
||||
fields,
|
||||
esClient,
|
||||
withAgentCount = false,
|
||||
} = options;
|
||||
|
||||
const baseFindParams = {
|
||||
|
@ -424,14 +430,8 @@ class AgentPolicyService {
|
|||
...agentPolicySO.attributes,
|
||||
};
|
||||
if (withPackagePolicies) {
|
||||
const agentPolicyWithPackagePolicies = await this.get(
|
||||
soClient,
|
||||
agentPolicySO.id,
|
||||
withPackagePolicies
|
||||
);
|
||||
if (agentPolicyWithPackagePolicies) {
|
||||
agentPolicy.package_policies = agentPolicyWithPackagePolicies.package_policies;
|
||||
}
|
||||
agentPolicy.package_policies =
|
||||
(await packagePolicyService.findAllForAgentPolicy(soClient, agentPolicySO.id)) || [];
|
||||
}
|
||||
return agentPolicy;
|
||||
},
|
||||
|
@ -445,6 +445,11 @@ class AgentPolicyService {
|
|||
savedObjectType: AGENT_POLICY_SAVED_OBJECT_TYPE,
|
||||
});
|
||||
}
|
||||
if (esClient && withAgentCount) {
|
||||
await populateAssignedAgentsCount(esClient, soClient, agentPolicies);
|
||||
} else {
|
||||
agentPolicies.forEach((item) => (item.agents = 0));
|
||||
}
|
||||
|
||||
return {
|
||||
items: agentPolicies,
|
||||
|
|
|
@ -25,6 +25,7 @@ export enum SYNTHETICS_API_URLS {
|
|||
OVERVIEW_STATUS = `/internal/synthetics/overview_status`,
|
||||
INDEX_SIZE = `/internal/synthetics/index_size`,
|
||||
PARAMS = `/internal/synthetics/params`,
|
||||
AGENT_POLICIES = `/internal/synthetics/agent_policies`,
|
||||
PRIVATE_LOCATIONS = `/internal/synthetics/private_locations`,
|
||||
PRIVATE_LOCATIONS_MONITORS = `/internal/synthetics/private_locations/monitors`,
|
||||
SYNC_GLOBAL_PARAMS = `/internal/synthetics/sync_global_params`,
|
||||
|
|
|
@ -159,8 +159,9 @@ journey(`PrivateLocationsSettings`, async ({ page, params }) => {
|
|||
step('viewer user cannot add locations', async () => {
|
||||
await syntheticsApp.navigateToSettings(false);
|
||||
await page.click('text=Private Locations');
|
||||
await page.hover(byTestId('syntheticsEmptyLocationsButton'), { force: true });
|
||||
await page.waitForSelector(
|
||||
`text="You're missing some Kibana privileges to manage private locations"`
|
||||
`text="You do not have sufficient permissions to perform this action."`
|
||||
);
|
||||
const createLocationBtn = await page.getByRole('button', { name: 'Create location' });
|
||||
expect(await createLocationBtn.getAttribute('disabled')).toEqual('');
|
||||
|
|
|
@ -14,30 +14,28 @@ export const FleetPermissionsCallout = () => {
|
|||
return (
|
||||
<EuiCallOut title={NEED_PERMISSIONS_PRIVATE_LOCATIONS} color="warning" iconType="help">
|
||||
<p>{NEED_PRIVATE_LOCATIONS_PERMISSION}</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.synthetics.privateLocations.needFleetPermission.description"
|
||||
defaultMessage="Once there is an agent policy available, you'll be able to manage private locations and monitors with the regular Synthetics app privileges."
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* If any of the canEditSynthetics or canUpdatePrivateMonitor is false, then wrap the children with a tooltip
|
||||
* If canEditSynthetics is false, then wrap the children with a tooltip
|
||||
* so that a reason can be conveyed to the user explaining why the action is disabled.
|
||||
*/
|
||||
export const NoPermissionsTooltip = ({
|
||||
canEditSynthetics = true,
|
||||
canUpdatePrivateMonitor = true,
|
||||
canAddPrivateMonitor = true,
|
||||
children,
|
||||
}: {
|
||||
canEditSynthetics?: boolean;
|
||||
canUpdatePrivateMonitor?: boolean;
|
||||
canAddPrivateMonitor?: boolean;
|
||||
children: ReactNode;
|
||||
}) => {
|
||||
const disabledMessage = getRestrictionReasonLabel(
|
||||
canEditSynthetics,
|
||||
canUpdatePrivateMonitor,
|
||||
canAddPrivateMonitor
|
||||
);
|
||||
const disabledMessage = getRestrictionReasonLabel(canEditSynthetics);
|
||||
if (disabledMessage) {
|
||||
return (
|
||||
<EuiToolTip content={disabledMessage}>
|
||||
|
@ -49,18 +47,8 @@ export const NoPermissionsTooltip = ({
|
|||
return <>{children}</>;
|
||||
};
|
||||
|
||||
function getRestrictionReasonLabel(
|
||||
canEditSynthetics = true,
|
||||
canUpdatePrivateMonitor = true,
|
||||
canAddPrivateMonitor = true
|
||||
): string | undefined {
|
||||
return !canEditSynthetics
|
||||
? CANNOT_PERFORM_ACTION_SYNTHETICS
|
||||
: !canUpdatePrivateMonitor
|
||||
? CANNOT_PERFORM_ACTION_FLEET
|
||||
: !canAddPrivateMonitor
|
||||
? PRIVATE_LOCATIONS_NOT_ALLOWED_MESSAGE
|
||||
: undefined;
|
||||
function getRestrictionReasonLabel(canEditSynthetics = true): string | undefined {
|
||||
return !canEditSynthetics ? CANNOT_PERFORM_ACTION_SYNTHETICS : undefined;
|
||||
}
|
||||
|
||||
export const NEED_PERMISSIONS_PRIVATE_LOCATIONS = i18n.translate(
|
||||
|
@ -77,40 +65,16 @@ export const ALL = i18n.translate('xpack.synthetics.monitorManagement.priviledge
|
|||
export const NEED_PRIVATE_LOCATIONS_PERMISSION = (
|
||||
<FormattedMessage
|
||||
id="xpack.synthetics.monitorManagement.privateLocations.needFleetPermission"
|
||||
defaultMessage="You are not authorized to manage private locations. It requires the {all} Kibana privilege for both Fleet and Integrations."
|
||||
defaultMessage="In order to create private locations, you need an agent policy. You are not authorized to create Fleet agent policies. It requires the {all} Kibana privilege for Fleet."
|
||||
values={{
|
||||
all: <EuiCode>{`"${ALL}"`}</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
export const CANNOT_SAVE_INTEGRATION_LABEL = i18n.translate(
|
||||
'xpack.synthetics.monitorManagement.cannotSaveIntegration',
|
||||
{
|
||||
defaultMessage:
|
||||
'You are not authorized to manage private locations. It requires the "All" Kibana privilege for both Fleet and Integrations.',
|
||||
}
|
||||
);
|
||||
|
||||
const CANNOT_PERFORM_ACTION_FLEET = i18n.translate(
|
||||
'xpack.synthetics.monitorManagement.noFleetPermission',
|
||||
{
|
||||
defaultMessage:
|
||||
'You are not authorized to perform this action. It requires the "All" Kibana privilege for Integrations.',
|
||||
}
|
||||
);
|
||||
|
||||
export const CANNOT_PERFORM_ACTION_SYNTHETICS = i18n.translate(
|
||||
'xpack.synthetics.monitorManagement.noSyntheticsPermissions',
|
||||
{
|
||||
defaultMessage: 'You do not have sufficient permissions to perform this action.',
|
||||
}
|
||||
);
|
||||
|
||||
const PRIVATE_LOCATIONS_NOT_ALLOWED_MESSAGE = i18n.translate(
|
||||
'xpack.synthetics.monitorManagement.privateLocationsNotAllowedMessage',
|
||||
{
|
||||
defaultMessage:
|
||||
'You do not have permission to add monitors to private locations. Contact your administrator to request access.',
|
||||
}
|
||||
);
|
||||
|
|
|
@ -57,22 +57,22 @@ describe('JourneyScreenshotDialog', () => {
|
|||
});
|
||||
|
||||
it('shows loading indicator when image is loading', async () => {
|
||||
const { queryByTestId } = render(<JourneyScreenshotDialog {...testProps} />);
|
||||
const { getByTestId, queryByTestId } = render(<JourneyScreenshotDialog {...testProps} />);
|
||||
|
||||
expect(queryByTestId('screenshotImageLoadingProgress')).not.toBeInTheDocument();
|
||||
userEvent.click(queryByTestId('screenshotImageNextButton'));
|
||||
userEvent.click(getByTestId('screenshotImageNextButton'));
|
||||
});
|
||||
|
||||
it('respects maxSteps', () => {
|
||||
const { queryByTestId } = render(<JourneyScreenshotDialog {...testProps} />);
|
||||
const { getByTestId, queryByTestId } = render(<JourneyScreenshotDialog {...testProps} />);
|
||||
|
||||
expect(queryByTestId('screenshotImageLoadingProgress')).not.toBeInTheDocument();
|
||||
userEvent.click(queryByTestId('screenshotImageNextButton'));
|
||||
expect(queryByTestId('screenshotImageNextButton')).toHaveProperty('disabled');
|
||||
userEvent.click(getByTestId('screenshotImageNextButton'));
|
||||
expect(getByTestId('screenshotImageNextButton')).toHaveProperty('disabled');
|
||||
});
|
||||
|
||||
it('shows correct image source and step name', () => {
|
||||
const { queryByTestId, getByText } = render(<JourneyScreenshotDialog {...testProps} />);
|
||||
const { getByText, queryByTestId } = render(<JourneyScreenshotDialog {...testProps} />);
|
||||
expect(queryByTestId('stepScreenshotThumbnail')).toHaveProperty(
|
||||
'src',
|
||||
'http://localhost/test-img-url-1'
|
||||
|
|
|
@ -10,6 +10,9 @@ import * as permissionsHooks from '../../hooks/use_fleet_permissions';
|
|||
import { render } from '../../utils/testing/rtl_helpers';
|
||||
import { GettingStartedPage } from './getting_started_page';
|
||||
import * as privateLocationsHooks from '../settings/private_locations/hooks/use_locations_api';
|
||||
import * as settingsHooks from '../../contexts/synthetics_settings_context';
|
||||
import { SyntheticsSettingsContextValues } from '../../contexts/synthetics_settings_context';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
|
||||
describe('GettingStartedPage', () => {
|
||||
beforeEach(() => {
|
||||
|
@ -66,6 +69,10 @@ describe('GettingStartedPage', () => {
|
|||
});
|
||||
|
||||
it('shows need agent flyout when isAddingNewPrivateLocation is true and agentPolicies.length === 0', async () => {
|
||||
jest.spyOn(settingsHooks, 'useSyntheticsSettingsContext').mockReturnValue({
|
||||
canSave: true,
|
||||
} as SyntheticsSettingsContextValues);
|
||||
|
||||
const { getByText, getByRole, queryByLabelText } = render(<GettingStartedPage />, {
|
||||
state: {
|
||||
serviceLocations: {
|
||||
|
@ -87,7 +94,7 @@ describe('GettingStartedPage', () => {
|
|||
|
||||
expect(getByRole('heading', { name: 'Create private location', level: 2 }));
|
||||
expect(getByText('No agent policies found')).toBeInTheDocument();
|
||||
expect(getByRole('link', { name: 'Create agent policy' })).toBeEnabled();
|
||||
expect(getByRole('button', { name: 'Create agent policy' })).not.toBeEnabled();
|
||||
expect(queryByLabelText('Location name')).not.toBeInTheDocument();
|
||||
expect(queryByLabelText('Agent policy')).not.toBeInTheDocument();
|
||||
});
|
||||
|
@ -119,57 +126,41 @@ describe('GettingStartedPage', () => {
|
|||
expect(getByLabelText('Agent policy')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows permissions callout and hides form when agent policies are available but the user does not have permissions', async () => {
|
||||
jest.spyOn(permissionsHooks, 'useCanManagePrivateLocation').mockReturnValue(false);
|
||||
const { getByText, getByRole, queryByLabelText, queryByRole } = render(<GettingStartedPage />, {
|
||||
state: {
|
||||
serviceLocations: {
|
||||
locations: [],
|
||||
locationsLoaded: true,
|
||||
loading: false,
|
||||
},
|
||||
agentPolicies: {
|
||||
data: {
|
||||
total: 1,
|
||||
items: [{}],
|
||||
it('shows permissions tooltip when the user does not have permissions', async () => {
|
||||
jest.spyOn(settingsHooks, 'useSyntheticsSettingsContext').mockReturnValue({
|
||||
canSave: false,
|
||||
} as SyntheticsSettingsContextValues);
|
||||
const { getByText, getByRole, queryByLabelText, queryByRole, findByText } = render(
|
||||
<GettingStartedPage />,
|
||||
{
|
||||
state: {
|
||||
serviceLocations: {
|
||||
locations: [],
|
||||
locationsLoaded: true,
|
||||
loading: false,
|
||||
},
|
||||
agentPolicies: {
|
||||
data: {
|
||||
total: 1,
|
||||
items: [{}],
|
||||
},
|
||||
isAddingNewPrivateLocation: true,
|
||||
},
|
||||
isAddingNewPrivateLocation: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// page is loaded
|
||||
expect(getByText('Get started with synthetic monitoring')).toBeInTheDocument();
|
||||
|
||||
expect(getByRole('heading', { name: 'Create private location', level: 2 }));
|
||||
expect(queryByLabelText('Location name')).not.toBeInTheDocument();
|
||||
expect(queryByLabelText('Agent policy')).not.toBeInTheDocument();
|
||||
expect(queryByRole('button', { name: 'Save' })).not.toBeInTheDocument();
|
||||
expect(getByText("You're missing some Kibana privileges to manage private locations"));
|
||||
});
|
||||
|
||||
it('shows permissions callout when agent policy is needed but the user does not have permissions', async () => {
|
||||
jest.spyOn(permissionsHooks, 'useCanManagePrivateLocation').mockReturnValue(false);
|
||||
const { getByText, getByRole, queryByLabelText } = render(<GettingStartedPage />, {
|
||||
state: {
|
||||
serviceLocations: {
|
||||
locations: [],
|
||||
locationsLoaded: true,
|
||||
loading: false,
|
||||
},
|
||||
agentPolicies: {
|
||||
data: undefined, // data will be undefined when user does not have permissions
|
||||
isAddingNewPrivateLocation: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// page is loaded
|
||||
expect(getByText('Get started with synthetic monitoring')).toBeInTheDocument();
|
||||
|
||||
expect(getByRole('heading', { name: 'Create private location', level: 2 }));
|
||||
expect(queryByLabelText('Location name')).not.toBeInTheDocument();
|
||||
expect(queryByLabelText('Agent policy')).not.toBeInTheDocument();
|
||||
expect(getByText("You're missing some Kibana privileges to manage private locations"));
|
||||
expect(queryByLabelText('Location name')).toBeInTheDocument();
|
||||
expect(queryByLabelText('Agent policy')).toBeInTheDocument();
|
||||
expect(queryByRole('button', { name: 'Save' })).toBeInTheDocument();
|
||||
expect(queryByRole('button', { name: 'Save' })).toBeDisabled();
|
||||
fireEvent.mouseOver(getByRole('button', { name: 'Save' }));
|
||||
expect(
|
||||
await findByText(/You do not have sufficient permissions to perform this action./)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -20,7 +20,7 @@ import { useHistory } from 'react-router-dom';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import styled from 'styled-components';
|
||||
import { useBreadcrumbs, useLocations, useFleetPermissions } from '../../hooks';
|
||||
import { useBreadcrumbs, useLocations } from '../../hooks';
|
||||
import { usePrivateLocationsAPI } from '../settings/private_locations/hooks/use_locations_api';
|
||||
import { LoadingState } from '../monitors_page/overview/overview/monitor_detail_flyout';
|
||||
import {
|
||||
|
@ -40,17 +40,14 @@ export const GettingStartedPage = () => {
|
|||
const dispatch = useDispatch();
|
||||
const history = useHistory();
|
||||
|
||||
const { canReadAgentPolicies } = useFleetPermissions();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getServiceLocations());
|
||||
if (canReadAgentPolicies) {
|
||||
dispatch(getAgentPoliciesAction.get());
|
||||
}
|
||||
dispatch(getAgentPoliciesAction.get());
|
||||
|
||||
return () => {
|
||||
dispatch(cleanMonitorListState());
|
||||
};
|
||||
}, [canReadAgentPolicies, dispatch]);
|
||||
}, [dispatch]);
|
||||
|
||||
useBreadcrumbs([{ text: MONITORING_OVERVIEW_LABEL }]); // No extra breadcrumbs on overview
|
||||
|
||||
|
|
|
@ -18,10 +18,9 @@ import React, { useState } from 'react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { useSimpleMonitor } from './use_simple_monitor';
|
||||
import { ServiceLocationsField } from './form_fields/service_locations';
|
||||
import { ConfigKey, ServiceLocation, ServiceLocations } from '../../../../../common/runtime_types';
|
||||
import { ConfigKey, ServiceLocations } from '../../../../../common/runtime_types';
|
||||
import { useCanEditSynthetics } from '../../../../hooks/use_capabilities';
|
||||
import { useFormWrapped } from '../../../../hooks/use_form_wrapped';
|
||||
import { useFleetPermissions } from '../../hooks';
|
||||
import { NoPermissionsTooltip } from '../common/components/permissions';
|
||||
|
||||
export interface SimpleFormData {
|
||||
|
@ -35,7 +34,6 @@ export const SimpleMonitorForm = () => {
|
|||
register,
|
||||
handleSubmit,
|
||||
formState: { errors, isValid, isSubmitted },
|
||||
getValues,
|
||||
} = useFormWrapped({
|
||||
mode: 'onSubmit',
|
||||
reValidateMode: 'onChange',
|
||||
|
@ -52,11 +50,6 @@ export const SimpleMonitorForm = () => {
|
|||
const { loading, data: newMonitor } = useSimpleMonitor({ monitorData });
|
||||
|
||||
const canEditSynthetics = useCanEditSynthetics();
|
||||
const { canSaveIntegrations } = useFleetPermissions();
|
||||
const hasAnyPrivateLocationSelected = (getValues(ConfigKey.LOCATIONS) as ServiceLocations)?.some(
|
||||
({ isServiceManaged }: ServiceLocation) => !isServiceManaged
|
||||
);
|
||||
const canSavePrivateLocation = !hasAnyPrivateLocationSelected || canSaveIntegrations;
|
||||
|
||||
const hasURLError = !!errors?.[ConfigKey.URLS];
|
||||
|
||||
|
@ -88,17 +81,14 @@ export const SimpleMonitorForm = () => {
|
|||
<EuiSpacer size="m" />
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
<NoPermissionsTooltip
|
||||
canEditSynthetics={canEditSynthetics}
|
||||
canAddPrivateMonitor={canSavePrivateLocation}
|
||||
>
|
||||
<NoPermissionsTooltip canEditSynthetics={canEditSynthetics}>
|
||||
<EuiButton
|
||||
type="submit"
|
||||
fill
|
||||
iconType="plusInCircleFilled"
|
||||
isLoading={loading}
|
||||
data-test-subj="syntheticsMonitorConfigSubmitButton"
|
||||
disabled={!canEditSynthetics || !canSavePrivateLocation}
|
||||
disabled={!canEditSynthetics}
|
||||
>
|
||||
{CREATE_MONITOR_LABEL}
|
||||
</EuiButton>
|
||||
|
|
|
@ -13,11 +13,10 @@ import { useFormContext } from 'react-hook-form';
|
|||
import { FETCH_STATUS } from '@kbn/observability-shared-plugin/public';
|
||||
import { RunTestButton } from './run_test_btn';
|
||||
import { useCanEditSynthetics } from '../../../../../hooks/use_capabilities';
|
||||
import { useFleetPermissions } from '../../../hooks';
|
||||
import { useMonitorSave } from '../hooks/use_monitor_save';
|
||||
import { NoPermissionsTooltip } from '../../common/components/permissions';
|
||||
import { DeleteMonitor } from '../../monitors_page/management/monitor_list_table/delete_monitor';
|
||||
import { ConfigKey, ServiceLocation, SourceType, SyntheticsMonitor } from '../types';
|
||||
import { ConfigKey, SourceType, SyntheticsMonitor } from '../types';
|
||||
import { format } from './formatter';
|
||||
|
||||
import { MONITORS_ROUTE } from '../../../../../../common/constants';
|
||||
|
@ -41,11 +40,6 @@ export const ActionBar = ({ readOnly = false }: { readOnly: boolean }) => {
|
|||
const { status, loading, isEdit } = useMonitorSave({ monitorData });
|
||||
|
||||
const canEditSynthetics = useCanEditSynthetics();
|
||||
const { canSaveIntegrations } = useFleetPermissions();
|
||||
const hasAnyPrivateLocationSelected = getValues(ConfigKey.LOCATIONS)?.some(
|
||||
({ isServiceManaged }: ServiceLocation) => !isServiceManaged
|
||||
);
|
||||
const canSavePrivateLocation = !hasAnyPrivateLocationSelected || canSaveIntegrations;
|
||||
|
||||
const formSubmitter = (formData: Record<string, any>) => {
|
||||
// An additional invalid field check to account for customHook managed validation
|
||||
|
@ -91,17 +85,13 @@ export const ActionBar = ({ readOnly = false }: { readOnly: boolean }) => {
|
|||
<RunTestButton />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false} css={{ marginLeft: 'auto' }}>
|
||||
<NoPermissionsTooltip
|
||||
canEditSynthetics={canEditSynthetics}
|
||||
canAddPrivateMonitor={isEdit || canSavePrivateLocation}
|
||||
canUpdatePrivateMonitor={!isEdit || canSavePrivateLocation}
|
||||
>
|
||||
<NoPermissionsTooltip canEditSynthetics={canEditSynthetics}>
|
||||
<EuiButton
|
||||
fill
|
||||
isLoading={loading}
|
||||
onClick={handleSubmit(formSubmitter)}
|
||||
data-test-subj="syntheticsMonitorConfigSubmitButton"
|
||||
disabled={!canEditSynthetics || !canSavePrivateLocation}
|
||||
disabled={!canEditSynthetics}
|
||||
>
|
||||
{isEdit ? UPDATE_MONITOR_LABEL : CREATE_MONITOR_LABEL}
|
||||
</EuiButton>
|
||||
|
|
|
@ -10,31 +10,23 @@ import { useParams } from 'react-router-dom';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
|
||||
import { EncryptedSyntheticsMonitor } from '../../../../../../common/runtime_types';
|
||||
import { useCanEditSynthetics } from '../../../../../hooks/use_capabilities';
|
||||
import { useSyntheticsSettingsContext } from '../../../contexts';
|
||||
import { useCanUpdatePrivateMonitor } from '../../../hooks';
|
||||
import { NoPermissionsTooltip } from '../../common/components/permissions';
|
||||
import { useSelectedMonitor } from '../hooks/use_selected_monitor';
|
||||
|
||||
export const EditMonitorLink = () => {
|
||||
const { basePath } = useSyntheticsSettingsContext();
|
||||
|
||||
const { monitorId } = useParams<{ monitorId: string }>();
|
||||
const { monitor } = useSelectedMonitor();
|
||||
|
||||
const canEditSynthetics = useCanEditSynthetics();
|
||||
const canUpdatePrivateMonitor = useCanUpdatePrivateMonitor(monitor as EncryptedSyntheticsMonitor);
|
||||
const isLinkDisabled = !canEditSynthetics || !canUpdatePrivateMonitor;
|
||||
const isLinkDisabled = !canEditSynthetics;
|
||||
const linkProps = isLinkDisabled
|
||||
? { disabled: true }
|
||||
: { href: `${basePath}/app/synthetics/edit-monitor/${monitorId}` };
|
||||
|
||||
return (
|
||||
<NoPermissionsTooltip
|
||||
canEditSynthetics={canEditSynthetics}
|
||||
canUpdatePrivateMonitor={canUpdatePrivateMonitor}
|
||||
>
|
||||
<NoPermissionsTooltip canEditSynthetics={canEditSynthetics}>
|
||||
<EuiButton
|
||||
data-test-subj="syntheticsEditMonitorLinkButton"
|
||||
fill
|
||||
|
|
|
@ -9,7 +9,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
|
|||
import { EuiButton } from '@elastic/eui';
|
||||
import { useCanEditSynthetics } from '../../../../hooks/use_capabilities';
|
||||
import { NoPermissionsTooltip } from '../common/components/permissions';
|
||||
import { useEnablement, useFleetPermissions, useLocations } from '../../hooks';
|
||||
import { useEnablement } from '../../hooks';
|
||||
import { MONITOR_ADD_ROUTE } from '../../../../../common/constants';
|
||||
|
||||
import { SyntheticsSettingsContext } from '../../contexts/synthetics_settings_context';
|
||||
|
@ -22,25 +22,16 @@ export const CreateMonitorButton: React.FC = () => {
|
|||
} = useEnablement();
|
||||
|
||||
const canEditSynthetics = useCanEditSynthetics();
|
||||
const { canSaveIntegrations } = useFleetPermissions();
|
||||
const { locations } = useLocations();
|
||||
|
||||
const hasPublicLocation = locations.some((loc) => loc.isServiceManaged);
|
||||
|
||||
const canAddMonitor = canEditSynthetics && (hasPublicLocation || canSaveIntegrations);
|
||||
|
||||
return (
|
||||
<NoPermissionsTooltip
|
||||
canEditSynthetics={canEditSynthetics}
|
||||
canAddPrivateMonitor={canAddMonitor}
|
||||
>
|
||||
<NoPermissionsTooltip canEditSynthetics={canEditSynthetics}>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
fill
|
||||
iconSide="left"
|
||||
iconType="plusInCircleFilled"
|
||||
href={`${basePath}/app/synthetics${MONITOR_ADD_ROUTE}`}
|
||||
isDisabled={!isEnabled || !canAddMonitor}
|
||||
isDisabled={!isEnabled || !canEditSynthetics}
|
||||
data-test-subj="syntheticsAddMonitorBtn"
|
||||
>
|
||||
<FormattedMessage
|
||||
|
|
|
@ -32,7 +32,6 @@ import {
|
|||
SyntheticsMonitorSchedule,
|
||||
} from '../../../../../../../common/runtime_types';
|
||||
|
||||
import { canUpdatePrivateMonitor, useFleetPermissions } from '../../../../hooks';
|
||||
import { MonitorTypeBadge } from '../../../common/components/monitor_type_badge';
|
||||
import { getFrequencyLabel } from './labels';
|
||||
import { MonitorEnabled } from './monitor_enabled';
|
||||
|
@ -51,7 +50,6 @@ export function useMonitorListColumns({
|
|||
const canEditSynthetics = useCanEditSynthetics();
|
||||
|
||||
const { alertStatus, updateAlertEnabledState } = useMonitorAlertEnable();
|
||||
const { canSaveIntegrations } = useFleetPermissions();
|
||||
|
||||
const isActionLoading = (fields: EncryptedSyntheticsSavedMonitor) => {
|
||||
return alertStatus(fields[ConfigKey.CONFIG_ID]) === FETCH_STATUS.LOADING;
|
||||
|
@ -168,20 +166,14 @@ export function useMonitorListColumns({
|
|||
'data-test-subj': 'syntheticsMonitorEditAction',
|
||||
isPrimary: true,
|
||||
name: (fields) => (
|
||||
<NoPermissionsTooltip
|
||||
canEditSynthetics={canEditSynthetics}
|
||||
canUpdatePrivateMonitor={canUpdatePrivateMonitor(fields, canSaveIntegrations)}
|
||||
>
|
||||
<NoPermissionsTooltip canEditSynthetics={canEditSynthetics}>
|
||||
{labels.EDIT_LABEL}
|
||||
</NoPermissionsTooltip>
|
||||
),
|
||||
description: labels.EDIT_LABEL,
|
||||
icon: 'pencil' as const,
|
||||
type: 'icon' as const,
|
||||
enabled: (fields) =>
|
||||
canEditSynthetics &&
|
||||
!isActionLoading(fields) &&
|
||||
canUpdatePrivateMonitor(fields, canSaveIntegrations),
|
||||
enabled: (fields) => canEditSynthetics && !isActionLoading(fields),
|
||||
onClick: (fields) => {
|
||||
history.push({
|
||||
pathname: `/edit-monitor/${fields[ConfigKey.CONFIG_ID]}`,
|
||||
|
@ -192,10 +184,7 @@ export function useMonitorListColumns({
|
|||
'data-test-subj': 'syntheticsMonitorDeleteAction',
|
||||
isPrimary: true,
|
||||
name: (fields) => (
|
||||
<NoPermissionsTooltip
|
||||
canEditSynthetics={canEditSynthetics}
|
||||
canUpdatePrivateMonitor={canUpdatePrivateMonitor(fields, canSaveIntegrations)}
|
||||
>
|
||||
<NoPermissionsTooltip canEditSynthetics={canEditSynthetics}>
|
||||
{labels.DELETE_LABEL}
|
||||
</NoPermissionsTooltip>
|
||||
),
|
||||
|
@ -203,10 +192,7 @@ export function useMonitorListColumns({
|
|||
icon: 'trash' as const,
|
||||
type: 'icon' as const,
|
||||
color: 'danger' as const,
|
||||
enabled: (fields) =>
|
||||
canEditSynthetics &&
|
||||
!isActionLoading(fields) &&
|
||||
canUpdatePrivateMonitor(fields, canSaveIntegrations),
|
||||
enabled: (fields) => canEditSynthetics && !isActionLoading(fields),
|
||||
onClick: (fields) => {
|
||||
setMonitorPendingDeletion(fields);
|
||||
},
|
||||
|
@ -252,7 +238,7 @@ export function useMonitorListColumns({
|
|||
defaultMessage: 'Actions',
|
||||
}),
|
||||
render: () => (
|
||||
<NoPermissionsTooltip canEditSynthetics={canEditSynthetics} canUpdatePrivateMonitor={true}>
|
||||
<NoPermissionsTooltip canEditSynthetics={canEditSynthetics}>
|
||||
<EuiButtonIcon
|
||||
iconType="boxesHorizontal"
|
||||
isDisabled={true}
|
||||
|
|
|
@ -11,7 +11,7 @@ import { euiStyled } from '@kbn/kibana-react-plugin/common';
|
|||
import { FETCH_STATUS } from '@kbn/observability-shared-plugin/public';
|
||||
import { ConfigKey, EncryptedSyntheticsMonitor } from '../../../../../../../common/runtime_types';
|
||||
import { useCanEditSynthetics } from '../../../../../../hooks/use_capabilities';
|
||||
import { useCanUpdatePrivateMonitor, useMonitorEnableHandler } from '../../../../hooks';
|
||||
import { useMonitorEnableHandler } from '../../../../hooks';
|
||||
import { NoPermissionsTooltip } from '../../../common/components/permissions';
|
||||
import * as labels from './labels';
|
||||
|
||||
|
@ -30,7 +30,6 @@ export const MonitorEnabled = ({
|
|||
initialLoading = false,
|
||||
isSwitchable = true,
|
||||
}: Props) => {
|
||||
const canUpdatePrivateMonitor = useCanUpdatePrivateMonitor(monitor);
|
||||
const canEditSynthetics = useCanEditSynthetics();
|
||||
|
||||
const monitorName = monitor[ConfigKey.NAME];
|
||||
|
@ -64,14 +63,11 @@ export const MonitorEnabled = ({
|
|||
{isLoading || initialLoading ? (
|
||||
<EuiLoadingSpinner size="m" />
|
||||
) : (
|
||||
<NoPermissionsTooltip
|
||||
canEditSynthetics={canEditSynthetics}
|
||||
canUpdatePrivateMonitor={canUpdatePrivateMonitor}
|
||||
>
|
||||
<NoPermissionsTooltip canEditSynthetics={canEditSynthetics}>
|
||||
<SwitchWithCursor
|
||||
compressed={true}
|
||||
checked={enabled}
|
||||
disabled={isLoading || !canEditSynthetics || !canUpdatePrivateMonitor}
|
||||
disabled={isLoading || !canEditSynthetics}
|
||||
showLabel={false}
|
||||
label={enabledDisableLabel}
|
||||
title={enabledDisableLabel}
|
||||
|
|
|
@ -27,17 +27,9 @@ import {
|
|||
import { toggleStatusAlert } from '../../../../../../../common/runtime_types/monitor_management/alert_config';
|
||||
import { useSelectedMonitor } from '../../../monitor_details/hooks/use_selected_monitor';
|
||||
import { useMonitorAlertEnable } from '../../../../hooks/use_monitor_alert_enable';
|
||||
import {
|
||||
ConfigKey,
|
||||
EncryptedSyntheticsMonitor,
|
||||
MonitorOverviewItem,
|
||||
} from '../../../../../../../common/runtime_types';
|
||||
import { ConfigKey, MonitorOverviewItem } from '../../../../../../../common/runtime_types';
|
||||
import { useCanEditSynthetics } from '../../../../../../hooks/use_capabilities';
|
||||
import {
|
||||
useMonitorEnableHandler,
|
||||
useLocationName,
|
||||
useCanUpdatePrivateMonitor,
|
||||
} from '../../../../hooks';
|
||||
import { useMonitorEnableHandler, useLocationName } from '../../../../hooks';
|
||||
import { setFlyoutConfig } from '../../../../state/overview/actions';
|
||||
import { useEditMonitorLocator } from '../../../../hooks/use_edit_monitor_locator';
|
||||
import { useMonitorDetailLocator } from '../../../../hooks/use_monitor_detail_locator';
|
||||
|
@ -125,9 +117,6 @@ export function ActionsPopover({
|
|||
|
||||
const { monitor: monitorFields } = useSelectedMonitor(monitor.configId);
|
||||
const canEditSynthetics = useCanEditSynthetics();
|
||||
const canUpdatePrivateMonitor = useCanUpdatePrivateMonitor(
|
||||
monitorFields as EncryptedSyntheticsMonitor
|
||||
);
|
||||
|
||||
const labels = useMemo(
|
||||
() => ({
|
||||
|
@ -206,28 +195,22 @@ export function ActionsPopover({
|
|||
},
|
||||
{
|
||||
name: (
|
||||
<NoPermissionsTooltip
|
||||
canEditSynthetics={canEditSynthetics}
|
||||
canUpdatePrivateMonitor={canUpdatePrivateMonitor}
|
||||
>
|
||||
<NoPermissionsTooltip canEditSynthetics={canEditSynthetics}>
|
||||
{actionsMenuEditMonitorName}
|
||||
</NoPermissionsTooltip>
|
||||
),
|
||||
icon: 'pencil',
|
||||
disabled: !canEditSynthetics || !canUpdatePrivateMonitor,
|
||||
disabled: !canEditSynthetics,
|
||||
href: editUrl,
|
||||
},
|
||||
{
|
||||
name: (
|
||||
<NoPermissionsTooltip
|
||||
canEditSynthetics={canEditSynthetics}
|
||||
canUpdatePrivateMonitor={canUpdatePrivateMonitor}
|
||||
>
|
||||
<NoPermissionsTooltip canEditSynthetics={canEditSynthetics}>
|
||||
{enableLabel}
|
||||
</NoPermissionsTooltip>
|
||||
),
|
||||
icon: 'invert',
|
||||
disabled: !canEditSynthetics || !canUpdatePrivateMonitor,
|
||||
disabled: !canEditSynthetics,
|
||||
onClick: () => {
|
||||
if (status !== FETCH_STATUS.LOADING) {
|
||||
updateMonitorEnabledState(!monitor.isEnabled);
|
||||
|
|
|
@ -19,10 +19,10 @@ import {
|
|||
EuiButton,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useCanManagePrivateLocation } from '../../../hooks/use_fleet_permissions';
|
||||
import { NoPermissionsTooltip } from '../../common/components/permissions';
|
||||
import { useSyntheticsSettingsContext } from '../../../contexts';
|
||||
import { useFormWrapped } from '../../../../../hooks/use_form_wrapped';
|
||||
import { PrivateLocation } from '../../../../../../common/runtime_types';
|
||||
import { FleetPermissionsCallout } from '../../common/components/permissions';
|
||||
import { LocationForm } from './location_form';
|
||||
import { ManageEmptyState } from './manage_empty_state';
|
||||
|
||||
|
@ -53,10 +53,9 @@ export const AddLocationFlyout = ({
|
|||
},
|
||||
});
|
||||
|
||||
const { canSave } = useSyntheticsSettingsContext();
|
||||
|
||||
const { handleSubmit } = form;
|
||||
|
||||
const canManagePrivateLocation = useCanManagePrivateLocation();
|
||||
|
||||
const closeFlyout = () => {
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
@ -70,45 +69,38 @@ export const AddLocationFlyout = ({
|
|||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<ManageEmptyState
|
||||
privateLocations={privateLocations}
|
||||
hasFleetPermissions={canManagePrivateLocation}
|
||||
showEmptyLocations={false}
|
||||
>
|
||||
{!canManagePrivateLocation && <FleetPermissionsCallout />}
|
||||
<LocationForm
|
||||
privateLocations={privateLocations}
|
||||
hasPermissions={canManagePrivateLocation}
|
||||
/>
|
||||
<ManageEmptyState privateLocations={privateLocations} showEmptyLocations={false}>
|
||||
<LocationForm privateLocations={privateLocations} />
|
||||
</ManageEmptyState>
|
||||
</EuiFlyoutBody>
|
||||
{canManagePrivateLocation && (
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="syntheticsAddLocationFlyoutButton"
|
||||
iconType="cross"
|
||||
onClick={closeFlyout}
|
||||
flush="left"
|
||||
isLoading={isLoading}
|
||||
>
|
||||
{CANCEL_LABEL}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="syntheticsAddLocationFlyoutButton"
|
||||
iconType="cross"
|
||||
onClick={closeFlyout}
|
||||
flush="left"
|
||||
isLoading={isLoading}
|
||||
>
|
||||
{CANCEL_LABEL}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<NoPermissionsTooltip canEditSynthetics={canSave}>
|
||||
<EuiButton
|
||||
data-test-subj="syntheticsAddLocationFlyoutButton"
|
||||
fill
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
isLoading={isLoading}
|
||||
isDisabled={!canSave}
|
||||
>
|
||||
{SAVE_LABEL}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
)}
|
||||
</NoPermissionsTooltip>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</EuiFlyout>
|
||||
</FormProvider>
|
||||
);
|
||||
|
|
|
@ -6,45 +6,55 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiLink, EuiButton, EuiEmptyPrompt, EuiTitle } from '@elastic/eui';
|
||||
import { EuiLink, EuiButton, EuiEmptyPrompt, EuiTitle, EuiSpacer } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useFleetPermissions } from '../../../hooks';
|
||||
import { FleetPermissionsCallout, NoPermissionsTooltip } from '../../common/components/permissions';
|
||||
import { useSyntheticsSettingsContext } from '../../../contexts';
|
||||
import { LEARN_MORE, READ_DOCS } from './empty_locations';
|
||||
|
||||
export const AgentPolicyNeeded = ({ disabled }: { disabled: boolean }) => {
|
||||
const { basePath } = useSyntheticsSettingsContext();
|
||||
export const AgentPolicyNeeded = () => {
|
||||
const { basePath, canSave } = useSyntheticsSettingsContext();
|
||||
|
||||
const { canCreateAgentPolicies } = useFleetPermissions();
|
||||
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
hasBorder
|
||||
title={<h2>{AGENT_POLICY_NEEDED}</h2>}
|
||||
body={<p>{ADD_AGENT_POLICY_DESCRIPTION}</p>}
|
||||
actions={
|
||||
<EuiButton
|
||||
data-test-subj="syntheticsAgentPolicyNeededButton"
|
||||
fill
|
||||
href={`${basePath}/app/fleet/policies?create`}
|
||||
color="primary"
|
||||
isDisabled={disabled}
|
||||
>
|
||||
{CREATE_AGENT_POLICY}
|
||||
</EuiButton>
|
||||
}
|
||||
footer={
|
||||
<>
|
||||
<EuiTitle size="xxs">
|
||||
<h3>{LEARN_MORE}</h3>
|
||||
</EuiTitle>
|
||||
<EuiLink
|
||||
data-test-subj="syntheticsAgentPolicyNeededLink"
|
||||
target="_blank"
|
||||
href="https://www.elastic.co/guide/en/observability/current/uptime-set-up-choose-agent.html#private-locations"
|
||||
>
|
||||
{READ_DOCS}
|
||||
</EuiLink>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<>
|
||||
{!canCreateAgentPolicies && <FleetPermissionsCallout />}
|
||||
<EuiSpacer size="m" />
|
||||
<EuiEmptyPrompt
|
||||
hasBorder
|
||||
title={<h2>{AGENT_POLICY_NEEDED}</h2>}
|
||||
body={<p>{ADD_AGENT_POLICY_DESCRIPTION}</p>}
|
||||
actions={
|
||||
<NoPermissionsTooltip canEditSynthetics={canSave}>
|
||||
<EuiButton
|
||||
data-test-subj="syntheticsAgentPolicyNeededButton"
|
||||
fill
|
||||
href={`${basePath}/app/fleet/policies?create`}
|
||||
color="primary"
|
||||
isDisabled={!canSave || !canCreateAgentPolicies}
|
||||
>
|
||||
{CREATE_AGENT_POLICY}
|
||||
</EuiButton>
|
||||
</NoPermissionsTooltip>
|
||||
}
|
||||
footer={
|
||||
<>
|
||||
<EuiTitle size="xxs">
|
||||
<h3>{LEARN_MORE}</h3>
|
||||
</EuiTitle>
|
||||
<EuiLink
|
||||
data-test-subj="syntheticsAgentPolicyNeededLink"
|
||||
target="_blank"
|
||||
href="https://www.elastic.co/guide/en/observability/current/uptime-set-up-choose-agent.html#private-locations"
|
||||
>
|
||||
{READ_DOCS}
|
||||
</EuiLink>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -9,8 +9,6 @@ import React, { useState } from 'react';
|
|||
import { EuiButtonIcon, EuiConfirmModal, EuiToolTip } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useSyntheticsSettingsContext } from '../../../contexts';
|
||||
import { useFleetPermissions, useCanManagePrivateLocation } from '../../../hooks';
|
||||
import { CANNOT_SAVE_INTEGRATION_LABEL } from '../../common/components/permissions';
|
||||
|
||||
export const DeleteLocation = ({
|
||||
loading,
|
||||
|
@ -29,18 +27,17 @@ export const DeleteLocation = ({
|
|||
const canDelete = monCount === 0;
|
||||
|
||||
const { canSave } = useSyntheticsSettingsContext();
|
||||
const { canSaveIntegrations } = useFleetPermissions();
|
||||
const canManagePrivateLocation = useCanManagePrivateLocation();
|
||||
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
const deleteDisabledReason = !canSaveIntegrations
|
||||
? CANNOT_SAVE_INTEGRATION_LABEL
|
||||
: i18n.translate('xpack.synthetics.monitorManagement.cannotDelete.description', {
|
||||
defaultMessage: `You can't delete this location because it is used in {monCount, number} {monCount, plural,one {monitor} other {monitors}}.
|
||||
const deleteDisabledReason = i18n.translate(
|
||||
'xpack.synthetics.monitorManagement.cannotDelete.description',
|
||||
{
|
||||
defaultMessage: `You can't delete this location because it is used in {monCount, number} {monCount, plural,one {monitor} other {monitors}}.
|
||||
Remove this location from all monitors first.`,
|
||||
values: { monCount },
|
||||
});
|
||||
values: { monCount },
|
||||
}
|
||||
);
|
||||
|
||||
const deleteModal = (
|
||||
<EuiConfirmModal
|
||||
|
@ -63,9 +60,7 @@ export const DeleteLocation = ({
|
|||
return (
|
||||
<>
|
||||
{isModalOpen && deleteModal}
|
||||
<EuiToolTip
|
||||
content={canDelete && canManagePrivateLocation ? DELETE_LABEL : deleteDisabledReason}
|
||||
>
|
||||
<EuiToolTip content={canDelete ? DELETE_LABEL : deleteDisabledReason}>
|
||||
<EuiButtonIcon
|
||||
data-test-subj={`deleteLocation-${id}`}
|
||||
isLoading={loading}
|
||||
|
@ -75,7 +70,7 @@ export const DeleteLocation = ({
|
|||
onClick={() => {
|
||||
setIsModalOpen(true);
|
||||
}}
|
||||
isDisabled={!canDelete || !canManagePrivateLocation || !canSave}
|
||||
isDisabled={!canDelete || !canSave}
|
||||
/>
|
||||
</EuiToolTip>
|
||||
</>
|
||||
|
|
|
@ -10,23 +10,25 @@ import { useHistory } from 'react-router-dom';
|
|||
import { EuiEmptyPrompt, EuiButton, EuiLink, EuiText } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { NoPermissionsTooltip } from '../../common/components/permissions';
|
||||
import { useSyntheticsSettingsContext } from '../../../contexts';
|
||||
import { PRIVATE_LOCATIOSN_ROUTE } from '../../../../../../common/constants';
|
||||
import { setAddingNewPrivateLocation, setManageFlyoutOpen } from '../../../state/private_locations';
|
||||
|
||||
export const EmptyLocations = ({
|
||||
inFlyout = true,
|
||||
setIsAddingNew,
|
||||
disabled,
|
||||
redirectToSettings,
|
||||
}: {
|
||||
inFlyout?: boolean;
|
||||
disabled?: boolean;
|
||||
setIsAddingNew?: (val: boolean) => void;
|
||||
redirectToSettings?: boolean;
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const history = useHistory();
|
||||
|
||||
const { canSave } = useSyntheticsSettingsContext();
|
||||
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
hasBorder
|
||||
|
@ -38,35 +40,37 @@ export const EmptyLocations = ({
|
|||
</EuiText>
|
||||
}
|
||||
actions={
|
||||
redirectToSettings ? (
|
||||
<EuiButton
|
||||
data-test-subj="syntheticsEmptyLocationsButton"
|
||||
iconType="plusInCircle"
|
||||
color="primary"
|
||||
fill
|
||||
isDisabled={disabled}
|
||||
href={history.createHref({
|
||||
pathname: PRIVATE_LOCATIOSN_ROUTE,
|
||||
})}
|
||||
>
|
||||
{ADD_LOCATION}
|
||||
</EuiButton>
|
||||
) : (
|
||||
<EuiButton
|
||||
data-test-subj="syntheticsEmptyLocationsButton"
|
||||
iconType="plusInCircle"
|
||||
disabled={disabled}
|
||||
color="primary"
|
||||
fill
|
||||
onClick={() => {
|
||||
setIsAddingNew?.(true);
|
||||
dispatch(setManageFlyoutOpen(true));
|
||||
dispatch(setAddingNewPrivateLocation(true));
|
||||
}}
|
||||
>
|
||||
{ADD_LOCATION}
|
||||
</EuiButton>
|
||||
)
|
||||
<NoPermissionsTooltip canEditSynthetics={canSave}>
|
||||
{redirectToSettings ? (
|
||||
<EuiButton
|
||||
data-test-subj="syntheticsEmptyLocationsButton"
|
||||
iconType="plusInCircle"
|
||||
color="primary"
|
||||
fill
|
||||
isDisabled={!canSave}
|
||||
href={history.createHref({
|
||||
pathname: PRIVATE_LOCATIOSN_ROUTE,
|
||||
})}
|
||||
>
|
||||
{ADD_LOCATION}
|
||||
</EuiButton>
|
||||
) : (
|
||||
<EuiButton
|
||||
data-test-subj="syntheticsEmptyLocationsButton"
|
||||
iconType="plusInCircle"
|
||||
isDisabled={!canSave}
|
||||
color="primary"
|
||||
fill
|
||||
onClick={() => {
|
||||
setIsAddingNew?.(true);
|
||||
dispatch(setManageFlyoutOpen(true));
|
||||
dispatch(setAddingNewPrivateLocation(true));
|
||||
}}
|
||||
>
|
||||
{ADD_LOCATION}
|
||||
</EuiButton>
|
||||
)}
|
||||
</NoPermissionsTooltip>
|
||||
}
|
||||
footer={
|
||||
<EuiText size="s">
|
||||
|
|
|
@ -24,14 +24,7 @@ import { AgentPolicyNeeded } from './agent_policy_needed';
|
|||
import { PolicyHostsField } from './policy_hosts';
|
||||
import { selectAgentPolicies } from '../../../state/private_locations';
|
||||
|
||||
export const LocationForm = ({
|
||||
privateLocations,
|
||||
hasPermissions,
|
||||
}: {
|
||||
onDiscard?: () => void;
|
||||
privateLocations: PrivateLocation[];
|
||||
hasPermissions: boolean;
|
||||
}) => {
|
||||
export const LocationForm = ({ privateLocations }: { privateLocations: PrivateLocation[] }) => {
|
||||
const { data } = useSelector(selectAgentPolicies);
|
||||
const { control, register, watch } = useFormContext<PrivateLocation>();
|
||||
const { errors } = useFormState();
|
||||
|
@ -45,50 +38,82 @@ export const LocationForm = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
{data?.items.length === 0 && <AgentPolicyNeeded disabled={!hasPermissions} />}
|
||||
{hasPermissions ? (
|
||||
<EuiForm component="form" noValidate>
|
||||
<EuiFormRow
|
||||
{data?.items.length === 0 && <AgentPolicyNeeded />}
|
||||
<EuiForm component="form" noValidate>
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
label={LOCATION_NAME_LABEL}
|
||||
isInvalid={Boolean(errors?.label)}
|
||||
error={errors?.label?.message}
|
||||
>
|
||||
<EuiFieldText
|
||||
data-test-subj="syntheticsLocationFormFieldText"
|
||||
fullWidth
|
||||
label={LOCATION_NAME_LABEL}
|
||||
isInvalid={Boolean(errors?.label)}
|
||||
error={errors?.label?.message}
|
||||
aria-label={LOCATION_NAME_LABEL}
|
||||
{...register('label', {
|
||||
required: {
|
||||
value: true,
|
||||
message: NAME_REQUIRED,
|
||||
},
|
||||
validate: (val: string) => {
|
||||
return privateLocations.some((loc) => loc.label === val)
|
||||
? NAME_ALREADY_EXISTS
|
||||
: undefined;
|
||||
},
|
||||
})}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiSpacer />
|
||||
<PolicyHostsField errors={errors} control={control} privateLocations={privateLocations} />
|
||||
<EuiSpacer />
|
||||
<TagsField tagsList={tagsList} control={control} errors={errors} />
|
||||
<EuiSpacer />
|
||||
<EuiCallOut title={AGENT_CALLOUT_TITLE} size="s" style={{ textAlign: 'left' }}>
|
||||
<p>
|
||||
{
|
||||
<FormattedMessage
|
||||
id="xpack.synthetics.monitorManagement.agentCallout.content"
|
||||
defaultMessage='To run "Browser" monitors on this private location, make sure that you're using the {code} Docker container, which contains the dependencies necessary to run these monitors. For more information, {link}.'
|
||||
values={{
|
||||
code: <EuiCode>elastic-agent-complete</EuiCode>,
|
||||
link: (
|
||||
<EuiLink
|
||||
data-test-subj="syntheticsLocationFormReadTheDocsLink"
|
||||
target="_blank"
|
||||
href="https://www.elastic.co/guide/en/observability/current/uptime-set-up-choose-agent.html#private-locations"
|
||||
external
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.synthetics.monitorManagement.agentCallout.link"
|
||||
defaultMessage="read the docs"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
}
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
|
||||
<EuiSpacer />
|
||||
{selectedPolicy?.agents === 0 && (
|
||||
<EuiCallOut
|
||||
title={AGENT_MISSING_CALLOUT_TITLE}
|
||||
size="s"
|
||||
style={{ textAlign: 'left' }}
|
||||
color="warning"
|
||||
>
|
||||
<EuiFieldText
|
||||
data-test-subj="syntheticsLocationFormFieldText"
|
||||
fullWidth
|
||||
aria-label={LOCATION_NAME_LABEL}
|
||||
{...register('label', {
|
||||
required: {
|
||||
value: true,
|
||||
message: NAME_REQUIRED,
|
||||
},
|
||||
validate: (val: string) => {
|
||||
return privateLocations.some((loc) => loc.label === val)
|
||||
? NAME_ALREADY_EXISTS
|
||||
: undefined;
|
||||
},
|
||||
})}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiSpacer />
|
||||
<PolicyHostsField errors={errors} control={control} privateLocations={privateLocations} />
|
||||
<EuiSpacer />
|
||||
<TagsField tagsList={tagsList} control={control} errors={errors} />
|
||||
<EuiSpacer />
|
||||
<EuiCallOut title={AGENT_CALLOUT_TITLE} size="s" style={{ textAlign: 'left' }}>
|
||||
<p>
|
||||
{
|
||||
<FormattedMessage
|
||||
id="xpack.synthetics.monitorManagement.agentCallout.content"
|
||||
defaultMessage='To run "Browser" monitors on this private location, make sure that you're using the {code} Docker container, which contains the dependencies necessary to run these monitors. For more information, {link}.'
|
||||
id="xpack.synthetics.monitorManagement.agentMissingCallout.content"
|
||||
defaultMessage="You have selected an agent policy that has no agent attached. Make sure that you have at least one agent enrolled in this policy. You can add an agent before or after creating a location. For more information, {link}."
|
||||
values={{
|
||||
code: <EuiCode>elastic-agent-complete</EuiCode>,
|
||||
link: (
|
||||
<EuiLink
|
||||
data-test-subj="syntheticsLocationFormReadTheDocsLink"
|
||||
target="_blank"
|
||||
href="https://www.elastic.co/guide/en/observability/current/uptime-set-up-choose-agent.html#private-locations"
|
||||
href="https://www.elastic.co/guide/en/observability/current/synthetics-private-location.html#synthetics-private-location-fleet-agent"
|
||||
external
|
||||
>
|
||||
<FormattedMessage
|
||||
|
@ -102,42 +127,8 @@ export const LocationForm = ({
|
|||
}
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
|
||||
<EuiSpacer />
|
||||
{selectedPolicy?.agents === 0 && (
|
||||
<EuiCallOut
|
||||
title={AGENT_MISSING_CALLOUT_TITLE}
|
||||
size="s"
|
||||
style={{ textAlign: 'left' }}
|
||||
color="warning"
|
||||
>
|
||||
<p>
|
||||
{
|
||||
<FormattedMessage
|
||||
id="xpack.synthetics.monitorManagement.agentMissingCallout.content"
|
||||
defaultMessage="You have selected an agent policy that has no agent attached. Make sure that you have at least one agent enrolled in this policy. You can add agent before or after creating a location. For more information, {link}."
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink
|
||||
data-test-subj="syntheticsLocationFormReadTheDocsLink"
|
||||
target="_blank"
|
||||
href="https://www.elastic.co/guide/en/observability/current/synthetics-private-location.html#synthetics-private-location-fleet-agent"
|
||||
external
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.synthetics.monitorManagement.agentCallout.link"
|
||||
defaultMessage="read the docs"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
}
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
)}
|
||||
</EuiForm>
|
||||
) : null}
|
||||
)}
|
||||
</EuiForm>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -22,14 +22,10 @@ import { ViewLocationMonitors } from './view_location_monitors';
|
|||
import { TableTitle } from '../../common/components/table_title';
|
||||
import { TAGS_LABEL } from '../components/tags_field';
|
||||
import { useSyntheticsSettingsContext } from '../../../contexts';
|
||||
import { useCanManagePrivateLocation } from '../../../hooks';
|
||||
import { setAddingNewPrivateLocation } from '../../../state/private_locations';
|
||||
import { PrivateLocationDocsLink, START_ADDING_LOCATIONS_DESCRIPTION } from './empty_locations';
|
||||
import { PrivateLocation } from '../../../../../../common/runtime_types';
|
||||
import {
|
||||
CANNOT_SAVE_INTEGRATION_LABEL,
|
||||
NoPermissionsTooltip,
|
||||
} from '../../common/components/permissions';
|
||||
import { NoPermissionsTooltip } from '../../common/components/permissions';
|
||||
import { DeleteLocation } from './delete_location';
|
||||
import { useLocationMonitors } from './hooks/use_location_monitors';
|
||||
import { PolicyName } from './policy_name';
|
||||
|
@ -56,7 +52,6 @@ export const PrivateLocationsTable = ({
|
|||
const { locationMonitors, loading } = useLocationMonitors();
|
||||
|
||||
const { canSave } = useSyntheticsSettingsContext();
|
||||
const canManagePrivateLocations = useCanManagePrivateLocation();
|
||||
|
||||
const tagsList = privateLocations.reduce((acc, item) => {
|
||||
const tags = item.tags || [];
|
||||
|
@ -137,10 +132,9 @@ export const PrivateLocationsTable = ({
|
|||
fill
|
||||
data-test-subj={'addPrivateLocationButton'}
|
||||
isLoading={loading}
|
||||
disabled={!canManagePrivateLocations || !canSave}
|
||||
disabled={!canSave}
|
||||
onClick={() => setIsAddingNew(true)}
|
||||
iconType="plusInCircle"
|
||||
title={!canManagePrivateLocations ? CANNOT_SAVE_INTEGRATION_LABEL : undefined}
|
||||
>
|
||||
{ADD_LABEL}
|
||||
</EuiButton>
|
||||
|
|
|
@ -14,7 +14,6 @@ import { selectAgentPolicies } from '../../../state/private_locations';
|
|||
|
||||
export const ManageEmptyState: FC<{
|
||||
privateLocations: PrivateLocation[];
|
||||
hasFleetPermissions: boolean;
|
||||
setIsAddingNew?: (val: boolean) => void;
|
||||
showNeedAgentPolicy?: boolean;
|
||||
showEmptyLocations?: boolean;
|
||||
|
@ -22,18 +21,17 @@ export const ManageEmptyState: FC<{
|
|||
children,
|
||||
privateLocations,
|
||||
setIsAddingNew,
|
||||
hasFleetPermissions,
|
||||
showNeedAgentPolicy = true,
|
||||
showEmptyLocations = true,
|
||||
}) => {
|
||||
const { data: agentPolicies } = useSelector(selectAgentPolicies);
|
||||
|
||||
if (agentPolicies?.total === 0 && showNeedAgentPolicy) {
|
||||
return <AgentPolicyNeeded disabled={!hasFleetPermissions} />;
|
||||
return <AgentPolicyNeeded />;
|
||||
}
|
||||
|
||||
if (privateLocations.length === 0 && showEmptyLocations) {
|
||||
return <EmptyLocations setIsAddingNew={setIsAddingNew} disabled={!hasFleetPermissions} />;
|
||||
return <EmptyLocations setIsAddingNew={setIsAddingNew} />;
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
|
|
|
@ -13,6 +13,7 @@ import * as settingsHooks from '../../../contexts/synthetics_settings_context';
|
|||
import type { SyntheticsSettingsContextValues } from '../../../contexts';
|
||||
import { ManagePrivateLocations } from './manage_private_locations';
|
||||
import { PrivateLocation } from '../../../../../../common/runtime_types';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
|
||||
jest.mock('../../../hooks');
|
||||
jest.mock('./hooks/use_locations_api');
|
||||
|
@ -24,6 +25,7 @@ describe('<ManagePrivateLocations />', () => {
|
|||
jest.spyOn(permissionsHooks, 'useFleetPermissions').mockReturnValue({
|
||||
canReadAgentPolicies: true,
|
||||
canSaveIntegrations: false,
|
||||
canCreateAgentPolicies: false,
|
||||
});
|
||||
jest.spyOn(locationHooks, 'usePrivateLocationsAPI').mockReturnValue({
|
||||
formData: {} as PrivateLocation,
|
||||
|
@ -33,18 +35,15 @@ describe('<ManagePrivateLocations />', () => {
|
|||
onDelete: jest.fn(),
|
||||
deleteLoading: false,
|
||||
});
|
||||
jest.spyOn(settingsHooks, 'useSyntheticsSettingsContext').mockReturnValue({
|
||||
canSave: true,
|
||||
} as SyntheticsSettingsContextValues);
|
||||
});
|
||||
|
||||
it.each([true, false])(
|
||||
'handles no agent found when the user does and does not have permissions',
|
||||
(hasFleetPermissions) => {
|
||||
jest
|
||||
.spyOn(permissionsHooks, 'useCanManagePrivateLocation')
|
||||
.mockReturnValue(hasFleetPermissions);
|
||||
const { getByText, getByRole, queryByText } = render(<ManagePrivateLocations />, {
|
||||
async (canSave) => {
|
||||
jest.spyOn(settingsHooks, 'useSyntheticsSettingsContext').mockReturnValue({
|
||||
canSave,
|
||||
} as SyntheticsSettingsContextValues);
|
||||
const { getByText, getByRole, findByText } = render(<ManagePrivateLocations />, {
|
||||
state: {
|
||||
agentPolicies: {
|
||||
data: {
|
||||
|
@ -62,27 +61,28 @@ describe('<ManagePrivateLocations />', () => {
|
|||
});
|
||||
expect(getByText('No agent policies found')).toBeInTheDocument();
|
||||
|
||||
if (hasFleetPermissions) {
|
||||
const button = getByRole('link', { name: 'Create agent policy' });
|
||||
expect(button).not.toBeDisabled();
|
||||
expect(
|
||||
queryByText(/You are not authorized to manage private locations./)
|
||||
).not.toBeInTheDocument();
|
||||
if (canSave) {
|
||||
const button = getByRole('button', { name: 'Create agent policy' });
|
||||
expect(button).toBeDisabled();
|
||||
} else {
|
||||
const button = getByRole('button', { name: 'Create agent policy' });
|
||||
expect(button).toBeDisabled();
|
||||
expect(getByText(/You are not authorized to manage private locations./));
|
||||
// hover over the button to see the tooltip
|
||||
fireEvent.mouseOver(button);
|
||||
expect(
|
||||
await findByText(/You do not have sufficient permissions to perform this action./)
|
||||
).toBeInTheDocument();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
it.each([true, false])(
|
||||
'handles create first location when the user does and does not have permissions',
|
||||
(hasFleetPermissions) => {
|
||||
jest
|
||||
.spyOn(permissionsHooks, 'useCanManagePrivateLocation')
|
||||
.mockReturnValue(hasFleetPermissions);
|
||||
const { getByText, getByRole, queryByText } = render(<ManagePrivateLocations />, {
|
||||
async (canSave) => {
|
||||
jest.spyOn(settingsHooks, 'useSyntheticsSettingsContext').mockReturnValue({
|
||||
canSave,
|
||||
} as SyntheticsSettingsContextValues);
|
||||
const { getByText, getByRole, findByText } = render(<ManagePrivateLocations />, {
|
||||
state: {
|
||||
agentPolicies: {
|
||||
data: {
|
||||
|
@ -101,29 +101,26 @@ describe('<ManagePrivateLocations />', () => {
|
|||
expect(getByText('Create your first private location')).toBeInTheDocument();
|
||||
const button = getByRole('button', { name: 'Create location' });
|
||||
|
||||
if (hasFleetPermissions) {
|
||||
if (canSave) {
|
||||
expect(button).not.toBeDisabled();
|
||||
expect(
|
||||
queryByText(/You are not authorized to manage private locations./)
|
||||
).not.toBeInTheDocument();
|
||||
} else {
|
||||
expect(button).toBeDisabled();
|
||||
expect(getByText(/You are not authorized to manage private locations./));
|
||||
fireEvent.mouseOver(button);
|
||||
expect(
|
||||
await findByText(/You do not have sufficient permissions to perform this action./)
|
||||
).toBeInTheDocument();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
it.each([true, false])(
|
||||
'handles location table when the user does and does not have permissions',
|
||||
(hasFleetPermissions) => {
|
||||
async (canSave) => {
|
||||
const privateLocationName = 'Test private location';
|
||||
jest
|
||||
.spyOn(permissionsHooks, 'useCanManagePrivateLocation')
|
||||
.mockReturnValue(hasFleetPermissions);
|
||||
jest.spyOn(permissionsHooks, 'useFleetPermissions').mockReturnValue({
|
||||
canSaveIntegrations: hasFleetPermissions,
|
||||
canReadAgentPolicies: hasFleetPermissions,
|
||||
});
|
||||
jest.spyOn(settingsHooks, 'useSyntheticsSettingsContext').mockReturnValue({
|
||||
canSave,
|
||||
} as SyntheticsSettingsContextValues);
|
||||
|
||||
jest.spyOn(locationHooks, 'usePrivateLocationsAPI').mockReturnValue({
|
||||
formData: {} as PrivateLocation,
|
||||
loading: false,
|
||||
|
@ -140,7 +137,7 @@ describe('<ManagePrivateLocations />', () => {
|
|||
onDelete: jest.fn(),
|
||||
deleteLoading: false,
|
||||
});
|
||||
const { getByText, getByRole, queryByText } = render(<ManagePrivateLocations />, {
|
||||
const { getByText, getByRole, findByText } = render(<ManagePrivateLocations />, {
|
||||
state: {
|
||||
agentPolicies: {
|
||||
data: {
|
||||
|
@ -159,14 +156,14 @@ describe('<ManagePrivateLocations />', () => {
|
|||
expect(getByText(privateLocationName)).toBeInTheDocument();
|
||||
const button = getByRole('button', { name: 'Create location' });
|
||||
|
||||
if (hasFleetPermissions) {
|
||||
if (canSave) {
|
||||
expect(button).not.toBeDisabled();
|
||||
expect(
|
||||
queryByText(/You are not authorized to manage private locations./)
|
||||
).not.toBeInTheDocument();
|
||||
} else {
|
||||
expect(button).toBeDisabled();
|
||||
expect(getByText(/You are not authorized to manage private locations./));
|
||||
fireEvent.mouseOver(button);
|
||||
expect(
|
||||
await findByText('You do not have sufficient permissions to perform this action.')
|
||||
).toBeInTheDocument();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -6,10 +6,8 @@
|
|||
*/
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
import { LoadingState } from '../../monitors_page/overview/overview/monitor_detail_flyout';
|
||||
import { PrivateLocationsTable } from './locations_table';
|
||||
import { useCanManagePrivateLocation, useFleetPermissions } from '../../../hooks';
|
||||
import { ManageEmptyState } from './manage_empty_state';
|
||||
import { AddLocationFlyout } from './add_location_flyout';
|
||||
import { usePrivateLocationsAPI } from './hooks/use_locations_api';
|
||||
|
@ -20,7 +18,6 @@ import {
|
|||
} from '../../../state/private_locations';
|
||||
import { PrivateLocation } from '../../../../../../common/runtime_types';
|
||||
import { getServiceLocations } from '../../../state';
|
||||
import { FleetPermissionsCallout } from '../../common/components/permissions';
|
||||
|
||||
export const ManagePrivateLocations = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
@ -33,20 +30,15 @@ export const ManagePrivateLocations = () => {
|
|||
|
||||
const { onSubmit, loading, privateLocations, onDelete, deleteLoading } = usePrivateLocationsAPI();
|
||||
|
||||
const { canReadAgentPolicies } = useFleetPermissions();
|
||||
const canManagePrivateLocation = useCanManagePrivateLocation();
|
||||
|
||||
// make sure flyout is closed when first visiting the page
|
||||
useEffect(() => {
|
||||
setIsAddingNew(false);
|
||||
}, [setIsAddingNew]);
|
||||
|
||||
useEffect(() => {
|
||||
if (canReadAgentPolicies) {
|
||||
dispatch(getAgentPoliciesAction.get());
|
||||
}
|
||||
dispatch(getAgentPoliciesAction.get());
|
||||
dispatch(getServiceLocations());
|
||||
}, [dispatch, canReadAgentPolicies]);
|
||||
}, [dispatch]);
|
||||
|
||||
const handleSubmit = (formData: PrivateLocation) => {
|
||||
onSubmit(formData);
|
||||
|
@ -54,21 +46,10 @@ export const ManagePrivateLocations = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
{!canManagePrivateLocation && (
|
||||
<>
|
||||
<FleetPermissionsCallout />
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
|
||||
{loading ? (
|
||||
<LoadingState />
|
||||
) : (
|
||||
<ManageEmptyState
|
||||
privateLocations={privateLocations}
|
||||
setIsAddingNew={setIsAddingNew}
|
||||
hasFleetPermissions={canManagePrivateLocation}
|
||||
>
|
||||
<ManageEmptyState privateLocations={privateLocations} setIsAddingNew={setIsAddingNew}>
|
||||
<PrivateLocationsTable
|
||||
privateLocations={privateLocations}
|
||||
onDelete={onDelete}
|
||||
|
|
|
@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { useFetcher } from '@kbn/observability-shared-plugin/public';
|
||||
import { HelpCommands } from './help_commands';
|
||||
import { LoadingState } from '../../monitors_page/overview/overview/monitor_detail_flyout';
|
||||
import { fetchServiceAPIKey } from '../../../state/monitor_management/api';
|
||||
import { fetchProjectAPIKey } from '../../../state/monitor_management/api';
|
||||
import { ClientPluginsStart } from '../../../../../plugin';
|
||||
import { ApiKeyBtn } from './api_key_btn';
|
||||
import { useEnablement } from '../../../hooks';
|
||||
|
@ -33,7 +33,7 @@ export const ProjectAPIKeys = () => {
|
|||
|
||||
const { data, loading } = useFetcher(async () => {
|
||||
if (loadAPIKey) {
|
||||
return fetchServiceAPIKey();
|
||||
return fetchProjectAPIKey();
|
||||
}
|
||||
return null;
|
||||
}, [loadAPIKey]);
|
||||
|
|
|
@ -18,19 +18,15 @@ export function useFleetPermissions() {
|
|||
|
||||
const canSaveIntegrations: boolean = Boolean(fleet?.authz.integrations.writeIntegrationPolicies);
|
||||
const canReadAgentPolicies = Boolean(fleet?.authz.fleet.readAgentPolicies);
|
||||
const canCreateAgentPolicies = Boolean(fleet?.authz.fleet.all);
|
||||
|
||||
return {
|
||||
canReadAgentPolicies,
|
||||
canSaveIntegrations,
|
||||
canCreateAgentPolicies,
|
||||
};
|
||||
}
|
||||
|
||||
export function useCanUpdatePrivateMonitor(monitor: EncryptedSyntheticsMonitor) {
|
||||
const { canSaveIntegrations } = useFleetPermissions();
|
||||
|
||||
return canUpdatePrivateMonitor(monitor, canSaveIntegrations);
|
||||
}
|
||||
|
||||
export function useCanManagePrivateLocation() {
|
||||
const { canSaveIntegrations, canReadAgentPolicies } = useFleetPermissions();
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ export const getDecryptedMonitorAPI = async ({ id }: { id: string }): Promise<Sy
|
|||
SyntheticsMonitorCodec
|
||||
);
|
||||
|
||||
export const fetchServiceAPIKey = async (): Promise<{
|
||||
export const fetchProjectAPIKey = async (): Promise<{
|
||||
apiKey: { encoded: string };
|
||||
}> => {
|
||||
return await apiService.get(SYNTHETICS_API_URLS.SYNTHETICS_APIKEY);
|
||||
|
|
|
@ -10,23 +10,8 @@ import { PrivateLocation, SyntheticsPrivateLocations } from '../../../../../comm
|
|||
import { apiService } from '../../../../utils/api_service/api_service';
|
||||
import { AgentPoliciesList } from '.';
|
||||
|
||||
const FLEET_URLS = {
|
||||
AGENT_POLICIES: '/api/fleet/agent_policies',
|
||||
};
|
||||
|
||||
export const fetchAgentPolicies = async (): Promise<AgentPoliciesList> => {
|
||||
return await apiService.get(
|
||||
FLEET_URLS.AGENT_POLICIES,
|
||||
{
|
||||
page: 1,
|
||||
perPage: 10000,
|
||||
sortField: 'name',
|
||||
sortOrder: 'asc',
|
||||
full: true,
|
||||
kuery: 'ingest-agent-policies.is_managed : false',
|
||||
},
|
||||
null
|
||||
);
|
||||
return await apiService.get(SYNTHETICS_API_URLS.AGENT_POLICIES);
|
||||
};
|
||||
|
||||
export const addSyntheticsPrivateLocations = async (
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getAgentPoliciesRoute } from './settings/private_locations/get_agent_policies';
|
||||
import { inspectSyntheticsMonitorRoute } from './monitor_cruds/inspect_monitor';
|
||||
import { deletePackagePolicyRoute } from './monitor_cruds/delete_integration';
|
||||
import { createJourneyScreenshotRoute } from './pings/journey_screenshots';
|
||||
|
@ -100,6 +101,7 @@ export const syntheticsAppRestApiRoutes: SyntheticsRestApiRouteFactory[] = [
|
|||
getPrivateLocationsRoute,
|
||||
getSyntheticsFilters,
|
||||
inspectSyntheticsMonitorRoute,
|
||||
getAgentPoliciesRoute,
|
||||
];
|
||||
|
||||
export const syntheticsAppStreamingApiRoutes: SyntheticsStreamingRouteFactory[] = [
|
||||
|
|
|
@ -31,6 +31,7 @@ export const addSyntheticsProjectMonitorRoute: SyntheticsRestApiRouteFactory = (
|
|||
maxBytes: MAX_PAYLOAD_SIZE,
|
||||
},
|
||||
},
|
||||
writeAccess: true,
|
||||
handler: async (routeContext): Promise<any> => {
|
||||
const { request, response, server } = routeContext;
|
||||
const { projectName } = request.params;
|
||||
|
|
|
@ -8,7 +8,6 @@ import { schema } from '@kbn/config-schema';
|
|||
import { SavedObjectsClientContract, SavedObjectsErrorHelpers } from '@kbn/core/server';
|
||||
import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common';
|
||||
import { syntheticsMonitorType } from '../../../common/types/saved_objects';
|
||||
import { deletePermissionError } from '../../synthetics_service/private_location/synthetics_private_location';
|
||||
import {
|
||||
ConfigKey,
|
||||
EncryptedSyntheticsMonitor,
|
||||
|
@ -79,19 +78,12 @@ export const deleteMonitor = async ({
|
|||
server
|
||||
);
|
||||
|
||||
const { locations } = monitor.attributes;
|
||||
|
||||
const hasPrivateLocation = locations.filter((loc) => !loc.isServiceManaged);
|
||||
|
||||
if (hasPrivateLocation.length > 0) {
|
||||
await syntheticsMonitorClient.privateLocationAPI.checkPermissions(
|
||||
request,
|
||||
deletePermissionError(monitor.attributes.name)
|
||||
);
|
||||
}
|
||||
let deletePromise;
|
||||
|
||||
try {
|
||||
const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID;
|
||||
deletePromise = savedObjectsClient.delete(syntheticsMonitorType, monitorId);
|
||||
|
||||
const deleteSyncPromise = syntheticsMonitorClient.deleteMonitors(
|
||||
[
|
||||
{
|
||||
|
@ -105,7 +97,6 @@ export const deleteMonitor = async ({
|
|||
savedObjectsClient,
|
||||
spaceId
|
||||
);
|
||||
const deletePromise = savedObjectsClient.delete(syntheticsMonitorType, monitorId);
|
||||
|
||||
const [errors] = await Promise.all([deleteSyncPromise, deletePromise]).catch((e) => {
|
||||
server.logger.error(e);
|
||||
|
@ -126,6 +117,9 @@ export const deleteMonitor = async ({
|
|||
|
||||
return errors;
|
||||
} catch (e) {
|
||||
if (deletePromise) {
|
||||
await deletePromise;
|
||||
}
|
||||
server.logger.error(
|
||||
`Unable to delete Synthetics monitor ${monitor.attributes[ConfigKey.NAME]}`
|
||||
);
|
||||
|
|
|
@ -11,7 +11,6 @@ import { ConfigKey } from '../../../common/runtime_types';
|
|||
import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes/types';
|
||||
import { SYNTHETICS_API_URLS } from '../../../common/constants';
|
||||
import { getMonitors, getKqlFilter } from '../common';
|
||||
import { INSUFFICIENT_FLEET_PERMISSIONS } from '../../synthetics_service/project_monitor/project_monitor_formatter';
|
||||
import { deleteMonitorBulk } from './bulk_cruds/delete_monitor_bulk';
|
||||
|
||||
export const deleteSyntheticsMonitorProjectRoute: SyntheticsRestApiRouteFactory = () => ({
|
||||
|
@ -56,22 +55,6 @@ export const deleteSyntheticsMonitorProjectRoute: SyntheticsRestApiRouteFactory
|
|||
{ fields: [] }
|
||||
);
|
||||
|
||||
const {
|
||||
integrations: { writeIntegrationPolicies },
|
||||
} = await server.fleet.authz.fromRequest(request);
|
||||
|
||||
const hasPrivateMonitor = monitors.some((monitor) =>
|
||||
monitor.attributes.locations.some((location) => !location.isServiceManaged)
|
||||
);
|
||||
|
||||
if (!writeIntegrationPolicies && hasPrivateMonitor) {
|
||||
return response.forbidden({
|
||||
body: {
|
||||
message: INSUFFICIENT_FLEET_PERMISSIONS,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
await deleteMonitorBulk({
|
||||
monitors,
|
||||
server,
|
||||
|
|
|
@ -42,11 +42,6 @@ describe('syncEditedMonitor', () => {
|
|||
},
|
||||
},
|
||||
fleet: {
|
||||
authz: {
|
||||
fromRequest: jest
|
||||
.fn()
|
||||
.mockReturnValue({ integrations: { writeIntegrationPolicies: true } }),
|
||||
},
|
||||
packagePolicyService: {
|
||||
get: jest.fn().mockReturnValue({}),
|
||||
getByIDs: jest.fn().mockReturnValue([]),
|
||||
|
|
|
@ -95,7 +95,6 @@ export const editSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => (
|
|||
normalizedMonitor: monitorWithRevision,
|
||||
spaceId,
|
||||
});
|
||||
|
||||
if (failedPolicyUpdates && failedPolicyUpdates.length > 0) {
|
||||
const hasError = failedPolicyUpdates.find((update) => update.error);
|
||||
await rollbackUpdate({
|
||||
|
|
|
@ -16,7 +16,7 @@ export const getAPIKeySyntheticsRoute: SyntheticsRestApiRouteFactory = (libs) =>
|
|||
const apiKey = await generateAPIKey({
|
||||
request,
|
||||
server,
|
||||
uptimePrivileges: true,
|
||||
projectAPIKey: true,
|
||||
});
|
||||
|
||||
return { apiKey };
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { SyntheticsRestApiRouteFactory } from '../../../legacy_uptime/routes';
|
||||
import { SYNTHETICS_API_URLS } from '../../../../common/constants';
|
||||
|
||||
export const getAgentPoliciesRoute: SyntheticsRestApiRouteFactory = () => ({
|
||||
method: 'GET',
|
||||
path: SYNTHETICS_API_URLS.AGENT_POLICIES,
|
||||
validate: {},
|
||||
handler: async ({ server, context, uptimeEsClient }): Promise<any> => {
|
||||
const soClient = server.coreStart.savedObjects.createInternalRepository();
|
||||
const esClient = server.coreStart.elasticsearch.client.asInternalUser;
|
||||
|
||||
return server.fleet?.agentPolicyService.list(soClient, {
|
||||
page: 1,
|
||||
perPage: 10000,
|
||||
sortField: 'name',
|
||||
sortOrder: 'asc',
|
||||
kuery: 'ingest-agent-policies.is_managed : false',
|
||||
esClient,
|
||||
});
|
||||
},
|
||||
});
|
|
@ -79,11 +79,11 @@ export const getAPIKeyForSyntheticsService = async ({
|
|||
export const generateAPIKey = async ({
|
||||
server,
|
||||
request,
|
||||
uptimePrivileges = false,
|
||||
projectAPIKey = false,
|
||||
}: {
|
||||
server: UptimeServerSetup;
|
||||
request: KibanaRequest;
|
||||
uptimePrivileges?: boolean;
|
||||
projectAPIKey?: boolean;
|
||||
}) => {
|
||||
const { security } = server;
|
||||
const isApiKeysEnabled = await security.authc.apiKeys?.areAPIKeysEnabled();
|
||||
|
@ -92,7 +92,7 @@ export const generateAPIKey = async ({
|
|||
throw new Error('Please enable API keys in kibana to use synthetics service.');
|
||||
}
|
||||
|
||||
if (uptimePrivileges) {
|
||||
if (projectAPIKey) {
|
||||
/* Exposed to the user. Must create directly with the user */
|
||||
return security.authc.apiKeys?.create(request, {
|
||||
name: 'synthetics-api-key (required for project monitors)',
|
||||
|
@ -105,8 +105,6 @@ export const generateAPIKey = async ({
|
|||
spaces: [ALL_SPACES_ID],
|
||||
feature: {
|
||||
uptime: ['all'],
|
||||
fleet: ['all'],
|
||||
fleetv2: ['all'],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
import { SyntheticsPrivateLocation } from './synthetics_private_location';
|
||||
import { testMonitorPolicy } from './test_policy';
|
||||
import { formatSyntheticsPolicy } from '../formatters/private_formatters/format_synthetics_policy';
|
||||
import { savedObjectsServiceMock } from '@kbn/core-saved-objects-server-mocks';
|
||||
|
||||
describe('SyntheticsPrivateLocation', () => {
|
||||
const mockPrivateLocation: PrivateLocation = {
|
||||
|
@ -52,15 +53,6 @@ describe('SyntheticsPrivateLocation', () => {
|
|||
username: '',
|
||||
} as unknown as HeartbeatConfig;
|
||||
|
||||
const savedObjectsClientMock = {
|
||||
bulkUpdate: jest.fn(),
|
||||
get: jest.fn().mockReturnValue({
|
||||
attributes: {
|
||||
locations: [mockPrivateLocation],
|
||||
},
|
||||
}),
|
||||
} as unknown as SavedObjectsClientContract;
|
||||
|
||||
const serverMock: UptimeServerSetup = {
|
||||
uptimeEsClient: { search: jest.fn() },
|
||||
logger: loggerMock.create(),
|
||||
|
@ -72,11 +64,6 @@ describe('SyntheticsPrivateLocation', () => {
|
|||
},
|
||||
},
|
||||
fleet: {
|
||||
authz: {
|
||||
fromRequest: jest
|
||||
.fn()
|
||||
.mockReturnValue({ integrations: { writeIntegrationPolicies: true } }),
|
||||
},
|
||||
packagePolicyService: {
|
||||
get: jest.fn().mockReturnValue({}),
|
||||
buildPackagePolicyFromPackage: jest.fn(),
|
||||
|
@ -87,84 +74,79 @@ describe('SyntheticsPrivateLocation', () => {
|
|||
getSpaceId: jest.fn().mockReturnValue('nonDefaultSpace'),
|
||||
},
|
||||
},
|
||||
coreStart: {
|
||||
savedObjects: savedObjectsServiceMock.createStartContract(),
|
||||
},
|
||||
} as unknown as UptimeServerSetup;
|
||||
|
||||
it.each([
|
||||
[true, 'Unable to create Synthetics package policy template for private location'],
|
||||
[
|
||||
false,
|
||||
'Unable to create Synthetics package policy for monitor. Fleet write permissions are needed to use Synthetics private locations.',
|
||||
],
|
||||
])('throws errors for create monitor', async (writeIntegrationPolicies, error) => {
|
||||
const syntheticsPrivateLocation = new SyntheticsPrivateLocation({
|
||||
...serverMock,
|
||||
fleet: {
|
||||
...serverMock.fleet,
|
||||
authz: {
|
||||
fromRequest: jest.fn().mockReturnValue({ integrations: { writeIntegrationPolicies } }),
|
||||
it.each([['Unable to create Synthetics package policy template for private location']])(
|
||||
'throws errors for create monitor',
|
||||
async (error) => {
|
||||
const syntheticsPrivateLocation = new SyntheticsPrivateLocation({
|
||||
...serverMock,
|
||||
fleet: {
|
||||
...serverMock.fleet,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
await syntheticsPrivateLocation.createPackagePolicies(
|
||||
[{ config: testConfig, globalParams: {} }],
|
||||
{} as unknown as KibanaRequest,
|
||||
savedObjectsClientMock,
|
||||
[mockPrivateLocation],
|
||||
'test-space'
|
||||
);
|
||||
} catch (e) {
|
||||
expect(e).toEqual(new Error(error));
|
||||
try {
|
||||
await syntheticsPrivateLocation.createPackagePolicies(
|
||||
[{ config: testConfig, globalParams: {} }],
|
||||
{} as unknown as KibanaRequest,
|
||||
[mockPrivateLocation],
|
||||
'test-space'
|
||||
);
|
||||
} catch (e) {
|
||||
expect(e).toEqual(new Error(error));
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
it.each([
|
||||
[true, 'Unable to create Synthetics package policy template for private location'],
|
||||
[
|
||||
false,
|
||||
'Unable to update Synthetics package policy for monitor. Fleet write permissions are needed to use Synthetics private locations.',
|
||||
],
|
||||
])('throws errors for edit monitor', async (writeIntegrationPolicies, error) => {
|
||||
const syntheticsPrivateLocation = new SyntheticsPrivateLocation({
|
||||
...serverMock,
|
||||
fleet: {
|
||||
...serverMock.fleet,
|
||||
authz: {
|
||||
fromRequest: jest.fn().mockReturnValue({ integrations: { writeIntegrationPolicies } }),
|
||||
it.each([['Unable to create Synthetics package policy template for private location']])(
|
||||
'throws errors for edit monitor',
|
||||
async (error) => {
|
||||
const syntheticsPrivateLocation = new SyntheticsPrivateLocation({
|
||||
...serverMock,
|
||||
fleet: {
|
||||
...serverMock.fleet,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
await syntheticsPrivateLocation.editMonitors(
|
||||
[{ config: testConfig, globalParams: {} }],
|
||||
{} as unknown as KibanaRequest,
|
||||
savedObjectsClientMock,
|
||||
[mockPrivateLocation],
|
||||
'test-space'
|
||||
);
|
||||
} catch (e) {
|
||||
expect(e).toEqual(new Error(error));
|
||||
try {
|
||||
await syntheticsPrivateLocation.editMonitors(
|
||||
[{ config: testConfig, globalParams: {} }],
|
||||
{} as unknown as KibanaRequest,
|
||||
[mockPrivateLocation],
|
||||
'test-space'
|
||||
);
|
||||
} catch (e) {
|
||||
expect(e).toEqual(new Error(error));
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
it.each([
|
||||
[
|
||||
true,
|
||||
'Unable to delete Synthetics package policy for monitor Test Monitor with private location Test Location',
|
||||
],
|
||||
[
|
||||
false,
|
||||
'Unable to delete Synthetics package policy for monitor Test Monitor. Fleet write permissions are needed to use Synthetics private locations.',
|
||||
],
|
||||
])('throws errors for delete monitor', async (writeIntegrationPolicies, error) => {
|
||||
])('throws errors for delete monitor', async (error) => {
|
||||
const syntheticsPrivateLocation = new SyntheticsPrivateLocation({
|
||||
...serverMock,
|
||||
fleet: {
|
||||
...serverMock.fleet,
|
||||
authz: {
|
||||
fromRequest: jest.fn().mockReturnValue({ integrations: { writeIntegrationPolicies } }),
|
||||
packagePolicyService: {
|
||||
...serverMock.fleet.packagePolicyService,
|
||||
delete(
|
||||
soClient: SavedObjectsClientContract,
|
||||
esClient: any,
|
||||
ids: string[],
|
||||
options?: any
|
||||
): any {
|
||||
throw new Error(error);
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -172,7 +154,6 @@ describe('SyntheticsPrivateLocation', () => {
|
|||
await syntheticsPrivateLocation.deleteMonitors(
|
||||
[testConfig],
|
||||
{} as unknown as KibanaRequest,
|
||||
savedObjectsClientMock,
|
||||
'test-space'
|
||||
);
|
||||
} catch (e) {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { KibanaRequest, SavedObjectsClientContract } from '@kbn/core/server';
|
||||
import { KibanaRequest } from '@kbn/core/server';
|
||||
import { NewPackagePolicy } from '@kbn/fleet-plugin/common';
|
||||
import { NewPackagePolicyWithId } from '@kbn/fleet-plugin/server/services/package_policy';
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
@ -38,9 +38,11 @@ export class SyntheticsPrivateLocation {
|
|||
this.server = _server;
|
||||
}
|
||||
|
||||
async buildNewPolicy(savedObjectsClient: SavedObjectsClientContract): Promise<NewPackagePolicy> {
|
||||
async buildNewPolicy(): Promise<NewPackagePolicy> {
|
||||
const soClient = this.server.coreStart.savedObjects.createInternalRepository();
|
||||
|
||||
const newPolicy = await this.server.fleet.packagePolicyService.buildPackagePolicyFromPackage(
|
||||
savedObjectsClient,
|
||||
soClient,
|
||||
'synthetics',
|
||||
this.server.logger
|
||||
);
|
||||
|
@ -62,15 +64,10 @@ export class SyntheticsPrivateLocation {
|
|||
generateNewPolicy(
|
||||
config: HeartbeatConfig,
|
||||
privateLocation: PrivateLocation,
|
||||
savedObjectsClient: SavedObjectsClientContract,
|
||||
newPolicyTemplate: NewPackagePolicy,
|
||||
spaceId: string,
|
||||
globalParams: Record<string, string>
|
||||
): (NewPackagePolicy & { policy_id: string }) | null {
|
||||
if (!savedObjectsClient) {
|
||||
throw new Error('Could not find savedObjectsClient');
|
||||
}
|
||||
|
||||
const { label: locName } = privateLocation;
|
||||
|
||||
const newPolicy = cloneDeep(newPolicyTemplate);
|
||||
|
@ -106,34 +103,19 @@ export class SyntheticsPrivateLocation {
|
|||
}
|
||||
}
|
||||
|
||||
async checkPermissions(request: KibanaRequest, error: string) {
|
||||
const {
|
||||
integrations: { writeIntegrationPolicies },
|
||||
} = await this.server.fleet.authz.fromRequest(request);
|
||||
|
||||
if (!writeIntegrationPolicies) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async createPackagePolicies(
|
||||
configs: PrivateConfig[],
|
||||
request: KibanaRequest,
|
||||
savedObjectsClient: SavedObjectsClientContract,
|
||||
privateLocations: PrivateLocation[],
|
||||
spaceId: string
|
||||
) {
|
||||
if (configs.length === 0) {
|
||||
return { created: [], failed: [] };
|
||||
}
|
||||
await this.checkPermissions(
|
||||
request,
|
||||
`Unable to create Synthetics package policy for monitor. Fleet write permissions are needed to use Synthetics private locations.`
|
||||
);
|
||||
|
||||
const newPolicies: NewPackagePolicyWithId[] = [];
|
||||
|
||||
const newPolicyTemplate = await this.buildNewPolicy(savedObjectsClient);
|
||||
const newPolicyTemplate = await this.buildNewPolicy();
|
||||
|
||||
for (const { config, globalParams } of configs) {
|
||||
try {
|
||||
|
@ -153,7 +135,6 @@ export class SyntheticsPrivateLocation {
|
|||
const newPolicy = this.generateNewPolicy(
|
||||
config,
|
||||
location,
|
||||
savedObjectsClient,
|
||||
newPolicyTemplate,
|
||||
spaceId,
|
||||
globalParams
|
||||
|
@ -181,7 +162,7 @@ export class SyntheticsPrivateLocation {
|
|||
}
|
||||
|
||||
try {
|
||||
return await this.createPolicyBulk(newPolicies, savedObjectsClient);
|
||||
return await this.createPolicyBulk(newPolicies);
|
||||
} catch (e) {
|
||||
this.server.logger.error(e);
|
||||
throw e;
|
||||
|
@ -190,19 +171,18 @@ export class SyntheticsPrivateLocation {
|
|||
|
||||
async inspectPackagePolicy({
|
||||
privateConfig,
|
||||
savedObjectsClient,
|
||||
spaceId,
|
||||
allPrivateLocations,
|
||||
}: {
|
||||
privateConfig?: PrivateConfig;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
allPrivateLocations: PrivateLocation[];
|
||||
spaceId: string;
|
||||
}) {
|
||||
if (!privateConfig) {
|
||||
return null;
|
||||
}
|
||||
const newPolicyTemplate = await this.buildNewPolicy(savedObjectsClient);
|
||||
const newPolicyTemplate = await this.buildNewPolicy();
|
||||
const soClient = this.server.coreStart.savedObjects.createInternalRepository();
|
||||
|
||||
const { config, globalParams } = privateConfig;
|
||||
try {
|
||||
|
@ -215,7 +195,6 @@ export class SyntheticsPrivateLocation {
|
|||
const newPolicy = this.generateNewPolicy(
|
||||
config,
|
||||
location,
|
||||
savedObjectsClient,
|
||||
newPolicyTemplate,
|
||||
spaceId,
|
||||
globalParams
|
||||
|
@ -226,7 +205,7 @@ export class SyntheticsPrivateLocation {
|
|||
id: this.getPolicyId(config, location.id, spaceId),
|
||||
} as NewPackagePolicyWithId;
|
||||
|
||||
return await this.server.fleet.packagePolicyService.inspect(savedObjectsClient, pkgPolicy);
|
||||
return await this.server.fleet.packagePolicyService.inspect(soClient, pkgPolicy);
|
||||
} catch (e) {
|
||||
this.server.logger.error(e);
|
||||
return null;
|
||||
|
@ -236,7 +215,6 @@ export class SyntheticsPrivateLocation {
|
|||
async editMonitors(
|
||||
configs: Array<{ config: HeartbeatConfig; globalParams: Record<string, string> }>,
|
||||
request: KibanaRequest,
|
||||
savedObjectsClient: SavedObjectsClientContract,
|
||||
allPrivateLocations: PrivateLocation[],
|
||||
spaceId: string
|
||||
) {
|
||||
|
@ -244,12 +222,7 @@ export class SyntheticsPrivateLocation {
|
|||
return {};
|
||||
}
|
||||
|
||||
await this.checkPermissions(
|
||||
request,
|
||||
`Unable to update Synthetics package policy for monitor. Fleet write permissions are needed to use Synthetics private locations.`
|
||||
);
|
||||
|
||||
const newPolicyTemplate = await this.buildNewPolicy(savedObjectsClient);
|
||||
const newPolicyTemplate = await this.buildNewPolicy();
|
||||
|
||||
const policiesToUpdate: NewPackagePolicyWithId[] = [];
|
||||
const policiesToCreate: NewPackagePolicyWithId[] = [];
|
||||
|
@ -258,7 +231,6 @@ export class SyntheticsPrivateLocation {
|
|||
const existingPolicies = await this.getExistingPolicies(
|
||||
configs.map(({ config }) => config),
|
||||
allPrivateLocations,
|
||||
savedObjectsClient,
|
||||
spaceId
|
||||
);
|
||||
|
||||
|
@ -276,7 +248,6 @@ export class SyntheticsPrivateLocation {
|
|||
const newPolicy = this.generateNewPolicy(
|
||||
config,
|
||||
privateLocation,
|
||||
savedObjectsClient,
|
||||
newPolicyTemplate,
|
||||
spaceId,
|
||||
globalParams
|
||||
|
@ -302,9 +273,9 @@ export class SyntheticsPrivateLocation {
|
|||
}
|
||||
|
||||
const [_createResponse, failedUpdatesRes, _deleteResponse] = await Promise.all([
|
||||
this.createPolicyBulk(policiesToCreate, savedObjectsClient),
|
||||
this.updatePolicyBulk(policiesToUpdate, savedObjectsClient),
|
||||
this.deletePolicyBulk(policiesToDelete, savedObjectsClient),
|
||||
this.createPolicyBulk(policiesToCreate),
|
||||
this.updatePolicyBulk(policiesToUpdate),
|
||||
this.deletePolicyBulk(policiesToDelete),
|
||||
]);
|
||||
|
||||
const failedUpdates = failedUpdatesRes?.map(({ packagePolicy, error }) => {
|
||||
|
@ -332,9 +303,10 @@ export class SyntheticsPrivateLocation {
|
|||
async getExistingPolicies(
|
||||
configs: HeartbeatConfig[],
|
||||
allPrivateLocations: PrivateLocation[],
|
||||
savedObjectsClient: SavedObjectsClientContract,
|
||||
spaceId: string
|
||||
) {
|
||||
const soClient = this.server.coreStart.savedObjects.createInternalRepository();
|
||||
|
||||
const listOfPolicies: string[] = [];
|
||||
for (const config of configs) {
|
||||
for (const privateLocation of allPrivateLocations) {
|
||||
|
@ -343,19 +315,16 @@ export class SyntheticsPrivateLocation {
|
|||
}
|
||||
}
|
||||
return (
|
||||
(await this.server.fleet.packagePolicyService.getByIDs(savedObjectsClient, listOfPolicies, {
|
||||
(await this.server.fleet.packagePolicyService.getByIDs(soClient, listOfPolicies, {
|
||||
ignoreMissing: true,
|
||||
})) ?? []
|
||||
);
|
||||
}
|
||||
|
||||
async createPolicyBulk(
|
||||
newPolicies: NewPackagePolicyWithId[],
|
||||
savedObjectsClient: SavedObjectsClientContract
|
||||
) {
|
||||
const soClient = savedObjectsClient;
|
||||
async createPolicyBulk(newPolicies: NewPackagePolicyWithId[]) {
|
||||
const soClient = this.server.coreStart.savedObjects.createInternalRepository();
|
||||
const esClient = this.server.uptimeEsClient.baseESClient;
|
||||
if (soClient && esClient && newPolicies.length > 0) {
|
||||
if (esClient && newPolicies.length > 0) {
|
||||
return await this.server.fleet.packagePolicyService.bulkCreate(
|
||||
soClient,
|
||||
esClient,
|
||||
|
@ -364,11 +333,8 @@ export class SyntheticsPrivateLocation {
|
|||
}
|
||||
}
|
||||
|
||||
async updatePolicyBulk(
|
||||
policiesToUpdate: NewPackagePolicyWithId[],
|
||||
savedObjectsClient: SavedObjectsClientContract
|
||||
) {
|
||||
const soClient = savedObjectsClient;
|
||||
async updatePolicyBulk(policiesToUpdate: NewPackagePolicyWithId[]) {
|
||||
const soClient = this.server.coreStart.savedObjects.createInternalRepository();
|
||||
const esClient = this.server.uptimeEsClient.baseESClient;
|
||||
if (soClient && esClient && policiesToUpdate.length > 0) {
|
||||
const { failedPolicies } = await this.server.fleet.packagePolicyService.bulkUpdate(
|
||||
|
@ -383,11 +349,8 @@ export class SyntheticsPrivateLocation {
|
|||
}
|
||||
}
|
||||
|
||||
async deletePolicyBulk(
|
||||
policyIdsToDelete: string[],
|
||||
savedObjectsClient: SavedObjectsClientContract
|
||||
) {
|
||||
const soClient = savedObjectsClient;
|
||||
async deletePolicyBulk(policyIdsToDelete: string[]) {
|
||||
const soClient = this.server.coreStart.savedObjects.createInternalRepository();
|
||||
const esClient = this.server.uptimeEsClient.baseESClient;
|
||||
if (soClient && esClient && policyIdsToDelete.length > 0) {
|
||||
return await this.server.fleet.packagePolicyService.delete(
|
||||
|
@ -401,12 +364,10 @@ export class SyntheticsPrivateLocation {
|
|||
}
|
||||
}
|
||||
|
||||
async deleteMonitors(
|
||||
configs: HeartbeatConfig[],
|
||||
request: KibanaRequest,
|
||||
soClient: SavedObjectsClientContract,
|
||||
spaceId: string
|
||||
) {
|
||||
async deleteMonitors(configs: HeartbeatConfig[], request: KibanaRequest, spaceId: string) {
|
||||
const soClient = this.server.coreStart.savedObjects.createInternalRepository();
|
||||
const esClient = this.server.uptimeEsClient.baseESClient;
|
||||
|
||||
const policyIdsToDelete = [];
|
||||
for (const config of configs) {
|
||||
const { locations } = config;
|
||||
|
@ -414,7 +375,6 @@ export class SyntheticsPrivateLocation {
|
|||
const monitorPrivateLocations = locations.filter((loc) => !loc.isServiceManaged);
|
||||
|
||||
for (const privateLocation of monitorPrivateLocations) {
|
||||
await this.checkPermissions(request, deletePermissionError(config[ConfigKey.NAME]));
|
||||
try {
|
||||
policyIdsToDelete.push(this.getPolicyId(config, privateLocation.id, spaceId));
|
||||
} catch (e) {
|
||||
|
@ -424,8 +384,19 @@ export class SyntheticsPrivateLocation {
|
|||
}
|
||||
}
|
||||
if (policyIdsToDelete.length > 0) {
|
||||
await this.checkPermissions(request, deletePermissionError());
|
||||
await this.deletePolicyBulk(policyIdsToDelete, soClient);
|
||||
const result = await this.server.fleet.packagePolicyService.delete(
|
||||
soClient,
|
||||
esClient,
|
||||
policyIdsToDelete,
|
||||
{
|
||||
force: true,
|
||||
}
|
||||
);
|
||||
const failedPolicies = result?.filter((policy) => !policy.success);
|
||||
if (failedPolicies?.length === policyIdsToDelete.length) {
|
||||
throw new Error(deletePolicyError(configs[0][ConfigKey.NAME]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -450,10 +421,6 @@ const throwAddEditError = (hasPolicy: boolean, location?: string, name?: string)
|
|||
);
|
||||
};
|
||||
|
||||
export const deletePermissionError = (name?: string) => {
|
||||
return `Unable to delete Synthetics package policy for monitor ${name}. Fleet write permissions are needed to use Synthetics private locations.`;
|
||||
};
|
||||
|
||||
const deletePolicyError = (name: string, location?: string) => {
|
||||
return `Unable to delete Synthetics package policy for monitor ${name} with private location ${location}`;
|
||||
};
|
||||
|
|
|
@ -5,11 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import { loggerMock } from '@kbn/logging-mocks';
|
||||
import { savedObjectsClientMock } from '@kbn/core/server/mocks';
|
||||
import {
|
||||
INSUFFICIENT_FLEET_PERMISSIONS,
|
||||
ProjectMonitorFormatter,
|
||||
} from './project_monitor_formatter';
|
||||
import { savedObjectsClientMock, savedObjectsServiceMock } from '@kbn/core/server/mocks';
|
||||
import { ProjectMonitorFormatter } from './project_monitor_formatter';
|
||||
import {
|
||||
ConfigKey,
|
||||
DataStream,
|
||||
|
@ -123,6 +120,9 @@ describe('ProjectMonitorFormatter', () => {
|
|||
},
|
||||
},
|
||||
encryptedSavedObjects: mockEncryptedSO(),
|
||||
coreStart: {
|
||||
savedObjects: savedObjectsServiceMock.createStartContract(),
|
||||
},
|
||||
} as unknown as UptimeServerSetup;
|
||||
|
||||
const syntheticsService = new SyntheticsService(serverMock);
|
||||
|
@ -164,13 +164,14 @@ describe('ProjectMonitorFormatter', () => {
|
|||
} as any)
|
||||
);
|
||||
|
||||
it('should return errors', async () => {
|
||||
it('should return validation errors errors', async () => {
|
||||
const pushMonitorFormatter = new ProjectMonitorFormatter({
|
||||
projectId: 'test-project',
|
||||
spaceId: 'default-space',
|
||||
// @ts-ignore
|
||||
spaceId: 5,
|
||||
routeContext,
|
||||
encryptedSavedObjectsClient,
|
||||
monitors: testMonitors,
|
||||
monitors: [testMonitors[0]],
|
||||
});
|
||||
|
||||
pushMonitorFormatter.getProjectMonitorsForProject = jest.fn().mockResolvedValue([]);
|
||||
|
@ -185,76 +186,17 @@ describe('ProjectMonitorFormatter', () => {
|
|||
createdMonitors: [],
|
||||
failedMonitors: [
|
||||
{
|
||||
details: "Cannot read properties of undefined (reading 'authz')",
|
||||
details: 'spaceId.replace is not a function',
|
||||
id: 'check if title is present 10 0',
|
||||
payload: testMonitors[0],
|
||||
reason: 'Failed to create or update monitor',
|
||||
},
|
||||
{
|
||||
details: "Cannot read properties of undefined (reading 'authz')",
|
||||
id: 'check if title is present 10 1',
|
||||
payload: testMonitors[1],
|
||||
reason: 'Failed to create or update monitor',
|
||||
},
|
||||
],
|
||||
updatedMonitors: [],
|
||||
});
|
||||
});
|
||||
|
||||
it('throws fleet permission error', async () => {
|
||||
serverMock.fleet = {
|
||||
authz: {
|
||||
fromRequest: jest
|
||||
.fn()
|
||||
.mockResolvedValue({ integrations: { writeIntegrationPolicies: false } }),
|
||||
},
|
||||
} as any;
|
||||
|
||||
const pushMonitorFormatter = new ProjectMonitorFormatter({
|
||||
projectId: 'test-project',
|
||||
spaceId: 'default-space',
|
||||
encryptedSavedObjectsClient,
|
||||
routeContext,
|
||||
monitors: testMonitors,
|
||||
});
|
||||
|
||||
pushMonitorFormatter.getProjectMonitorsForProject = jest.fn().mockResolvedValue([]);
|
||||
|
||||
await pushMonitorFormatter.configureAllProjectMonitors();
|
||||
|
||||
expect({
|
||||
createdMonitors: pushMonitorFormatter.createdMonitors,
|
||||
updatedMonitors: pushMonitorFormatter.updatedMonitors,
|
||||
failedMonitors: pushMonitorFormatter.failedMonitors,
|
||||
}).toStrictEqual({
|
||||
createdMonitors: [],
|
||||
failedMonitors: [
|
||||
{
|
||||
details: INSUFFICIENT_FLEET_PERMISSIONS,
|
||||
id: 'check if title is present 10 0',
|
||||
payload: testMonitors[0],
|
||||
reason: 'Failed to create or update monitor',
|
||||
},
|
||||
{
|
||||
details: INSUFFICIENT_FLEET_PERMISSIONS,
|
||||
id: 'check if title is present 10 1',
|
||||
payload: testMonitors[1],
|
||||
reason: 'Failed to create or update monitor',
|
||||
},
|
||||
],
|
||||
updatedMonitors: [],
|
||||
});
|
||||
});
|
||||
|
||||
it('catches errors from bulk edit method', async () => {
|
||||
serverMock.fleet = {
|
||||
authz: {
|
||||
fromRequest: jest
|
||||
.fn()
|
||||
.mockResolvedValue({ integrations: { writeIntegrationPolicies: true } }),
|
||||
},
|
||||
} as any;
|
||||
|
||||
soClient.bulkCreate.mockImplementation(async () => {
|
||||
return {
|
||||
saved_objects: [],
|
||||
|
@ -282,7 +224,7 @@ describe('ProjectMonitorFormatter', () => {
|
|||
updatedMonitors: [],
|
||||
failedMonitors: [
|
||||
{
|
||||
details: "Cannot read properties of undefined (reading 'buildPackagePolicyFromPackage')",
|
||||
details: "Cannot read properties of undefined (reading 'packagePolicyService')",
|
||||
payload: payloadData,
|
||||
reason: 'Failed to create 2 monitors',
|
||||
},
|
||||
|
@ -291,14 +233,6 @@ describe('ProjectMonitorFormatter', () => {
|
|||
});
|
||||
|
||||
it('configures project monitors when there are errors', async () => {
|
||||
serverMock.fleet = {
|
||||
authz: {
|
||||
fromRequest: jest
|
||||
.fn()
|
||||
.mockResolvedValue({ integrations: { writeIntegrationPolicies: true } }),
|
||||
},
|
||||
} as any;
|
||||
|
||||
soClient.bulkCreate = jest.fn().mockResolvedValue({ saved_objects: [] });
|
||||
|
||||
const pushMonitorFormatter = new ProjectMonitorFormatter({
|
||||
|
@ -322,7 +256,7 @@ describe('ProjectMonitorFormatter', () => {
|
|||
updatedMonitors: [],
|
||||
failedMonitors: [
|
||||
{
|
||||
details: "Cannot read properties of undefined (reading 'buildPackagePolicyFromPackage')",
|
||||
details: "Cannot read properties of undefined (reading 'packagePolicyService')",
|
||||
payload: payloadData,
|
||||
reason: 'Failed to create 2 monitors',
|
||||
},
|
||||
|
@ -331,15 +265,6 @@ describe('ProjectMonitorFormatter', () => {
|
|||
});
|
||||
|
||||
it('shows errors thrown by fleet api', async () => {
|
||||
serverMock.fleet = {
|
||||
authz: {
|
||||
fromRequest: jest
|
||||
.fn()
|
||||
.mockResolvedValue({ integrations: { writeIntegrationPolicies: true } }),
|
||||
},
|
||||
packagePolicyService: {},
|
||||
} as any;
|
||||
|
||||
soClient.bulkCreate = jest.fn().mockResolvedValue({ saved_objects: soResult });
|
||||
|
||||
const pushMonitorFormatter = new ProjectMonitorFormatter({
|
||||
|
@ -363,8 +288,7 @@ describe('ProjectMonitorFormatter', () => {
|
|||
updatedMonitors: [],
|
||||
failedMonitors: [
|
||||
{
|
||||
details:
|
||||
'this.server.fleet.packagePolicyService.buildPackagePolicyFromPackage is not a function',
|
||||
details: "Cannot read properties of undefined (reading 'packagePolicyService')",
|
||||
reason: 'Failed to create 2 monitors',
|
||||
payload: payloadData,
|
||||
},
|
||||
|
@ -373,14 +297,6 @@ describe('ProjectMonitorFormatter', () => {
|
|||
});
|
||||
|
||||
it('creates project monitors when no errors', async () => {
|
||||
serverMock.fleet = {
|
||||
authz: {
|
||||
fromRequest: jest
|
||||
.fn()
|
||||
.mockResolvedValue({ integrations: { writeIntegrationPolicies: true } }),
|
||||
},
|
||||
} as any;
|
||||
|
||||
soClient.bulkCreate = jest.fn().mockResolvedValue({ saved_objects: soResult });
|
||||
|
||||
monitorClient.addMonitors = jest.fn().mockReturnValue([]);
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { KibanaRequest } from '@kbn/core/server';
|
||||
import pMap from 'p-map';
|
||||
import {
|
||||
SavedObjectsUpdateResponse,
|
||||
|
@ -41,14 +40,6 @@ import { normalizeProjectMonitor } from './normalizers';
|
|||
|
||||
type FailedError = Array<{ id?: string; reason: string; details: string; payload?: object }>;
|
||||
|
||||
export const INSUFFICIENT_FLEET_PERMISSIONS = i18n.translate(
|
||||
'xpack.synthetics.service.projectMonitors.insufficientFleetPermissions',
|
||||
{
|
||||
defaultMessage:
|
||||
'Insufficient permissions. In order to configure private locations, you must have Fleet and Integrations write permissions. To resolve, please generate a new API key with a user who has Fleet and Integrations write permissions.',
|
||||
}
|
||||
);
|
||||
|
||||
export const CANNOT_UPDATE_MONITOR_TO_DIFFERENT_TYPE = i18n.translate(
|
||||
'xpack.synthetics.service.projectMonitors.cannotUpdateMonitorToDifferentType',
|
||||
{
|
||||
|
@ -77,11 +68,8 @@ export class ProjectMonitorFormatter {
|
|||
private server: UptimeServerSetup;
|
||||
private projectFilter: string;
|
||||
private syntheticsMonitorClient: SyntheticsMonitorClient;
|
||||
private request: KibanaRequest;
|
||||
private routeContext: RouteContext;
|
||||
|
||||
private writeIntegrationPoliciesPermissions?: boolean;
|
||||
|
||||
constructor({
|
||||
encryptedSavedObjectsClient,
|
||||
projectId,
|
||||
|
@ -104,7 +92,6 @@ export class ProjectMonitorFormatter {
|
|||
this.monitors = monitors;
|
||||
this.server = routeContext.server;
|
||||
this.projectFilter = `${syntheticsMonitorType}.attributes.${ConfigKey.PROJECT_ID}: "${this.projectId}"`;
|
||||
this.request = routeContext.request;
|
||||
this.publicLocations = [];
|
||||
this.privateLocations = [];
|
||||
}
|
||||
|
@ -186,21 +173,6 @@ export class ProjectMonitorFormatter {
|
|||
]);
|
||||
};
|
||||
|
||||
validatePermissions = async ({ monitor }: { monitor: ProjectMonitor }) => {
|
||||
if (this.writeIntegrationPoliciesPermissions || (monitor.privateLocations ?? []).length === 0) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
integrations: { writeIntegrationPolicies },
|
||||
} = await this.server.fleet.authz.fromRequest(this.request);
|
||||
|
||||
this.writeIntegrationPoliciesPermissions = writeIntegrationPolicies;
|
||||
|
||||
if (!writeIntegrationPolicies) {
|
||||
throw new Error(INSUFFICIENT_FLEET_PERMISSIONS);
|
||||
}
|
||||
};
|
||||
|
||||
validateProjectMonitor = async ({
|
||||
monitor,
|
||||
publicLocations,
|
||||
|
@ -211,8 +183,6 @@ export class ProjectMonitorFormatter {
|
|||
privateLocations: PrivateLocation[];
|
||||
}) => {
|
||||
try {
|
||||
await this.validatePermissions({ monitor });
|
||||
|
||||
const { normalizedFields: normalizedMonitor, errors } = normalizeProjectMonitor({
|
||||
monitor,
|
||||
locations: this.publicLocations,
|
||||
|
|
|
@ -77,7 +77,6 @@ export class SyntheticsMonitorClient {
|
|||
const newPolicies = this.privateLocationAPI.createPackagePolicies(
|
||||
privateConfigs,
|
||||
request,
|
||||
savedObjectsClient,
|
||||
allPrivateLocations,
|
||||
spaceId
|
||||
);
|
||||
|
@ -98,7 +97,7 @@ export class SyntheticsMonitorClient {
|
|||
allPrivateLocations: PrivateLocation[],
|
||||
spaceId: string
|
||||
) {
|
||||
const { request, savedObjectsClient } = routeContext;
|
||||
const { request } = routeContext;
|
||||
const privateConfigs: Array<{ config: HeartbeatConfig; globalParams: Record<string, string> }> =
|
||||
[];
|
||||
|
||||
|
@ -146,7 +145,6 @@ export class SyntheticsMonitorClient {
|
|||
const privateEditPromise = this.privateLocationAPI.editMonitors(
|
||||
privateConfigs,
|
||||
request,
|
||||
savedObjectsClient,
|
||||
allPrivateLocations,
|
||||
spaceId
|
||||
);
|
||||
|
@ -168,12 +166,7 @@ export class SyntheticsMonitorClient {
|
|||
savedObjectsClient: SavedObjectsClientContract,
|
||||
spaceId: string
|
||||
) {
|
||||
const privateDeletePromise = this.privateLocationAPI.deleteMonitors(
|
||||
monitors,
|
||||
request,
|
||||
savedObjectsClient,
|
||||
spaceId
|
||||
);
|
||||
const privateDeletePromise = this.privateLocationAPI.deleteMonitors(monitors, request, spaceId);
|
||||
|
||||
const publicDeletePromise = this.syntheticsService.deleteConfigs(
|
||||
monitors.map((monitor) => ({ monitor, configId: monitor.config_id, params: {} }))
|
||||
|
@ -256,7 +249,6 @@ export class SyntheticsMonitorClient {
|
|||
await this.privateLocationAPI.editMonitors(
|
||||
privateConfigs,
|
||||
request,
|
||||
savedObjectsClient,
|
||||
allPrivateLocations,
|
||||
spaceId
|
||||
);
|
||||
|
@ -401,7 +393,6 @@ export class SyntheticsMonitorClient {
|
|||
);
|
||||
const privatePromise = this.privateLocationAPI.inspectPackagePolicy({
|
||||
privateConfig: privateConfigs?.[0],
|
||||
savedObjectsClient,
|
||||
allPrivateLocations,
|
||||
spaceId,
|
||||
});
|
||||
|
|
|
@ -83,6 +83,7 @@
|
|||
"@kbn/core-http-server",
|
||||
"@kbn/core-application-browser",
|
||||
"@kbn/std",
|
||||
"@kbn/core-saved-objects-server-mocks",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -36493,7 +36493,6 @@
|
|||
"xpack.synthetics.monitorManagement.callout.disabled": "La Gestion des moniteurs est actuellement désactivée",
|
||||
"xpack.synthetics.monitorManagement.callout.disabled.adminContact": "La Gestion des moniteurs sera activée lorsqu'un administrateur visitera l'application Synthetics.",
|
||||
"xpack.synthetics.monitorManagement.cancelLabel": "Annuler",
|
||||
"xpack.synthetics.monitorManagement.cannotSaveIntegration": "Vous n'êtes pas autorisé à gérer des emplacements privés. Cela nécessite le privilège Kibana \"Tous\" pour Fleet et Integrations.",
|
||||
"xpack.synthetics.monitorManagement.closeButtonLabel": "Fermer",
|
||||
"xpack.synthetics.monitorManagement.completed": "TERMINÉ",
|
||||
"xpack.synthetics.monitorManagement.configurations.label": "Configurations",
|
||||
|
@ -36554,7 +36553,6 @@
|
|||
"xpack.synthetics.monitorManagement.monitorSync.failure.statusLabel": "Statut",
|
||||
"xpack.synthetics.monitorManagement.monitorSync.failure.title": "Impossible de synchroniser les moniteurs avec le service Synthetics",
|
||||
"xpack.synthetics.monitorManagement.nameRequired": "Le nom de l’emplacement est requis",
|
||||
"xpack.synthetics.monitorManagement.noFleetPermission": "Vous n'êtes pas autorisé à effectuer cette action. Cela nécessite le privilège Kibana \"Tous\" pour Integrations.",
|
||||
"xpack.synthetics.monitorManagement.noSyntheticsPermissions": "Vous ne disposez pas d'autorisations suffisantes pour effectuer cette action.",
|
||||
"xpack.synthetics.monitorManagement.overviewTab.title": "Aperçu",
|
||||
"xpack.synthetics.monitorManagement.param.keyExists": "La clé existe déjà",
|
||||
|
@ -36567,7 +36565,6 @@
|
|||
"xpack.synthetics.monitorManagement.policyHost": "Politique d'agent",
|
||||
"xpack.synthetics.monitorManagement.privateLocations": "Emplacements privés",
|
||||
"xpack.synthetics.monitorManagement.privateLocations.needPermissions": "Il vous manque certains privilèges Kibana pour gérer les emplacements privés",
|
||||
"xpack.synthetics.monitorManagement.privateLocationsNotAllowedMessage": "Vous ne disposez pas d'autorisation pour ajouter des moniteurs dans des emplacements privés. Contactez votre administrateur pour demander des droits d'accès.",
|
||||
"xpack.synthetics.monitorManagement.priviledges.all": "Tous",
|
||||
"xpack.synthetics.monitorManagement.projectDelete.docsLink": "En savoir plus",
|
||||
"xpack.synthetics.monitorManagement.projectPush.label": "Commande push du projet",
|
||||
|
@ -36772,7 +36769,6 @@
|
|||
"xpack.synthetics.server.projectMonitors.locationEmptyError": "Vous devez ajouter au moins un emplacement ou un emplacement privé à ce moniteur.",
|
||||
"xpack.synthetics.service.projectMonitors.cannotUpdateMonitorToDifferentType": "Impossible de mettre à jour le moniteur avec un type différent.",
|
||||
"xpack.synthetics.service.projectMonitors.failedToUpdateMonitor": "Impossible de créer ou de mettre à jour le moniteur",
|
||||
"xpack.synthetics.service.projectMonitors.insufficientFleetPermissions": "Permissions insuffisantes. Pour configurer les emplacements privés, vous devez disposer d'autorisations d'écriture sur Fleet et sur les intégrations. Pour résoudre ce problème, veuillez générer une nouvelle clé d'API avec un utilisateur disposant des autorisations d'écriture sur Fleet et sur les intégrations.",
|
||||
"xpack.synthetics.settings.alertDefaultForm.requiredEmail": "À : L'e-mail est requis pour le connecteur d'e-mails sélectionné",
|
||||
"xpack.synthetics.settings.applyChanges": "Appliquer les modifications",
|
||||
"xpack.synthetics.settings.blank.error": "Ne peut pas être vide.",
|
||||
|
@ -39611,4 +39607,4 @@
|
|||
"xpack.painlessLab.title": "Painless Lab",
|
||||
"xpack.painlessLab.walkthroughButtonLabel": "Présentation"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36474,7 +36474,6 @@
|
|||
"xpack.synthetics.monitorManagement.callout.disabled": "モニター管理は現在無効です",
|
||||
"xpack.synthetics.monitorManagement.callout.disabled.adminContact": "管理者がSyntheticsアプリにアクセスすると、モニター管理が有効化されます。",
|
||||
"xpack.synthetics.monitorManagement.cancelLabel": "キャンセル",
|
||||
"xpack.synthetics.monitorManagement.cannotSaveIntegration": "非公開の場所を管理する権限がありません。Fleetと統合の両方で「すべて」Kibana権限が必要です。",
|
||||
"xpack.synthetics.monitorManagement.closeButtonLabel": "閉じる",
|
||||
"xpack.synthetics.monitorManagement.completed": "完了",
|
||||
"xpack.synthetics.monitorManagement.configurations.label": "構成",
|
||||
|
@ -36535,7 +36534,6 @@
|
|||
"xpack.synthetics.monitorManagement.monitorSync.failure.statusLabel": "ステータス",
|
||||
"xpack.synthetics.monitorManagement.monitorSync.failure.title": "モニターをSyntheticsサービスと同期できませんでした",
|
||||
"xpack.synthetics.monitorManagement.nameRequired": "場所名は必須です",
|
||||
"xpack.synthetics.monitorManagement.noFleetPermission": "このアクションを実行する権限がありません。統合には「すべて」Kibana権限が必要です。",
|
||||
"xpack.synthetics.monitorManagement.noSyntheticsPermissions": "このアクションを実行する十分な権限がありません。",
|
||||
"xpack.synthetics.monitorManagement.overviewTab.title": "概要",
|
||||
"xpack.synthetics.monitorManagement.param.keyExists": "キーがすでに存在します",
|
||||
|
@ -36548,7 +36546,6 @@
|
|||
"xpack.synthetics.monitorManagement.policyHost": "エージェントポリシー",
|
||||
"xpack.synthetics.monitorManagement.privateLocations": "非公開の場所",
|
||||
"xpack.synthetics.monitorManagement.privateLocations.needPermissions": "非公開の場所を管理するための一部のKibana権限がありません",
|
||||
"xpack.synthetics.monitorManagement.privateLocationsNotAllowedMessage": "モニターを非公開の場所に追加する権限がありません。アクセスをリクエストするには、管理者に問い合わせてください。",
|
||||
"xpack.synthetics.monitorManagement.priviledges.all": "すべて",
|
||||
"xpack.synthetics.monitorManagement.projectDelete.docsLink": "詳細",
|
||||
"xpack.synthetics.monitorManagement.projectPush.label": "プロジェクトプッシュコマンド",
|
||||
|
@ -36753,7 +36750,6 @@
|
|||
"xpack.synthetics.server.projectMonitors.locationEmptyError": "1つ以上の場所または非公開の場所をこのモニターに追加する必要があります。",
|
||||
"xpack.synthetics.service.projectMonitors.cannotUpdateMonitorToDifferentType": "モニターを別のタイプに更新できません。",
|
||||
"xpack.synthetics.service.projectMonitors.failedToUpdateMonitor": "モニターを作成または更新できません",
|
||||
"xpack.synthetics.service.projectMonitors.insufficientFleetPermissions": "パーミッションがありません。非公開の場所を構成するには、Fleetと統合の書き込み権限が必要です。解決するには、Fleetと統合の書き込み権限が割り当てられたユーザーで、新しいAPIキーを生成してください。",
|
||||
"xpack.synthetics.settings.alertDefaultForm.requiredEmail": "終了:選択した電子メールコネクターには電子メールアドレスが必須です",
|
||||
"xpack.synthetics.settings.applyChanges": "変更を適用",
|
||||
"xpack.synthetics.settings.blank.error": "空白にすることはできません。",
|
||||
|
@ -39581,4 +39577,4 @@
|
|||
"xpack.painlessLab.title": "Painless Lab",
|
||||
"xpack.painlessLab.walkthroughButtonLabel": "実地検証"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36468,7 +36468,6 @@
|
|||
"xpack.synthetics.monitorManagement.callout.disabled": "监测管理当前处于禁用状态",
|
||||
"xpack.synthetics.monitorManagement.callout.disabled.adminContact": "管理员访问 Synthetics 应用时,将启用监测管理。",
|
||||
"xpack.synthetics.monitorManagement.cancelLabel": "取消",
|
||||
"xpack.synthetics.monitorManagement.cannotSaveIntegration": "您无权管理专用位置。这需要 Fleet 和集成的“全部”Kibana 权限。",
|
||||
"xpack.synthetics.monitorManagement.closeButtonLabel": "关闭",
|
||||
"xpack.synthetics.monitorManagement.completed": "已完成",
|
||||
"xpack.synthetics.monitorManagement.configurations.label": "配置",
|
||||
|
@ -36529,7 +36528,6 @@
|
|||
"xpack.synthetics.monitorManagement.monitorSync.failure.statusLabel": "状态",
|
||||
"xpack.synthetics.monitorManagement.monitorSync.failure.title": "监测无法与 Synthetics 服务同步",
|
||||
"xpack.synthetics.monitorManagement.nameRequired": "“位置名称”必填",
|
||||
"xpack.synthetics.monitorManagement.noFleetPermission": "您无权执行此操作。这需要集成的“全部”Kibana 权限。",
|
||||
"xpack.synthetics.monitorManagement.noSyntheticsPermissions": "您的权限不足,无法执行此操作。",
|
||||
"xpack.synthetics.monitorManagement.overviewTab.title": "概览",
|
||||
"xpack.synthetics.monitorManagement.param.keyExists": "密钥已存在",
|
||||
|
@ -36542,7 +36540,6 @@
|
|||
"xpack.synthetics.monitorManagement.policyHost": "代理策略",
|
||||
"xpack.synthetics.monitorManagement.privateLocations": "专用位置",
|
||||
"xpack.synthetics.monitorManagement.privateLocations.needPermissions": "您缺少管理专用位置所需的某些 Kibana 权限",
|
||||
"xpack.synthetics.monitorManagement.privateLocationsNotAllowedMessage": "您无权将监测添加到专用位置。请联系管理员请求访问权限。",
|
||||
"xpack.synthetics.monitorManagement.priviledges.all": "全部",
|
||||
"xpack.synthetics.monitorManagement.projectDelete.docsLink": "了解详情",
|
||||
"xpack.synthetics.monitorManagement.projectPush.label": "项目推送命令",
|
||||
|
@ -36747,7 +36744,6 @@
|
|||
"xpack.synthetics.server.projectMonitors.locationEmptyError": "必须至少将一个位置或专用位置添加到此监测。",
|
||||
"xpack.synthetics.service.projectMonitors.cannotUpdateMonitorToDifferentType": "无法将监测更新为不同类型。",
|
||||
"xpack.synthetics.service.projectMonitors.failedToUpdateMonitor": "无法创建或更新监测",
|
||||
"xpack.synthetics.service.projectMonitors.insufficientFleetPermissions": "权限不足。要配置专用位置,您必须具有 Fleet 和集成写入权限。要解决问题,请通过具有 Fleet 和集成写入权限的用户生成新的 API 密钥。",
|
||||
"xpack.synthetics.settings.alertDefaultForm.requiredEmail": "到:选定的电子邮件连接器需要电子邮件",
|
||||
"xpack.synthetics.settings.applyChanges": "应用更改",
|
||||
"xpack.synthetics.settings.blank.error": "不能为空。",
|
||||
|
@ -39575,4 +39571,4 @@
|
|||
"xpack.painlessLab.title": "Painless 实验室",
|
||||
"xpack.painlessLab.walkthroughButtonLabel": "指导"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -324,8 +324,6 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
{
|
||||
feature: {
|
||||
uptime: ['all'],
|
||||
fleet: ['all'],
|
||||
fleetv2: ['all'],
|
||||
actions: ['all'],
|
||||
},
|
||||
spaces: ['*'],
|
||||
|
|
|
@ -1280,7 +1280,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
}
|
||||
});
|
||||
|
||||
it('project monitors - returns a failed monitor when user defines a private location without fleet permissions', async () => {
|
||||
it('project monitors - cannot update project monitors with read only privileges', async () => {
|
||||
const project = `test-project-${uuidv4()}`;
|
||||
|
||||
const secondMonitor = {
|
||||
|
@ -1297,7 +1297,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
kibana: [
|
||||
{
|
||||
feature: {
|
||||
uptime: ['all'],
|
||||
uptime: ['read'],
|
||||
},
|
||||
spaces: ['*'],
|
||||
},
|
||||
|
@ -1309,63 +1309,21 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
full_name: 'a kibana user',
|
||||
});
|
||||
|
||||
const { body } = await supertestWithoutAuth
|
||||
await supertestWithoutAuth
|
||||
.put(
|
||||
SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
|
||||
)
|
||||
.auth(username, password)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({ monitors: testMonitors })
|
||||
.expect(200);
|
||||
|
||||
expect(body).eql({
|
||||
createdMonitors: [testMonitors[0].id],
|
||||
updatedMonitors: [],
|
||||
failedMonitors: [
|
||||
{
|
||||
details:
|
||||
'Insufficient permissions. In order to configure private locations, you must have Fleet and Integrations write permissions. To resolve, please generate a new API key with a user who has Fleet and Integrations write permissions.',
|
||||
id: 'test-id-2',
|
||||
payload: {
|
||||
content:
|
||||
'UEsDBBQACAAIAON5qVQAAAAAAAAAAAAAAAAfAAAAZXhhbXBsZXMvdG9kb3MvYmFzaWMuam91cm5leS50c22Q0WrDMAxF3/sVF7MHB0LMXlc6RvcN+wDPVWNviW0sdUsp/fe5SSiD7UFCWFfHujIGlpnkybwxFTZfoY/E3hsaLEtwhs9RPNWKDU12zAOxkXRIbN4tB9d9pFOJdO6EN2HMqQguWN9asFBuQVMmJ7jiWNII9fIXrbabdUYr58l9IhwhQQZCYORCTFFUC31Btj21NRc7Mq4Nds+4bDD/pNVgT9F52Jyr2Fa+g75LAPttg8yErk+S9ELpTmVotlVwnfNCuh2lepl3+JflUmSBJ3uggt1v9INW/lHNLKze9dJe1J3QJK8pSvWkm6aTtCet5puq+x63+AFQSwcIAPQ3VfcAAACcAQAAUEsBAi0DFAAIAAgA43mpVAD0N1X3AAAAnAEAAB8AAAAAAAAAAAAgAKSBAAAAAGV4YW1wbGVzL3RvZG9zL2Jhc2ljLmpvdXJuZXkudHNQSwUGAAAAAAEAAQBNAAAARAEAAAAA',
|
||||
filter: {
|
||||
match: 'check if title is present',
|
||||
},
|
||||
id: 'test-id-2',
|
||||
locations: ['localhost'],
|
||||
name: 'check if title is present',
|
||||
params: {},
|
||||
playwrightOptions: {
|
||||
chromiumSandbox: false,
|
||||
headless: true,
|
||||
},
|
||||
privateLocations: ['Test private location 0'],
|
||||
schedule: 10,
|
||||
tags: [],
|
||||
throttling: {
|
||||
download: 5,
|
||||
latency: 20,
|
||||
upload: 3,
|
||||
},
|
||||
hash: 'ekrjelkjrelkjre',
|
||||
},
|
||||
reason: 'Failed to create or update monitor',
|
||||
},
|
||||
],
|
||||
});
|
||||
.expect(403);
|
||||
} finally {
|
||||
await Promise.all([
|
||||
testMonitors.map((monitor) => {
|
||||
return deleteMonitor(monitor.id, project, 'default');
|
||||
}),
|
||||
]);
|
||||
await security.user.delete(username);
|
||||
await security.role.delete(roleName);
|
||||
}
|
||||
});
|
||||
|
||||
it('project monitors - returns a successful monitor when user defines a private location with fleet permissions', async () => {
|
||||
it('project monitors - returns a successful monitor when user defines a private location, even without fleet permissions', async () => {
|
||||
const project = `test-project-${uuidv4()}`;
|
||||
const secondMonitor = {
|
||||
...projectMonitors.monitors[0],
|
||||
|
@ -1382,8 +1340,6 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
{
|
||||
feature: {
|
||||
uptime: ['all'],
|
||||
fleetv2: ['all'],
|
||||
fleet: ['all'],
|
||||
},
|
||||
spaces: ['*'],
|
||||
},
|
||||
|
@ -1394,10 +1350,11 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
roles: [roleName],
|
||||
full_name: 'a kibana user',
|
||||
});
|
||||
const { body } = await supertest
|
||||
const { body } = await supertestWithoutAuth
|
||||
.put(
|
||||
SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
|
||||
)
|
||||
.auth(username, password)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({ monitors: testMonitors })
|
||||
.expect(200);
|
||||
|
|
|
@ -8,6 +8,8 @@ import { v4 as uuidv4 } from 'uuid';
|
|||
import { HTTPFields, MonitorFields } from '@kbn/synthetics-plugin/common/runtime_types';
|
||||
import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
|
||||
import expect from '@kbn/expect';
|
||||
import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common';
|
||||
import { syntheticsMonitorType } from '@kbn/synthetics-plugin/common/types/saved_objects';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
import { getFixtureJson } from './helper/get_fixture_json';
|
||||
import { PrivateLocationTestService } from './services/private_location_test_service';
|
||||
|
@ -115,13 +117,9 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const username = 'admin';
|
||||
const roleName = `synthetics_admin`;
|
||||
const password = `${username}-password`;
|
||||
const SPACE_ID = `test-space-${uuidv4()}`;
|
||||
const SPACE_NAME = `test-space-name ${uuidv4()}`;
|
||||
let monitorId = '';
|
||||
|
||||
try {
|
||||
await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
|
||||
|
||||
// use a user without fleet permissions to cause an error
|
||||
await security.role.create(roleName, {
|
||||
kibana: [
|
||||
|
@ -140,6 +138,10 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
});
|
||||
const { id } = await saveMonitor(newMonitor as MonitorFields);
|
||||
monitorId = id;
|
||||
|
||||
// delete the integration policy to cause an error
|
||||
await kibanaServer.savedObjects.clean({ types: [PACKAGE_POLICY_SAVED_OBJECT_TYPE] });
|
||||
|
||||
await supertestWithoutAuth
|
||||
.delete(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '/' + monitorId)
|
||||
.auth(username, password)
|
||||
|
@ -153,10 +155,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
} finally {
|
||||
await security.user.delete(username);
|
||||
await security.role.delete(roleName);
|
||||
await supertest
|
||||
.delete(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '/' + monitorId)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(200);
|
||||
await kibanaServer.savedObjects.clean({ types: [syntheticsMonitorType] });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { ConfigKey, ProjectMonitorsRequest } from '@kbn/synthetics-plugin/common/runtime_types';
|
||||
import { INSUFFICIENT_FLEET_PERMISSIONS } from '@kbn/synthetics-plugin/server/synthetics_service/project_monitor/project_monitor_formatter';
|
||||
import { REQUEST_TOO_LARGE } from '@kbn/synthetics-plugin/server/routes/monitor_cruds/delete_monitor_project';
|
||||
import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
|
||||
import { PackagePolicy } from '@kbn/fleet-plugin/common';
|
||||
|
@ -505,17 +504,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const { total } = savedObjectsResponse.body;
|
||||
expect(total).to.eql(2);
|
||||
|
||||
const {
|
||||
body: { message },
|
||||
} = await supertestWithoutAuth
|
||||
await supertestWithoutAuth
|
||||
.delete(
|
||||
SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
|
||||
)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.auth(username, password)
|
||||
.send({ monitors: monitorsToDelete })
|
||||
.expect(403);
|
||||
expect(message).to.eql(INSUFFICIENT_FLEET_PERMISSIONS);
|
||||
.expect(200);
|
||||
} finally {
|
||||
await supertest
|
||||
.delete(
|
||||
|
|
|
@ -311,6 +311,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
label: 'Europe West',
|
||||
isServiceManaged: true,
|
||||
},
|
||||
{ id: testPolicyId, label: 'Private location', isServiceManaged: false },
|
||||
],
|
||||
};
|
||||
|
||||
|
@ -344,10 +345,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
monitorId = id;
|
||||
const toUpdate = {
|
||||
...savedMonitor,
|
||||
locations: [
|
||||
...savedMonitor.locations,
|
||||
{ id: testPolicyId, label: 'Private location', isServiceManaged: false },
|
||||
],
|
||||
name: '!@#$%^&*()_++[\\-\\]- wow',
|
||||
urls: 'https://google.com',
|
||||
};
|
||||
await supertestWithoutAuth
|
||||
|
@ -360,6 +358,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const response = await monitorTestService.getMonitor(monitorId);
|
||||
|
||||
// ensure monitor was not updated
|
||||
expect(response.body.urls).not.eql(toUpdate.urls);
|
||||
expect(response.body.urls).eql(newMonitor.urls);
|
||||
expect(response.body.locations).eql(newMonitor.locations);
|
||||
} finally {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue