mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Synthetics] Space aware private locations !! (#202634)
## Summary Fixes https://github.com/elastic/kibana/issues/199976 User can choose which space the location will be visible in while creating a location !! ### Testing - [ ] Create location in all spaces and make sure it's visible everywhere. - [ ] Creation location in a specific space and make sure it's only visible in specified space <img width="1728" alt="image" src="https://github.com/user-attachments/assets/6aa5cac9-500a-447a-8ef5-bf53e91a16dd" />
This commit is contained in:
parent
a108c632a4
commit
e51b2bda27
44 changed files with 1039 additions and 604 deletions
|
@ -37,6 +37,9 @@ The request body should contain the following attributes:
|
|||
- `lat` (Required, number): The latitude of the location.
|
||||
- `lon` (Required, number): The longitude of the location.
|
||||
|
||||
`spaces`::
|
||||
(Optional, array of strings) An array of space IDs where the private location is available. If not provided, the private location is available in all spaces.
|
||||
|
||||
[[private-location-create-example]]
|
||||
==== Example
|
||||
|
||||
|
@ -53,6 +56,7 @@ POST /api/private_locations
|
|||
"lat": 40.7128,
|
||||
"lon": -74.0060
|
||||
}
|
||||
"spaces": ["default"]
|
||||
}
|
||||
--------------------------------------------------
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ export const PrivateLocationCodec = t.intersection([
|
|||
lon: t.number,
|
||||
}),
|
||||
namespace: t.string,
|
||||
spaces: t.array(t.string),
|
||||
}),
|
||||
]);
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ describe('GettingStartedPage', () => {
|
|||
deleteLoading: false,
|
||||
onSubmit: jest.fn(),
|
||||
onDelete: jest.fn(),
|
||||
formData: undefined,
|
||||
createLoading: false,
|
||||
});
|
||||
jest.spyOn(permissionsHooks, 'useCanManagePrivateLocation').mockReturnValue(true);
|
||||
});
|
||||
|
@ -81,9 +81,11 @@ describe('GettingStartedPage', () => {
|
|||
locationsLoaded: true,
|
||||
loading: false,
|
||||
},
|
||||
privateLocations: {
|
||||
isCreatePrivateLocationFlyoutVisible: true,
|
||||
},
|
||||
agentPolicies: {
|
||||
data: [],
|
||||
isAddingNewPrivateLocation: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -108,7 +110,9 @@ describe('GettingStartedPage', () => {
|
|||
},
|
||||
agentPolicies: {
|
||||
data: [{}],
|
||||
isAddingNewPrivateLocation: true,
|
||||
},
|
||||
privateLocations: {
|
||||
isCreatePrivateLocationFlyoutVisible: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -145,7 +149,9 @@ describe('GettingStartedPage', () => {
|
|||
},
|
||||
agentPolicies: {
|
||||
data: [{}],
|
||||
isAddingNewPrivateLocation: true,
|
||||
},
|
||||
privateLocations: {
|
||||
isCreatePrivateLocationFlyoutVisible: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -24,18 +24,14 @@ import { useKibana } from '@kbn/kibana-react-plugin/public';
|
|||
import { useBreadcrumbs, useEnablement, useLocations } from '../../hooks';
|
||||
import { usePrivateLocationsAPI } from '../settings/private_locations/hooks/use_locations_api';
|
||||
import { LoadingState } from '../monitors_page/overview/overview/monitor_detail_flyout';
|
||||
import {
|
||||
getServiceLocations,
|
||||
selectAddingNewPrivateLocation,
|
||||
setAddingNewPrivateLocation,
|
||||
getAgentPoliciesAction,
|
||||
selectAgentPolicies,
|
||||
cleanMonitorListState,
|
||||
} from '../../state';
|
||||
import { getServiceLocations, cleanMonitorListState } from '../../state';
|
||||
import { MONITOR_ADD_ROUTE } from '../../../../../common/constants/ui';
|
||||
import { SimpleMonitorForm } from './simple_monitor_form';
|
||||
import { AddLocationFlyout, NewLocation } from '../settings/private_locations/add_location_flyout';
|
||||
import type { ClientPluginsStart } from '../../../../plugin';
|
||||
import { getAgentPoliciesAction, selectAgentPolicies } from '../../state/agent_policies';
|
||||
import { selectAddingNewPrivateLocation } from '../../state/settings/selectors';
|
||||
import { setIsCreatePrivateLocationFlyoutVisible } from '../../state/private_locations/actions';
|
||||
|
||||
export const GettingStartedPage = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
@ -134,11 +130,11 @@ export const GettingStartedOnPrem = () => {
|
|||
const isAddingNewLocation = useSelector(selectAddingNewPrivateLocation);
|
||||
|
||||
const setIsAddingNewLocation = useCallback(
|
||||
(val: boolean) => dispatch(setAddingNewPrivateLocation(val)),
|
||||
(val: boolean) => dispatch(setIsCreatePrivateLocationFlyoutVisible(val)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const { onSubmit, privateLocations, loading } = usePrivateLocationsAPI();
|
||||
const { onSubmit, privateLocations } = usePrivateLocationsAPI();
|
||||
|
||||
const handleSubmit = (formData: NewLocation) => {
|
||||
onSubmit(formData);
|
||||
|
@ -182,7 +178,6 @@ export const GettingStartedOnPrem = () => {
|
|||
setIsOpen={setIsAddingNewLocation}
|
||||
onSubmit={handleSubmit}
|
||||
privateLocations={privateLocations}
|
||||
isLoading={loading}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* 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 React, { useEffect } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { EuiComboBox, EuiFormRow } from '@elastic/eui';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { ALL_SPACES_ID } from '@kbn/security-plugin/public';
|
||||
|
||||
import { ClientPluginsStart } from '../../../../../plugin';
|
||||
import { PrivateLocation } from '../../../../../../common/runtime_types';
|
||||
|
||||
export const NAMESPACES_NAME = 'spaces';
|
||||
|
||||
export const SpaceSelector: React.FC = () => {
|
||||
const { services } = useKibana<ClientPluginsStart>();
|
||||
const [spacesList, setSpacesList] = React.useState<Array<{ id: string; label: string }>>([]);
|
||||
const data = services.spaces?.ui.useSpaces();
|
||||
|
||||
const {
|
||||
control,
|
||||
formState: { isSubmitted },
|
||||
trigger,
|
||||
} = useFormContext<PrivateLocation>();
|
||||
const { isTouched, error } = control.getFieldState(NAMESPACES_NAME);
|
||||
|
||||
const showFieldInvalid = (isSubmitted || isTouched) && !!error;
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
data.spacesDataPromise.then((spacesData) => {
|
||||
setSpacesList([
|
||||
allSpacesOption,
|
||||
...[...spacesData.spacesMap].map(([spaceId, dataS]) => ({
|
||||
id: spaceId,
|
||||
label: dataS.name,
|
||||
})),
|
||||
]);
|
||||
});
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
label={SPACES_LABEL}
|
||||
helpText={HELP_TEXT}
|
||||
isInvalid={showFieldInvalid}
|
||||
error={showFieldInvalid ? NAMESPACES_NAME : undefined}
|
||||
>
|
||||
<Controller
|
||||
name={NAMESPACES_NAME}
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<EuiComboBox
|
||||
fullWidth
|
||||
aria-label={SPACES_LABEL}
|
||||
placeholder={SPACES_LABEL}
|
||||
isInvalid={showFieldInvalid}
|
||||
{...field}
|
||||
onBlur={async () => {
|
||||
await trigger();
|
||||
}}
|
||||
options={spacesList}
|
||||
selectedOptions={(field.value ?? []).map((id) => {
|
||||
const sp = spacesList.find((space) => space.id === id);
|
||||
if (!sp) {
|
||||
return {
|
||||
id,
|
||||
label: id,
|
||||
};
|
||||
}
|
||||
return { id: sp.id, label: sp.label };
|
||||
})}
|
||||
isClearable={true}
|
||||
onChange={(selected) => {
|
||||
const selectedIds = selected.map((option) => option.id);
|
||||
|
||||
// if last value is not all spaces, remove all spaces value
|
||||
if (
|
||||
selectedIds.length > 0 &&
|
||||
selectedIds[selectedIds.length - 1] !== allSpacesOption.id
|
||||
) {
|
||||
field.onChange(selectedIds.filter((id) => id !== allSpacesOption.id));
|
||||
return;
|
||||
}
|
||||
|
||||
// if last value is all spaces, remove all other values
|
||||
if (
|
||||
selectedIds.length > 0 &&
|
||||
selectedIds[selectedIds.length - 1] === allSpacesOption.id
|
||||
) {
|
||||
field.onChange([allSpacesOption.id]);
|
||||
return;
|
||||
}
|
||||
|
||||
field.onChange(selectedIds);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
);
|
||||
};
|
||||
|
||||
export const ALL_SPACES_LABEL = i18n.translate('xpack.synthetics.spaceList.allSpacesLabel', {
|
||||
defaultMessage: `* All spaces`,
|
||||
});
|
||||
|
||||
const allSpacesOption = {
|
||||
id: ALL_SPACES_ID,
|
||||
label: ALL_SPACES_LABEL,
|
||||
};
|
||||
|
||||
const SPACES_LABEL = i18n.translate('xpack.synthetics.privateLocation.spacesLabel', {
|
||||
defaultMessage: 'Spaces ',
|
||||
});
|
||||
|
||||
const HELP_TEXT = i18n.translate('xpack.synthetics.privateLocation.spacesHelpText', {
|
||||
defaultMessage: 'Select the spaces where this location will be available.',
|
||||
});
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { FormProvider } from 'react-hook-form';
|
||||
import {
|
||||
EuiButtonEmpty,
|
||||
|
@ -19,22 +19,27 @@ import {
|
|||
EuiButton,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { SpacesContextProps } from '@kbn/spaces-plugin/public';
|
||||
import { ALL_SPACES_ID } from '@kbn/security-plugin/public';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { NoPermissionsTooltip } from '../../common/components/permissions';
|
||||
import { useSyntheticsSettingsContext } from '../../../contexts';
|
||||
import { useFormWrapped } from '../../../../../hooks/use_form_wrapped';
|
||||
import { PrivateLocation } from '../../../../../../common/runtime_types';
|
||||
import { LocationForm } from './location_form';
|
||||
import { ManageEmptyState } from './manage_empty_state';
|
||||
import { ClientPluginsStart } from '../../../../../plugin';
|
||||
import { selectPrivateLocationsState } from '../../../state/private_locations/selectors';
|
||||
|
||||
export type NewLocation = Omit<PrivateLocation, 'id'>;
|
||||
const getEmptyFunctionComponent: React.FC<SpacesContextProps> = ({ children }) => <>{children}</>;
|
||||
|
||||
export const AddLocationFlyout = ({
|
||||
onSubmit,
|
||||
setIsOpen,
|
||||
privateLocations,
|
||||
isLoading,
|
||||
}: {
|
||||
isLoading: boolean;
|
||||
onSubmit: (val: NewLocation) => void;
|
||||
setIsOpen: (val: boolean) => void;
|
||||
privateLocations: PrivateLocation[];
|
||||
|
@ -50,10 +55,21 @@ export const AddLocationFlyout = ({
|
|||
lat: 0,
|
||||
lon: 0,
|
||||
},
|
||||
spaces: [ALL_SPACES_ID],
|
||||
},
|
||||
});
|
||||
|
||||
const { canSave } = useSyntheticsSettingsContext();
|
||||
const { canSave, canManagePrivateLocations } = useSyntheticsSettingsContext();
|
||||
|
||||
const { createLoading } = useSelector(selectPrivateLocationsState);
|
||||
|
||||
const { spaces: spacesApi } = useKibana<ClientPluginsStart>().services;
|
||||
|
||||
const ContextWrapper = useMemo(
|
||||
() =>
|
||||
spacesApi ? spacesApi.ui.components.getSpacesContextProvider : getEmptyFunctionComponent,
|
||||
[spacesApi]
|
||||
);
|
||||
|
||||
const { handleSubmit } = form;
|
||||
const closeFlyout = () => {
|
||||
|
@ -61,48 +77,50 @@ export const AddLocationFlyout = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
<EuiFlyout onClose={closeFlyout} style={{ width: 540 }}>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle size="m">
|
||||
<h2>{ADD_PRIVATE_LOCATION}</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<ManageEmptyState privateLocations={privateLocations} showEmptyLocations={false}>
|
||||
<LocationForm privateLocations={privateLocations} />
|
||||
</ManageEmptyState>
|
||||
</EuiFlyoutBody>
|
||||
<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
|
||||
<ContextWrapper>
|
||||
<FormProvider {...form}>
|
||||
<EuiFlyout onClose={closeFlyout} style={{ width: 540 }}>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle size="m">
|
||||
<h2>{ADD_PRIVATE_LOCATION}</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<ManageEmptyState privateLocations={privateLocations} showEmptyLocations={false}>
|
||||
<LocationForm privateLocations={privateLocations} />
|
||||
</ManageEmptyState>
|
||||
</EuiFlyoutBody>
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="syntheticsAddLocationFlyoutButton"
|
||||
fill
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
isLoading={isLoading}
|
||||
isDisabled={!canSave}
|
||||
iconType="cross"
|
||||
onClick={closeFlyout}
|
||||
flush="left"
|
||||
isLoading={createLoading}
|
||||
>
|
||||
{SAVE_LABEL}
|
||||
</EuiButton>
|
||||
</NoPermissionsTooltip>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</EuiFlyout>
|
||||
</FormProvider>
|
||||
{CANCEL_LABEL}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<NoPermissionsTooltip canEditSynthetics={canSave}>
|
||||
<EuiButton
|
||||
data-test-subj="syntheticsAddLocationFlyoutButton"
|
||||
fill
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
isLoading={createLoading}
|
||||
isDisabled={!canSave || !canManagePrivateLocations}
|
||||
>
|
||||
{SAVE_LABEL}
|
||||
</EuiButton>
|
||||
</NoPermissionsTooltip>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</EuiFlyout>
|
||||
</FormProvider>
|
||||
</ContextWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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 { EuiCallOut, EuiLink } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import React from 'react';
|
||||
import { AGENT_MISSING_CALLOUT_TITLE } from './location_form';
|
||||
|
||||
export const AgentPolicyCallout: React.FC = () => {
|
||||
return (
|
||||
<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 an 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>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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 { EuiCallOut, EuiCode, EuiLink } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import React from 'react';
|
||||
import { AGENT_CALLOUT_TITLE } from './location_form';
|
||||
|
||||
export const BrowserMonitorCallout: React.FC = () => {
|
||||
return (
|
||||
<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>
|
||||
);
|
||||
};
|
|
@ -13,7 +13,10 @@ 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';
|
||||
import {
|
||||
setIsCreatePrivateLocationFlyoutVisible,
|
||||
setManageFlyoutOpen,
|
||||
} from '../../../state/private_locations/actions';
|
||||
|
||||
export const EmptyLocations = ({
|
||||
inFlyout = true,
|
||||
|
@ -64,7 +67,7 @@ export const EmptyLocations = ({
|
|||
onClick={() => {
|
||||
setIsAddingNew?.(true);
|
||||
dispatch(setManageFlyoutOpen(true));
|
||||
dispatch(setAddingNewPrivateLocation(true));
|
||||
dispatch(setIsCreatePrivateLocationFlyoutVisible(true));
|
||||
}}
|
||||
>
|
||||
{ADD_LOCATION}
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
/*
|
||||
* 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 { createElement } from 'react';
|
||||
import { act, waitFor, renderHook } from '@testing-library/react';
|
||||
import { WrappedHelper } from '../../../../utils/testing';
|
||||
import { getServiceLocations } from '../../../../state/service_locations';
|
||||
import { setAddingNewPrivateLocation } from '../../../../state/private_locations';
|
||||
import { usePrivateLocationsAPI } from './use_locations_api';
|
||||
import * as locationAPI from '../../../../state/private_locations/api';
|
||||
import * as reduxHooks from 'react-redux';
|
||||
|
||||
describe('usePrivateLocationsAPI', () => {
|
||||
const dispatch = jest.fn();
|
||||
const addAPI = jest.spyOn(locationAPI, 'addSyntheticsPrivateLocations').mockResolvedValue([]);
|
||||
const deletedAPI = jest
|
||||
.spyOn(locationAPI, 'deleteSyntheticsPrivateLocations')
|
||||
.mockResolvedValue([]);
|
||||
jest.spyOn(locationAPI, 'getSyntheticsPrivateLocations');
|
||||
jest.spyOn(reduxHooks, 'useDispatch').mockReturnValue(dispatch);
|
||||
|
||||
it('returns expected results', () => {
|
||||
const { result } = renderHook(() => usePrivateLocationsAPI(), {
|
||||
wrapper: ({ children }) => createElement(WrappedHelper, null, children),
|
||||
});
|
||||
|
||||
expect(result.current).toEqual(
|
||||
expect.objectContaining({
|
||||
loading: true,
|
||||
privateLocations: [],
|
||||
})
|
||||
);
|
||||
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
jest.spyOn(locationAPI, 'getSyntheticsPrivateLocations').mockResolvedValue([
|
||||
{
|
||||
id: 'Test',
|
||||
agentPolicyId: 'testPolicy',
|
||||
} as any,
|
||||
]);
|
||||
it('returns expected results after data', async () => {
|
||||
const { result } = renderHook(() => usePrivateLocationsAPI(), {
|
||||
wrapper: ({ children }) => createElement(WrappedHelper, null, children),
|
||||
});
|
||||
|
||||
expect(result.current).toEqual(
|
||||
expect.objectContaining({
|
||||
loading: true,
|
||||
privateLocations: [],
|
||||
})
|
||||
);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(result.current).toEqual(
|
||||
expect.objectContaining({
|
||||
loading: false,
|
||||
privateLocations: [],
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('adds location on submit', async () => {
|
||||
const { result } = renderHook(() => usePrivateLocationsAPI(), {
|
||||
wrapper: ({ children }) => createElement(WrappedHelper, null, children),
|
||||
});
|
||||
|
||||
await waitFor(() => new Promise((resolve) => resolve(null)));
|
||||
|
||||
act(() => {
|
||||
result.current.onSubmit({
|
||||
agentPolicyId: 'newPolicy',
|
||||
label: 'new',
|
||||
geo: {
|
||||
lat: 0,
|
||||
lon: 0,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(addAPI).toHaveBeenCalledWith({
|
||||
geo: {
|
||||
lat: 0,
|
||||
lon: 0,
|
||||
},
|
||||
label: 'new',
|
||||
agentPolicyId: 'newPolicy',
|
||||
});
|
||||
expect(dispatch).toBeCalledWith(setAddingNewPrivateLocation(false));
|
||||
expect(dispatch).toBeCalledWith(getServiceLocations());
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes location on delete', async () => {
|
||||
const { result } = renderHook(() => usePrivateLocationsAPI(), {
|
||||
wrapper: ({ children }) => createElement(WrappedHelper, null, children),
|
||||
});
|
||||
|
||||
await waitFor(() => new Promise((resolve) => resolve(null)));
|
||||
|
||||
act(() => {
|
||||
result.current.onDelete('Test');
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(deletedAPI).toHaveBeenLastCalledWith('Test');
|
||||
expect(dispatch).toBeCalledWith(setAddingNewPrivateLocation(false));
|
||||
expect(dispatch).toBeCalledWith(getServiceLocations());
|
||||
});
|
||||
});
|
||||
});
|
|
@ -5,75 +5,45 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useFetcher } from '@kbn/observability-shared-plugin/public';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { NewLocation } from '../add_location_flyout';
|
||||
import { getServiceLocations } from '../../../../state/service_locations';
|
||||
import {
|
||||
createPrivateLocationAction,
|
||||
deletePrivateLocationAction,
|
||||
getPrivateLocationsAction,
|
||||
selectPrivateLocations,
|
||||
selectPrivateLocationsLoading,
|
||||
setAddingNewPrivateLocation,
|
||||
} from '../../../../state/private_locations';
|
||||
import {
|
||||
addSyntheticsPrivateLocations,
|
||||
deleteSyntheticsPrivateLocations,
|
||||
} from '../../../../state/private_locations/api';
|
||||
} from '../../../../state/private_locations/actions';
|
||||
import { selectPrivateLocationsState } from '../../../../state/private_locations/selectors';
|
||||
|
||||
export const usePrivateLocationsAPI = () => {
|
||||
const [formData, setFormData] = useState<NewLocation>();
|
||||
const [deleteId, setDeleteId] = useState<string>();
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const setIsAddingNew = (val: boolean) => dispatch(setAddingNewPrivateLocation(val));
|
||||
const privateLocations = useSelector(selectPrivateLocations);
|
||||
const fetchLoading = useSelector(selectPrivateLocationsLoading);
|
||||
const { loading, createLoading, deleteLoading, data } = useSelector(selectPrivateLocationsState);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getPrivateLocationsAction.get());
|
||||
}, [dispatch]);
|
||||
|
||||
const { loading: saveLoading } = useFetcher(async () => {
|
||||
if (formData) {
|
||||
const result = await addSyntheticsPrivateLocations(formData);
|
||||
setFormData(undefined);
|
||||
setIsAddingNew(false);
|
||||
dispatch(getServiceLocations());
|
||||
useEffect(() => {
|
||||
if (data === null) {
|
||||
dispatch(getPrivateLocationsAction.get());
|
||||
return result;
|
||||
}
|
||||
// FIXME: Dario thinks there is a better way to do this but
|
||||
// he's getting tired and maybe the Synthetics folks can fix it
|
||||
}, [formData]);
|
||||
}, [data, dispatch]);
|
||||
|
||||
const onSubmit = (data: NewLocation) => {
|
||||
setFormData(data);
|
||||
const onSubmit = (newLoc: NewLocation) => {
|
||||
dispatch(createPrivateLocationAction.get(newLoc));
|
||||
};
|
||||
|
||||
const onDelete = (id: string) => {
|
||||
setDeleteId(id);
|
||||
dispatch(deletePrivateLocationAction.get(id));
|
||||
};
|
||||
|
||||
const { loading: deleteLoading } = useFetcher(async () => {
|
||||
if (deleteId) {
|
||||
const result = await deleteSyntheticsPrivateLocations(deleteId);
|
||||
setDeleteId(undefined);
|
||||
dispatch(getServiceLocations());
|
||||
dispatch(getPrivateLocationsAction.get());
|
||||
return result;
|
||||
}
|
||||
// FIXME: Dario thinks there is a better way to do this but
|
||||
// he's getting tired and maybe the Synthetics folks can fix it
|
||||
}, [deleteId]);
|
||||
|
||||
return {
|
||||
formData,
|
||||
onSubmit,
|
||||
onDelete,
|
||||
deleteLoading: Boolean(deleteLoading),
|
||||
loading: Boolean(fetchLoading || saveLoading),
|
||||
privateLocations,
|
||||
deleteLoading,
|
||||
loading,
|
||||
createLoading,
|
||||
privateLocations: data ?? [],
|
||||
};
|
||||
};
|
||||
|
|
|
@ -6,32 +6,22 @@
|
|||
*/
|
||||
|
||||
import React, { Ref } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import {
|
||||
EuiFieldText,
|
||||
EuiForm,
|
||||
EuiFormRow,
|
||||
EuiSpacer,
|
||||
EuiCallOut,
|
||||
EuiCode,
|
||||
EuiLink,
|
||||
EuiFieldTextProps,
|
||||
} from '@elastic/eui';
|
||||
import { EuiFieldText, EuiForm, EuiFormRow, EuiSpacer, EuiFieldTextProps } from '@elastic/eui';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useFormContext, useFormState } from 'react-hook-form';
|
||||
import { selectAgentPolicies } from '../../../state/agent_policies';
|
||||
import { BrowserMonitorCallout } from './browser_monitor_callout';
|
||||
import { SpaceSelector } from '../components/spaces_select';
|
||||
import { TagsField } from '../components/tags_field';
|
||||
import { PrivateLocation } from '../../../../../../common/runtime_types';
|
||||
import { AgentPolicyNeeded } from './agent_policy_needed';
|
||||
import { PolicyHostsField, AGENT_POLICY_FIELD_NAME } from './policy_hosts';
|
||||
import { selectAgentPolicies } from '../../../state/private_locations';
|
||||
import { PolicyHostsField } from './policy_hosts';
|
||||
|
||||
export const LocationForm = ({ privateLocations }: { privateLocations: PrivateLocation[] }) => {
|
||||
const { data } = useSelector(selectAgentPolicies);
|
||||
const { control, register, getValues } = useFormContext<PrivateLocation>();
|
||||
const { control, register } = useFormContext<PrivateLocation>();
|
||||
const { errors } = useFormState();
|
||||
const selectedPolicyId = getValues(AGENT_POLICY_FIELD_NAME);
|
||||
const selectedPolicy = data?.find((item) => item.id === selectedPolicyId);
|
||||
|
||||
const tagsList = privateLocations.reduce((acc, item) => {
|
||||
const tags = item.tags || [];
|
||||
|
@ -70,66 +60,9 @@ export const LocationForm = ({ privateLocations }: { privateLocations: PrivateLo
|
|||
<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>
|
||||
|
||||
<BrowserMonitorCallout />
|
||||
<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 an 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>
|
||||
)}
|
||||
<SpaceSelector />
|
||||
</EuiForm>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -18,12 +18,12 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { Criteria } from '@elastic/eui/src/components/basic_table/basic_table';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { CopyName } from './copy_name';
|
||||
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 { setAddingNewPrivateLocation } from '../../../state/private_locations';
|
||||
import { PrivateLocationDocsLink, START_ADDING_LOCATIONS_DESCRIPTION } from './empty_locations';
|
||||
import { PrivateLocation } from '../../../../../../common/runtime_types';
|
||||
import { NoPermissionsTooltip } from '../../common/components/permissions';
|
||||
|
@ -31,6 +31,8 @@ import { DeleteLocation } from './delete_location';
|
|||
import { useLocationMonitors } from './hooks/use_location_monitors';
|
||||
import { PolicyName } from './policy_name';
|
||||
import { LOCATION_NAME_LABEL } from './location_form';
|
||||
import { setIsCreatePrivateLocationFlyoutVisible } from '../../../state/private_locations/actions';
|
||||
import { ClientPluginsStart } from '../../../../../plugin';
|
||||
|
||||
interface ListItem extends PrivateLocation {
|
||||
monitors: number;
|
||||
|
@ -41,7 +43,7 @@ export const PrivateLocationsTable = ({
|
|||
onDelete,
|
||||
privateLocations,
|
||||
}: {
|
||||
deleteLoading: boolean;
|
||||
deleteLoading?: boolean;
|
||||
onDelete: (id: string) => void;
|
||||
privateLocations: PrivateLocation[];
|
||||
}) => {
|
||||
|
@ -54,6 +56,10 @@ export const PrivateLocationsTable = ({
|
|||
|
||||
const { canSave, canManagePrivateLocations } = useSyntheticsSettingsContext();
|
||||
|
||||
const { services } = useKibana<ClientPluginsStart>();
|
||||
|
||||
const LazySpaceList = services.spaces?.ui.components.getSpaceList ?? (() => null);
|
||||
|
||||
const tagsList = privateLocations.reduce((acc, item) => {
|
||||
const tags = item.tags || [];
|
||||
return new Set([...acc, ...tags]);
|
||||
|
@ -97,6 +103,14 @@ export const PrivateLocationsTable = ({
|
|||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Spaces',
|
||||
field: 'spaces',
|
||||
sortable: true,
|
||||
render: (spaces: string[]) => {
|
||||
return <LazySpaceList namespaces={spaces} behaviorContext="outside-space" />;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ACTIONS_LABEL,
|
||||
actions: [
|
||||
|
@ -124,7 +138,7 @@ export const PrivateLocationsTable = ({
|
|||
monitors: locationMonitors?.find((l) => l.id === location.id)?.count ?? 0,
|
||||
}));
|
||||
|
||||
const setIsAddingNew = (val: boolean) => dispatch(setAddingNewPrivateLocation(val));
|
||||
const setIsAddingNew = (val: boolean) => dispatch(setIsCreatePrivateLocationFlyoutVisible(val));
|
||||
|
||||
const renderToolRight = () => {
|
||||
return [
|
||||
|
|
|
@ -10,7 +10,7 @@ import { useSelector } from 'react-redux';
|
|||
import { PrivateLocation } from '../../../../../../common/runtime_types';
|
||||
import { AgentPolicyNeeded } from './agent_policy_needed';
|
||||
import { EmptyLocations } from './empty_locations';
|
||||
import { selectAgentPolicies } from '../../../state/private_locations';
|
||||
import { selectAgentPolicies } from '../../../state/agent_policies';
|
||||
|
||||
export const ManageEmptyState: FC<
|
||||
PropsWithChildren<{
|
||||
|
|
|
@ -12,7 +12,6 @@ import * as locationHooks from './hooks/use_locations_api';
|
|||
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');
|
||||
|
@ -28,12 +27,12 @@ describe('<ManagePrivateLocations />', () => {
|
|||
canCreateAgentPolicies: false,
|
||||
});
|
||||
jest.spyOn(locationHooks, 'usePrivateLocationsAPI').mockReturnValue({
|
||||
formData: {} as PrivateLocation,
|
||||
loading: false,
|
||||
onSubmit: jest.fn(),
|
||||
privateLocations: [],
|
||||
onDelete: jest.fn(),
|
||||
deleteLoading: false,
|
||||
createLoading: false,
|
||||
});
|
||||
jest.spyOn(permissionsHooks, 'useEnablement').mockReturnValue({
|
||||
isServiceAllowed: true,
|
||||
|
@ -59,8 +58,9 @@ describe('<ManagePrivateLocations />', () => {
|
|||
data: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
isManageFlyoutOpen: false,
|
||||
isAddingNewPrivateLocation: false,
|
||||
},
|
||||
privateLocations: {
|
||||
isCreatePrivateLocationFlyoutVisible: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -93,8 +93,9 @@ describe('<ManagePrivateLocations />', () => {
|
|||
data: [{}],
|
||||
loading: false,
|
||||
error: null,
|
||||
isManageFlyoutOpen: false,
|
||||
isAddingNewPrivateLocation: false,
|
||||
},
|
||||
privateLocations: {
|
||||
isCreatePrivateLocationFlyoutVisible: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -123,7 +124,6 @@ describe('<ManagePrivateLocations />', () => {
|
|||
} as SyntheticsSettingsContextValues);
|
||||
|
||||
jest.spyOn(locationHooks, 'usePrivateLocationsAPI').mockReturnValue({
|
||||
formData: {} as PrivateLocation,
|
||||
loading: false,
|
||||
onSubmit: jest.fn(),
|
||||
privateLocations: [
|
||||
|
@ -136,6 +136,7 @@ describe('<ManagePrivateLocations />', () => {
|
|||
],
|
||||
onDelete: jest.fn(),
|
||||
deleteLoading: false,
|
||||
createLoading: false,
|
||||
});
|
||||
const { getByText, getByRole, findByText } = render(<ManagePrivateLocations />, {
|
||||
state: {
|
||||
|
@ -143,8 +144,9 @@ describe('<ManagePrivateLocations />', () => {
|
|||
data: [{}],
|
||||
loading: false,
|
||||
error: null,
|
||||
isManageFlyoutOpen: false,
|
||||
isAddingNewPrivateLocation: false,
|
||||
},
|
||||
privateLocations: {
|
||||
isCreatePrivateLocationFlyoutVisible: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -4,39 +4,48 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import React, { useEffect, useCallback, useMemo } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { SpacesContextProps } from '@kbn/spaces-plugin/public';
|
||||
import { LoadingState } from '../../monitors_page/overview/overview/monitor_detail_flyout';
|
||||
import { PrivateLocationsTable } from './locations_table';
|
||||
import { ManageEmptyState } from './manage_empty_state';
|
||||
import { AddLocationFlyout, NewLocation } from './add_location_flyout';
|
||||
import { usePrivateLocationsAPI } from './hooks/use_locations_api';
|
||||
import {
|
||||
getAgentPoliciesAction,
|
||||
selectAddingNewPrivateLocation,
|
||||
setAddingNewPrivateLocation,
|
||||
} from '../../../state/private_locations';
|
||||
import { selectAddingNewPrivateLocation } from '../../../state/private_locations/selectors';
|
||||
import { getServiceLocations } from '../../../state';
|
||||
import { getAgentPoliciesAction } from '../../../state/agent_policies';
|
||||
import { setIsCreatePrivateLocationFlyoutVisible } from '../../../state/private_locations/actions';
|
||||
import { ClientPluginsStart } from '../../../../../plugin';
|
||||
|
||||
const getEmptyFunctionComponent: React.FC<SpacesContextProps> = ({ children }) => <>{children}</>;
|
||||
|
||||
export const ManagePrivateLocations = () => {
|
||||
const dispatch = useDispatch();
|
||||
const { services } = useKibana<ClientPluginsStart>();
|
||||
|
||||
const spacesApi = services.spaces;
|
||||
|
||||
const SpacesContextProvider = useMemo(
|
||||
() =>
|
||||
spacesApi ? spacesApi.ui.components.getSpacesContextProvider : getEmptyFunctionComponent,
|
||||
[spacesApi]
|
||||
);
|
||||
|
||||
const isAddingNew = useSelector(selectAddingNewPrivateLocation);
|
||||
const setIsAddingNew = useCallback(
|
||||
(val: boolean) => dispatch(setAddingNewPrivateLocation(val)),
|
||||
(val: boolean) => dispatch(setIsCreatePrivateLocationFlyoutVisible(val)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const { onSubmit, loading, privateLocations, onDelete, deleteLoading } = usePrivateLocationsAPI();
|
||||
|
||||
// make sure flyout is closed when first visiting the page
|
||||
useEffect(() => {
|
||||
setIsAddingNew(false);
|
||||
}, [setIsAddingNew]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getAgentPoliciesAction.get());
|
||||
dispatch(getServiceLocations());
|
||||
// make sure flyout is closed when first visiting the page
|
||||
dispatch(setIsCreatePrivateLocationFlyoutVisible(false));
|
||||
}, [dispatch]);
|
||||
|
||||
const handleSubmit = (formData: NewLocation) => {
|
||||
|
@ -44,7 +53,7 @@ export const ManagePrivateLocations = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<SpacesContextProvider>
|
||||
{loading ? (
|
||||
<LoadingState />
|
||||
) : (
|
||||
|
@ -62,9 +71,8 @@ export const ManagePrivateLocations = () => {
|
|||
setIsOpen={setIsAddingNew}
|
||||
onSubmit={handleSubmit}
|
||||
privateLocations={privateLocations}
|
||||
isLoading={loading}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
</SpacesContextProvider>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -17,23 +17,33 @@ import {
|
|||
EuiSuperSelect,
|
||||
EuiText,
|
||||
EuiToolTip,
|
||||
EuiSpacer,
|
||||
EuiButtonEmpty,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { useSyntheticsSettingsContext } from '../../../contexts';
|
||||
import { AgentPolicyCallout } from './agent_policy_callout';
|
||||
import { PrivateLocation } from '../../../../../../common/runtime_types';
|
||||
import { selectAgentPolicies } from '../../../state/private_locations';
|
||||
import { selectAgentPolicies } from '../../../state/agent_policies';
|
||||
|
||||
export const AGENT_POLICY_FIELD_NAME = 'agentPolicyId';
|
||||
|
||||
export const PolicyHostsField = ({ privateLocations }: { privateLocations: PrivateLocation[] }) => {
|
||||
const { data } = useSelector(selectAgentPolicies);
|
||||
const { basePath } = useSyntheticsSettingsContext();
|
||||
|
||||
const {
|
||||
control,
|
||||
formState: { isSubmitted },
|
||||
trigger,
|
||||
getValues,
|
||||
} = useFormContext<PrivateLocation>();
|
||||
const { isTouched, error } = control.getFieldState(AGENT_POLICY_FIELD_NAME);
|
||||
const showFieldInvalid = (isSubmitted || isTouched) && !!error;
|
||||
const selectedPolicyId = getValues(AGENT_POLICY_FIELD_NAME);
|
||||
|
||||
const selectedPolicy = data?.find((item) => item.id === selectedPolicyId);
|
||||
|
||||
const policyHostsOptions = data?.map((item) => {
|
||||
const hasLocation = privateLocations.find((location) => location.agentPolicyId === item.id);
|
||||
|
@ -89,36 +99,47 @@ export const PolicyHostsField = ({ privateLocations }: { privateLocations: Priva
|
|||
});
|
||||
|
||||
return (
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
label={POLICY_HOST_LABEL}
|
||||
helpText={showFieldInvalid ? SELECT_POLICY_HOSTS_HELP_TEXT : undefined}
|
||||
isInvalid={showFieldInvalid}
|
||||
error={showFieldInvalid ? SELECT_POLICY_HOSTS : undefined}
|
||||
>
|
||||
<Controller
|
||||
name={AGENT_POLICY_FIELD_NAME}
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<SuperSelect
|
||||
fullWidth
|
||||
aria-label={SELECT_POLICY_HOSTS}
|
||||
placeholder={SELECT_POLICY_HOSTS}
|
||||
valueOfSelected={field.value}
|
||||
itemLayoutAlign="top"
|
||||
popoverProps={{ repositionOnScroll: true }}
|
||||
hasDividers
|
||||
isInvalid={showFieldInvalid}
|
||||
options={policyHostsOptions ?? []}
|
||||
{...field}
|
||||
onBlur={async () => {
|
||||
await trigger();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<>
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
label={POLICY_HOST_LABEL}
|
||||
labelAppend={
|
||||
<EuiButtonEmpty size="xs" href={basePath + '/app/fleet/policies?create'}>
|
||||
{i18n.translate('xpack.synthetics.policyHostsField.createButtonEmptyLabel', {
|
||||
defaultMessage: 'Create policy',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
}
|
||||
helpText={showFieldInvalid ? SELECT_POLICY_HOSTS_HELP_TEXT : undefined}
|
||||
isInvalid={showFieldInvalid}
|
||||
error={showFieldInvalid ? SELECT_POLICY_HOSTS : undefined}
|
||||
>
|
||||
<Controller
|
||||
name={AGENT_POLICY_FIELD_NAME}
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<SuperSelect
|
||||
fullWidth
|
||||
aria-label={SELECT_POLICY_HOSTS}
|
||||
placeholder={SELECT_POLICY_HOSTS}
|
||||
valueOfSelected={field.value}
|
||||
itemLayoutAlign="top"
|
||||
popoverProps={{ repositionOnScroll: true }}
|
||||
hasDividers
|
||||
isInvalid={showFieldInvalid}
|
||||
options={policyHostsOptions ?? []}
|
||||
{...field}
|
||||
onBlur={async () => {
|
||||
await trigger();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiSpacer />
|
||||
{selectedPolicy?.agents === 0 && <AgentPolicyCallout />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import { useSelector } from 'react-redux';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { useSyntheticsSettingsContext } from '../../../contexts';
|
||||
import { useFleetPermissions } from '../../../hooks';
|
||||
import { selectAgentPolicies } from '../../../state/private_locations';
|
||||
import { selectAgentPolicies } from '../../../state/agent_policies';
|
||||
|
||||
export const PolicyName = ({ agentPolicyId }: { agentPolicyId: string }) => {
|
||||
const { canReadAgentPolicies } = useFleetPermissions();
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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 { AgentPolicyInfo } from '../../../../../common/types';
|
||||
import { createAsyncAction } from '../utils/actions';
|
||||
|
||||
export const getAgentPoliciesAction = createAsyncAction<void, AgentPolicyInfo[]>(
|
||||
'[AGENT POLICIES] GET'
|
||||
);
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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 { NewLocation } from '../../components/settings/private_locations/add_location_flyout';
|
||||
import { AgentPolicyInfo } from '../../../../../common/types';
|
||||
import { INITIAL_REST_VERSION, SYNTHETICS_API_URLS } from '../../../../../common/constants';
|
||||
import { SyntheticsPrivateLocations } from '../../../../../common/runtime_types';
|
||||
import { apiService } from '../../../../utils/api_service/api_service';
|
||||
|
||||
export const fetchAgentPolicies = async (): Promise<AgentPolicyInfo[]> => {
|
||||
return await apiService.get(SYNTHETICS_API_URLS.AGENT_POLICIES);
|
||||
};
|
||||
|
||||
export const addSyntheticsPrivateLocations = async (
|
||||
newLocation: NewLocation
|
||||
): Promise<SyntheticsPrivateLocations> => {
|
||||
return await apiService.post(SYNTHETICS_API_URLS.PRIVATE_LOCATIONS, newLocation, undefined, {
|
||||
version: INITIAL_REST_VERSION,
|
||||
});
|
||||
};
|
||||
|
||||
export const getSyntheticsPrivateLocations = async (): Promise<SyntheticsPrivateLocations> => {
|
||||
return await apiService.get(SYNTHETICS_API_URLS.PRIVATE_LOCATIONS, {
|
||||
version: INITIAL_REST_VERSION,
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteSyntheticsPrivateLocations = async (
|
||||
locationId: string
|
||||
): Promise<SyntheticsPrivateLocations> => {
|
||||
return await apiService.delete(SYNTHETICS_API_URLS.PRIVATE_LOCATIONS + `/${locationId}`, {
|
||||
version: INITIAL_REST_VERSION,
|
||||
});
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 { takeLeading } from 'redux-saga/effects';
|
||||
import { fetchEffectFactory } from '../utils/fetch_effect';
|
||||
import { fetchAgentPolicies } from './api';
|
||||
import { getAgentPoliciesAction } from './actions';
|
||||
|
||||
export function* fetchAgentPoliciesEffect() {
|
||||
yield takeLeading(
|
||||
getAgentPoliciesAction.get,
|
||||
fetchEffectFactory(
|
||||
fetchAgentPolicies,
|
||||
getAgentPoliciesAction.success,
|
||||
getAgentPoliciesAction.fail
|
||||
)
|
||||
);
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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 { createReducer } from '@reduxjs/toolkit';
|
||||
import { AgentPolicyInfo } from '../../../../../common/types';
|
||||
import { IHttpSerializedFetchError } from '..';
|
||||
import { getAgentPoliciesAction } from './actions';
|
||||
|
||||
export interface AgentPoliciesState {
|
||||
data: AgentPolicyInfo[] | null;
|
||||
loading: boolean;
|
||||
error: IHttpSerializedFetchError | null;
|
||||
}
|
||||
|
||||
const initialState: AgentPoliciesState = {
|
||||
data: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
export const agentPoliciesReducer = createReducer(initialState, (builder) => {
|
||||
builder
|
||||
.addCase(getAgentPoliciesAction.get, (state) => {
|
||||
state.loading = true;
|
||||
})
|
||||
.addCase(getAgentPoliciesAction.success, (state, action) => {
|
||||
state.data = action.payload;
|
||||
state.loading = false;
|
||||
})
|
||||
.addCase(getAgentPoliciesAction.fail, (state, action) => {
|
||||
state.error = action.payload;
|
||||
state.loading = false;
|
||||
});
|
||||
});
|
||||
|
||||
export * from './actions';
|
||||
export * from './effects';
|
||||
export * from './selectors';
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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 { createSelector } from 'reselect';
|
||||
import { AppState } from '..';
|
||||
|
||||
const getState = (appState: AppState) => appState.agentPolicies;
|
||||
export const selectAgentPolicies = createSelector(getState, (state) => state);
|
|
@ -6,18 +6,24 @@
|
|||
*/
|
||||
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
import { SyntheticsPrivateLocations } from '../../../../../common/runtime_types';
|
||||
import { AgentPolicyInfo } from '../../../../../common/types';
|
||||
import { NewLocation } from '../../components/settings/private_locations/add_location_flyout';
|
||||
import { PrivateLocation, SyntheticsPrivateLocations } from '../../../../../common/runtime_types';
|
||||
import { createAsyncAction } from '../utils/actions';
|
||||
|
||||
export const getAgentPoliciesAction = createAsyncAction<void, AgentPolicyInfo[]>(
|
||||
'[AGENT POLICIES] GET'
|
||||
);
|
||||
|
||||
export const getPrivateLocationsAction = createAsyncAction<void, SyntheticsPrivateLocations>(
|
||||
'[PRIVATE LOCATIONS] GET'
|
||||
);
|
||||
|
||||
export const createPrivateLocationAction = createAsyncAction<NewLocation, PrivateLocation>(
|
||||
'CREATE PRIVATE LOCATION'
|
||||
);
|
||||
|
||||
export const deletePrivateLocationAction = createAsyncAction<string, SyntheticsPrivateLocations>(
|
||||
'DELETE PRIVATE LOCATION'
|
||||
);
|
||||
|
||||
export const setManageFlyoutOpen = createAction<boolean>('SET MANAGE FLYOUT OPEN');
|
||||
|
||||
export const setAddingNewPrivateLocation = createAction<boolean>('SET MANAGE FLYOUT ADDING NEW');
|
||||
export const setIsCreatePrivateLocationFlyoutVisible = createAction<boolean>(
|
||||
'SET IS CREATE PRIVATE LOCATION FLYOUT VISIBLE'
|
||||
);
|
||||
|
|
|
@ -8,16 +8,16 @@
|
|||
import { NewLocation } from '../../components/settings/private_locations/add_location_flyout';
|
||||
import { AgentPolicyInfo } from '../../../../../common/types';
|
||||
import { INITIAL_REST_VERSION, SYNTHETICS_API_URLS } from '../../../../../common/constants';
|
||||
import { SyntheticsPrivateLocations } from '../../../../../common/runtime_types';
|
||||
import { PrivateLocation, SyntheticsPrivateLocations } from '../../../../../common/runtime_types';
|
||||
import { apiService } from '../../../../utils/api_service/api_service';
|
||||
|
||||
export const fetchAgentPolicies = async (): Promise<AgentPolicyInfo[]> => {
|
||||
return await apiService.get(SYNTHETICS_API_URLS.AGENT_POLICIES);
|
||||
};
|
||||
|
||||
export const addSyntheticsPrivateLocations = async (
|
||||
export const createSyntheticsPrivateLocation = async (
|
||||
newLocation: NewLocation
|
||||
): Promise<SyntheticsPrivateLocations> => {
|
||||
): Promise<PrivateLocation> => {
|
||||
return await apiService.post(SYNTHETICS_API_URLS.PRIVATE_LOCATIONS, newLocation, undefined, {
|
||||
version: INITIAL_REST_VERSION,
|
||||
});
|
||||
|
@ -29,7 +29,7 @@ export const getSyntheticsPrivateLocations = async (): Promise<SyntheticsPrivate
|
|||
});
|
||||
};
|
||||
|
||||
export const deleteSyntheticsPrivateLocations = async (
|
||||
export const deleteSyntheticsPrivateLocation = async (
|
||||
locationId: string
|
||||
): Promise<SyntheticsPrivateLocations> => {
|
||||
return await apiService.delete(SYNTHETICS_API_URLS.PRIVATE_LOCATIONS + `/${locationId}`, {
|
||||
|
|
|
@ -6,20 +6,18 @@
|
|||
*/
|
||||
|
||||
import { takeLeading } from 'redux-saga/effects';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { fetchEffectFactory } from '../utils/fetch_effect';
|
||||
import { fetchAgentPolicies, getSyntheticsPrivateLocations } from './api';
|
||||
import { getAgentPoliciesAction, getPrivateLocationsAction } from './actions';
|
||||
|
||||
export function* fetchAgentPoliciesEffect() {
|
||||
yield takeLeading(
|
||||
getAgentPoliciesAction.get,
|
||||
fetchEffectFactory(
|
||||
fetchAgentPolicies,
|
||||
getAgentPoliciesAction.success,
|
||||
getAgentPoliciesAction.fail
|
||||
)
|
||||
);
|
||||
}
|
||||
import {
|
||||
createSyntheticsPrivateLocation,
|
||||
deleteSyntheticsPrivateLocation,
|
||||
getSyntheticsPrivateLocations,
|
||||
} from './api';
|
||||
import {
|
||||
createPrivateLocationAction,
|
||||
deletePrivateLocationAction,
|
||||
getPrivateLocationsAction,
|
||||
} from './actions';
|
||||
|
||||
export function* fetchPrivateLocationsEffect() {
|
||||
yield takeLeading(
|
||||
|
@ -31,3 +29,37 @@ export function* fetchPrivateLocationsEffect() {
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function* createPrivateLocationEffect() {
|
||||
yield takeLeading(
|
||||
createPrivateLocationAction.get,
|
||||
fetchEffectFactory(
|
||||
createSyntheticsPrivateLocation,
|
||||
createPrivateLocationAction.success,
|
||||
createPrivateLocationAction.fail,
|
||||
i18n.translate('xpack.synthetics.createPrivateLocationSuccess', {
|
||||
defaultMessage: 'Successfully created private location.',
|
||||
}),
|
||||
i18n.translate('xpack.synthetics.createPrivateLocationFailure', {
|
||||
defaultMessage: 'Failed to create private location.',
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function* deletePrivateLocationEffect() {
|
||||
yield takeLeading(
|
||||
deletePrivateLocationAction.get,
|
||||
fetchEffectFactory(
|
||||
deleteSyntheticsPrivateLocation,
|
||||
deletePrivateLocationAction.success,
|
||||
deletePrivateLocationAction.fail
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export const privateLocationsEffects = [
|
||||
fetchPrivateLocationsEffect,
|
||||
createPrivateLocationEffect,
|
||||
deletePrivateLocationEffect,
|
||||
];
|
||||
|
|
|
@ -6,62 +6,69 @@
|
|||
*/
|
||||
|
||||
import { createReducer } from '@reduxjs/toolkit';
|
||||
import { SyntheticsPrivateLocations } from '../../../../../common/runtime_types';
|
||||
import { AgentPolicyInfo } from '../../../../../common/types';
|
||||
import { IHttpSerializedFetchError } from '..';
|
||||
import {
|
||||
getAgentPoliciesAction,
|
||||
setAddingNewPrivateLocation,
|
||||
getPrivateLocationsAction,
|
||||
} from './actions';
|
||||
import { PrivateLocation, SyntheticsPrivateLocations } from '../../../../../common/runtime_types';
|
||||
import { createPrivateLocationAction, deletePrivateLocationAction } from './actions';
|
||||
import { setIsCreatePrivateLocationFlyoutVisible, getPrivateLocationsAction } from './actions';
|
||||
import { IHttpSerializedFetchError } from '../utils/http_error';
|
||||
|
||||
export interface AgentPoliciesState {
|
||||
data: AgentPolicyInfo[] | null;
|
||||
privateLocations?: SyntheticsPrivateLocations | null;
|
||||
export interface PrivateLocationsState {
|
||||
data?: SyntheticsPrivateLocations | null;
|
||||
loading: boolean;
|
||||
fetchLoading?: boolean;
|
||||
createLoading?: boolean;
|
||||
deleteLoading?: boolean;
|
||||
error: IHttpSerializedFetchError | null;
|
||||
isManageFlyoutOpen?: boolean;
|
||||
isAddingNewPrivateLocation?: boolean;
|
||||
isCreatePrivateLocationFlyoutVisible?: boolean;
|
||||
newLocation?: PrivateLocation;
|
||||
}
|
||||
|
||||
const initialState: AgentPoliciesState = {
|
||||
const initialState: PrivateLocationsState = {
|
||||
data: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
isManageFlyoutOpen: false,
|
||||
isAddingNewPrivateLocation: false,
|
||||
isCreatePrivateLocationFlyoutVisible: false,
|
||||
createLoading: false,
|
||||
};
|
||||
|
||||
export const agentPoliciesReducer = createReducer(initialState, (builder) => {
|
||||
export const privateLocationsStateReducer = createReducer(initialState, (builder) => {
|
||||
builder
|
||||
.addCase(getAgentPoliciesAction.get, (state) => {
|
||||
.addCase(getPrivateLocationsAction.get, (state) => {
|
||||
state.loading = true;
|
||||
})
|
||||
.addCase(getAgentPoliciesAction.success, (state, action) => {
|
||||
.addCase(getPrivateLocationsAction.success, (state, action) => {
|
||||
state.data = action.payload;
|
||||
state.loading = false;
|
||||
})
|
||||
.addCase(getAgentPoliciesAction.fail, (state, action) => {
|
||||
.addCase(getPrivateLocationsAction.fail, (state, action) => {
|
||||
state.error = action.payload;
|
||||
state.loading = false;
|
||||
})
|
||||
.addCase(getPrivateLocationsAction.get, (state) => {
|
||||
state.fetchLoading = true;
|
||||
.addCase(createPrivateLocationAction.get, (state) => {
|
||||
state.createLoading = true;
|
||||
})
|
||||
.addCase(getPrivateLocationsAction.success, (state, action) => {
|
||||
state.privateLocations = action.payload;
|
||||
state.fetchLoading = false;
|
||||
.addCase(createPrivateLocationAction.success, (state, action) => {
|
||||
state.newLocation = action.payload;
|
||||
state.createLoading = false;
|
||||
state.data = null;
|
||||
state.isCreatePrivateLocationFlyoutVisible = false;
|
||||
})
|
||||
.addCase(getPrivateLocationsAction.fail, (state, action) => {
|
||||
.addCase(createPrivateLocationAction.fail, (state, action) => {
|
||||
state.error = action.payload;
|
||||
state.fetchLoading = false;
|
||||
state.createLoading = false;
|
||||
})
|
||||
.addCase(setAddingNewPrivateLocation, (state, action) => {
|
||||
state.isAddingNewPrivateLocation = action.payload;
|
||||
.addCase(deletePrivateLocationAction.get, (state) => {
|
||||
state.deleteLoading = true;
|
||||
})
|
||||
.addCase(deletePrivateLocationAction.success, (state, action) => {
|
||||
state.deleteLoading = false;
|
||||
state.data = null;
|
||||
})
|
||||
.addCase(deletePrivateLocationAction.fail, (state, action) => {
|
||||
state.error = action.payload;
|
||||
state.deleteLoading = false;
|
||||
})
|
||||
.addCase(setIsCreatePrivateLocationFlyoutVisible, (state, action) => {
|
||||
state.isCreatePrivateLocationFlyoutVisible = action.payload;
|
||||
});
|
||||
});
|
||||
|
||||
export * from './actions';
|
||||
export * from './effects';
|
||||
export * from './selectors';
|
||||
|
|
|
@ -8,14 +8,21 @@
|
|||
import { createSelector } from 'reselect';
|
||||
import { AppState } from '..';
|
||||
|
||||
const getState = (appState: AppState) => appState.agentPolicies;
|
||||
const getState = (appState: AppState) => appState.privateLocations;
|
||||
export const selectAgentPolicies = createSelector(getState, (state) => state);
|
||||
|
||||
export const selectAddingNewPrivateLocation = (state: AppState) =>
|
||||
state.agentPolicies.isAddingNewPrivateLocation ?? false;
|
||||
state.privateLocations.isCreatePrivateLocationFlyoutVisible ?? false;
|
||||
|
||||
export const selectPrivateLocationsLoading = (state: AppState) =>
|
||||
state.agentPolicies.fetchLoading ?? false;
|
||||
state.privateLocations.loading ?? false;
|
||||
|
||||
export const selectPrivateLocations = (state: AppState) =>
|
||||
state.agentPolicies.privateLocations ?? [];
|
||||
export const selectPrivateLocationCreating = (state: AppState) =>
|
||||
state.privateLocations.createLoading ?? false;
|
||||
|
||||
export const selectPrivateLocationDeleting = (state: AppState) =>
|
||||
state.privateLocations.deleteLoading ?? false;
|
||||
|
||||
export const selectPrivateLocationsState = (state: AppState) => state.privateLocations;
|
||||
|
||||
export const selectPrivateLocations = (state: AppState) => state.privateLocations.data ?? [];
|
||||
|
|
|
@ -28,7 +28,7 @@ import {
|
|||
setDynamicSettingsEffect,
|
||||
} from './settings/effects';
|
||||
import { syncGlobalParamsEffect } from './settings';
|
||||
import { fetchAgentPoliciesEffect, fetchPrivateLocationsEffect } from './private_locations';
|
||||
import { privateLocationsEffects } from './private_locations/effects';
|
||||
import { fetchNetworkEventsEffect } from './network_events/effects';
|
||||
import { fetchSyntheticsMonitorEffect } from './monitor_details';
|
||||
import { fetchSyntheticsEnablementEffect } from './synthetics_enablement';
|
||||
|
@ -44,6 +44,7 @@ import { browserJourneyEffects, fetchJourneyStepsEffect } from './browser_journe
|
|||
import { fetchOverviewStatusEffect } from './overview_status';
|
||||
import { fetchMonitorStatusHeatmap, quietFetchMonitorStatusHeatmap } from './status_heatmap';
|
||||
import { fetchOverviewTrendStats, refreshOverviewTrendStats } from './overview/effects';
|
||||
import { fetchAgentPoliciesEffect } from './agent_policies';
|
||||
|
||||
export const rootEffect = function* root(): Generator {
|
||||
yield all([
|
||||
|
@ -57,7 +58,6 @@ export const rootEffect = function* root(): Generator {
|
|||
fork(fetchOverviewStatusEffect),
|
||||
fork(fetchNetworkEventsEffect),
|
||||
fork(fetchAgentPoliciesEffect),
|
||||
fork(fetchPrivateLocationsEffect),
|
||||
fork(fetchDynamicSettingsEffect),
|
||||
fork(fetchLocationMonitorsEffect),
|
||||
fork(setDynamicSettingsEffect),
|
||||
|
@ -80,5 +80,6 @@ export const rootEffect = function* root(): Generator {
|
|||
fork(quietFetchMonitorStatusHeatmap),
|
||||
fork(fetchOverviewTrendStats),
|
||||
fork(refreshOverviewTrendStats),
|
||||
...privateLocationsEffects.map((effect) => fork(effect)),
|
||||
]);
|
||||
};
|
||||
|
|
|
@ -21,7 +21,7 @@ import {
|
|||
SettingsState,
|
||||
} from './settings';
|
||||
import { elasticsearchReducer, QueriesState } from './elasticsearch';
|
||||
import { agentPoliciesReducer, AgentPoliciesState } from './private_locations';
|
||||
import { PrivateLocationsState, privateLocationsStateReducer } from './private_locations';
|
||||
import { networkEventsReducer, NetworkEventsState } from './network_events';
|
||||
import { monitorDetailsReducer, MonitorDetailsState } from './monitor_details';
|
||||
import { uiReducer, UiState } from './ui';
|
||||
|
@ -31,47 +31,50 @@ import { serviceLocationsReducer, ServiceLocationsState } from './service_locati
|
|||
import { monitorOverviewReducer, MonitorOverviewState } from './overview';
|
||||
import { BrowserJourneyState } from './browser_journey/models';
|
||||
import { monitorStatusHeatmapReducer, MonitorStatusHeatmap } from './status_heatmap';
|
||||
import { agentPoliciesReducer, AgentPoliciesState } from './agent_policies';
|
||||
|
||||
export interface SyntheticsAppState {
|
||||
ui: UiState;
|
||||
settings: SettingsState;
|
||||
elasticsearch: QueriesState;
|
||||
monitorList: MonitorListState;
|
||||
overview: MonitorOverviewState;
|
||||
certificates: CertificatesState;
|
||||
globalParams: GlobalParamsState;
|
||||
networkEvents: NetworkEventsState;
|
||||
agentPolicies: AgentPoliciesState;
|
||||
manualTestRuns: ManualTestRunsState;
|
||||
monitorDetails: MonitorDetailsState;
|
||||
browserJourney: BrowserJourneyState;
|
||||
certificates: CertificatesState;
|
||||
certsList: CertsListState;
|
||||
defaultAlerting: DefaultAlertingState;
|
||||
dynamicSettings: DynamicSettingsState;
|
||||
serviceLocations: ServiceLocationsState;
|
||||
overviewStatus: OverviewStatusStateReducer;
|
||||
syntheticsEnablement: SyntheticsEnablementState;
|
||||
elasticsearch: QueriesState;
|
||||
globalParams: GlobalParamsState;
|
||||
manualTestRuns: ManualTestRunsState;
|
||||
monitorDetails: MonitorDetailsState;
|
||||
monitorList: MonitorListState;
|
||||
monitorStatusHeatmap: MonitorStatusHeatmap;
|
||||
networkEvents: NetworkEventsState;
|
||||
overview: MonitorOverviewState;
|
||||
overviewStatus: OverviewStatusStateReducer;
|
||||
privateLocations: PrivateLocationsState;
|
||||
serviceLocations: ServiceLocationsState;
|
||||
settings: SettingsState;
|
||||
syntheticsEnablement: SyntheticsEnablementState;
|
||||
ui: UiState;
|
||||
}
|
||||
|
||||
export const rootReducer = combineReducers<SyntheticsAppState>({
|
||||
ui: uiReducer,
|
||||
settings: settingsReducer,
|
||||
monitorList: monitorListReducer,
|
||||
overview: monitorOverviewReducer,
|
||||
globalParams: globalParamsReducer,
|
||||
networkEvents: networkEventsReducer,
|
||||
elasticsearch: elasticsearchReducer,
|
||||
agentPolicies: agentPoliciesReducer,
|
||||
monitorDetails: monitorDetailsReducer,
|
||||
browserJourney: browserJourneyReducer,
|
||||
manualTestRuns: manualTestRunsReducer,
|
||||
overviewStatus: overviewStatusReducer,
|
||||
defaultAlerting: defaultAlertingReducer,
|
||||
dynamicSettings: dynamicSettingsReducer,
|
||||
serviceLocations: serviceLocationsReducer,
|
||||
syntheticsEnablement: syntheticsEnablementReducer,
|
||||
certificates: certificatesReducer,
|
||||
certsList: certsListReducer,
|
||||
defaultAlerting: defaultAlertingReducer,
|
||||
dynamicSettings: dynamicSettingsReducer,
|
||||
elasticsearch: elasticsearchReducer,
|
||||
globalParams: globalParamsReducer,
|
||||
manualTestRuns: manualTestRunsReducer,
|
||||
monitorDetails: monitorDetailsReducer,
|
||||
monitorList: monitorListReducer,
|
||||
monitorStatusHeatmap: monitorStatusHeatmapReducer,
|
||||
networkEvents: networkEventsReducer,
|
||||
overview: monitorOverviewReducer,
|
||||
overviewStatus: overviewStatusReducer,
|
||||
privateLocations: privateLocationsStateReducer,
|
||||
serviceLocations: serviceLocationsReducer,
|
||||
settings: settingsReducer,
|
||||
syntheticsEnablement: syntheticsEnablementReducer,
|
||||
ui: uiReducer,
|
||||
});
|
||||
|
|
|
@ -14,7 +14,7 @@ const getState = (appState: AppState) => appState.agentPolicies;
|
|||
export const selectAgentPolicies = createSelector(getState, (state) => state);
|
||||
|
||||
export const selectAddingNewPrivateLocation = (state: AppState) =>
|
||||
state.agentPolicies.isAddingNewPrivateLocation ?? false;
|
||||
state.privateLocations.isCreatePrivateLocationFlyoutVisible ?? false;
|
||||
|
||||
export const selectLocationMonitors = (state: AppState) => ({
|
||||
locationMonitors: state.dynamicSettings.locationMonitors,
|
||||
|
|
|
@ -114,6 +114,12 @@ export const mockState: SyntheticsAppState = {
|
|||
error: null,
|
||||
data: null,
|
||||
},
|
||||
privateLocations: {
|
||||
isCreatePrivateLocationFlyoutVisible: false,
|
||||
loading: false,
|
||||
error: null,
|
||||
data: [],
|
||||
},
|
||||
settings: {
|
||||
loading: false,
|
||||
error: null,
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
import type { ISavedObjectsRepository } from '@kbn/core-saved-objects-api-server';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { getAgentPoliciesAsInternalUser } from '../routes/settings/private_locations/get_agent_policies';
|
||||
import { PrivateLocationAttributes } from '../runtime_types/private_locations';
|
||||
import { PrivateLocationObject } from '../routes/settings/private_locations/add_private_location';
|
||||
import { RouteContext } from '../routes/types';
|
||||
import { privateLocationSavedObjectName } from '../../common/saved_objects/private_locations';
|
||||
|
||||
export class PrivateLocationRepository {
|
||||
internalSOClient: ISavedObjectsRepository;
|
||||
constructor(private routeContext: RouteContext) {
|
||||
const { server } = routeContext;
|
||||
this.internalSOClient = server.coreStart.savedObjects.createInternalRepository();
|
||||
}
|
||||
|
||||
async createPrivateLocation(formattedLocation: PrivateLocationAttributes, newId: string) {
|
||||
const { savedObjectsClient } = this.routeContext;
|
||||
const { spaces } = formattedLocation;
|
||||
|
||||
return await savedObjectsClient.create<PrivateLocationAttributes>(
|
||||
privateLocationSavedObjectName,
|
||||
formattedLocation,
|
||||
{
|
||||
id: newId,
|
||||
initialNamespaces: isEmpty(spaces) || spaces?.includes('*') ? ['*'] : spaces,
|
||||
}
|
||||
);
|
||||
}
|
||||
async validatePrivateLocation() {
|
||||
const { response, request, server } = this.routeContext;
|
||||
|
||||
let errorMessages = '';
|
||||
|
||||
const location = request.body as PrivateLocationObject;
|
||||
|
||||
const { spaces } = location;
|
||||
|
||||
const [data, agentPolicies] = await Promise.all([
|
||||
this.internalSOClient.find<PrivateLocationAttributes>({
|
||||
type: privateLocationSavedObjectName,
|
||||
perPage: 10000,
|
||||
namespaces: spaces,
|
||||
}),
|
||||
await getAgentPoliciesAsInternalUser({ server }),
|
||||
]);
|
||||
|
||||
const locations = data.saved_objects.map((loc) => ({
|
||||
...loc.attributes,
|
||||
spaces: loc.attributes.spaces || loc.namespaces,
|
||||
}));
|
||||
|
||||
const locWithAgentPolicyId = locations.find(
|
||||
(loc) => loc.agentPolicyId === location.agentPolicyId
|
||||
);
|
||||
|
||||
if (locWithAgentPolicyId) {
|
||||
errorMessages = i18n.translate(
|
||||
'xpack.synthetics.privateLocations.create.errorMessages.policyExists',
|
||||
{
|
||||
defaultMessage: `Private location with agentPolicyId {agentPolicyId} already exists in spaces {spaces}`,
|
||||
values: {
|
||||
agentPolicyId: location.agentPolicyId,
|
||||
spaces: formatSpaces(locWithAgentPolicyId.spaces),
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// return if name is already taken
|
||||
const locWithSameLabel = locations.find((loc) => loc.label === location.label);
|
||||
if (locWithSameLabel) {
|
||||
errorMessages = i18n.translate(
|
||||
'xpack.synthetics.privateLocations.create.errorMessages.labelExists',
|
||||
{
|
||||
defaultMessage: `Private location with label {label} already exists in spaces: {spaces}`,
|
||||
values: { label: location.label, spaces: formatSpaces(locWithSameLabel.spaces) },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const agentPolicy = agentPolicies?.find((policy) => policy.id === location.agentPolicyId);
|
||||
if (!agentPolicy) {
|
||||
errorMessages = `Agent policy with id ${location.agentPolicyId} does not exist`;
|
||||
}
|
||||
if (errorMessages) {
|
||||
return response.badRequest({
|
||||
body: {
|
||||
message: errorMessages,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const formatSpaces = (spaces: string[] | undefined) => {
|
||||
return (
|
||||
spaces
|
||||
?.map((space) =>
|
||||
space === '*'
|
||||
? i18n.translate('xpack.synthetics.formatSpaces.', { defaultMessage: '* All Spaces' })
|
||||
: space
|
||||
)
|
||||
.join(', ') ?? 'Unknown'
|
||||
);
|
||||
};
|
|
@ -6,13 +6,13 @@
|
|||
*/
|
||||
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
import { SavedObjectsErrorHelpers } from '@kbn/core/server';
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
import { PrivateLocationRepository } from '../../../repositories/private_location_repository';
|
||||
import { PRIVATE_LOCATION_WRITE_API } from '../../../feature';
|
||||
import { migrateLegacyPrivateLocations } from './migrate_legacy_private_locations';
|
||||
import { SyntheticsRestApiRouteFactory } from '../../types';
|
||||
import { getPrivateLocationsAndAgentPolicies } from './get_private_locations';
|
||||
import { privateLocationSavedObjectName } from '../../../../common/saved_objects/private_locations';
|
||||
import { SYNTHETICS_API_URLS } from '../../../../common/constants';
|
||||
import { PrivateLocationAttributes } from '../../../runtime_types/private_locations';
|
||||
import { toClientContract, toSavedObjectContract } from './helpers';
|
||||
import { PrivateLocation } from '../../../../common/runtime_types';
|
||||
|
||||
|
@ -26,6 +26,11 @@ export const PrivateLocationSchema = schema.object({
|
|||
lon: schema.number(),
|
||||
})
|
||||
),
|
||||
spaces: schema.maybe(
|
||||
schema.arrayOf(schema.string(), {
|
||||
minSize: 1,
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
export type PrivateLocationObject = TypeOf<typeof PrivateLocationSchema>;
|
||||
|
@ -41,60 +46,43 @@ export const addPrivateLocationRoute: SyntheticsRestApiRouteFactory<PrivateLocat
|
|||
},
|
||||
requiredPrivileges: [PRIVATE_LOCATION_WRITE_API],
|
||||
handler: async (routeContext) => {
|
||||
const { response, request, savedObjectsClient, syntheticsMonitorClient, server } = routeContext;
|
||||
const { response, request, server } = routeContext;
|
||||
const internalSOClient = server.coreStart.savedObjects.createInternalRepository();
|
||||
|
||||
await migrateLegacyPrivateLocations(internalSOClient, server.logger);
|
||||
|
||||
const repo = new PrivateLocationRepository(routeContext);
|
||||
|
||||
const invalidError = await repo.validatePrivateLocation();
|
||||
if (invalidError) {
|
||||
return invalidError;
|
||||
}
|
||||
|
||||
const location = request.body as PrivateLocationObject;
|
||||
const newId = uuidV4();
|
||||
const formattedLocation = toSavedObjectContract({ ...location, id: newId });
|
||||
const { spaces } = location;
|
||||
|
||||
const { locations, agentPolicies } = await getPrivateLocationsAndAgentPolicies(
|
||||
savedObjectsClient,
|
||||
syntheticsMonitorClient
|
||||
);
|
||||
try {
|
||||
const result = await repo.createPrivateLocation(formattedLocation, newId);
|
||||
|
||||
if (locations.find((loc) => loc.agentPolicyId === location.agentPolicyId)) {
|
||||
return response.badRequest({
|
||||
body: {
|
||||
message: `Private location with agentPolicyId ${location.agentPolicyId} already exists`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// return if name is already taken
|
||||
if (locations.find((loc) => loc.label === location.label)) {
|
||||
return response.badRequest({
|
||||
body: {
|
||||
message: `Private location with label ${location.label} already exists`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const formattedLocation = toSavedObjectContract({
|
||||
...location,
|
||||
id: location.agentPolicyId,
|
||||
});
|
||||
|
||||
const agentPolicy = agentPolicies?.find((policy) => policy.id === location.agentPolicyId);
|
||||
if (!agentPolicy) {
|
||||
return response.badRequest({
|
||||
body: {
|
||||
message: `Agent policy with id ${location.agentPolicyId} does not exist`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const soClient = routeContext.server.coreStart.savedObjects.createInternalRepository();
|
||||
|
||||
const result = await soClient.create<PrivateLocationAttributes>(
|
||||
privateLocationSavedObjectName,
|
||||
formattedLocation,
|
||||
{
|
||||
id: location.agentPolicyId,
|
||||
initialNamespaces: ['*'],
|
||||
return toClientContract(result);
|
||||
} catch (error) {
|
||||
if (SavedObjectsErrorHelpers.isForbiddenError(error)) {
|
||||
if (spaces?.includes('*')) {
|
||||
return response.badRequest({
|
||||
body: {
|
||||
message: `You do not have permission to create a location in all spaces.`,
|
||||
},
|
||||
});
|
||||
}
|
||||
return response.customError({
|
||||
statusCode: error.output.statusCode,
|
||||
body: {
|
||||
message: error.message,
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return toClientContract(result.attributes, agentPolicies);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { SavedObject } from '@kbn/core/server';
|
||||
import { AgentPolicyInfo } from '../../../../common/types';
|
||||
import type { SyntheticsPrivateLocations } from '../../../../common/runtime_types';
|
||||
import type {
|
||||
|
@ -13,18 +14,18 @@ import type {
|
|||
import { PrivateLocation } from '../../../../common/runtime_types';
|
||||
|
||||
export const toClientContract = (
|
||||
location: PrivateLocationAttributes,
|
||||
agentPolicies?: AgentPolicyInfo[]
|
||||
locationObject: SavedObject<PrivateLocationAttributes>
|
||||
): PrivateLocation => {
|
||||
const agPolicy = agentPolicies?.find((policy) => policy.id === location.agentPolicyId);
|
||||
const location = locationObject.attributes;
|
||||
return {
|
||||
label: location.label,
|
||||
id: location.id,
|
||||
agentPolicyId: location.agentPolicyId,
|
||||
isServiceManaged: false,
|
||||
isInvalid: !Boolean(agPolicy),
|
||||
isInvalid: false,
|
||||
tags: location.tags,
|
||||
geo: location.geo,
|
||||
spaces: locationObject.namespaces,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -42,6 +43,7 @@ export const allLocationsToClientContract = (
|
|||
isInvalid: !Boolean(agPolicy),
|
||||
tags: location.tags,
|
||||
geo: location.geo,
|
||||
spaces: location.spaces,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
@ -55,5 +57,6 @@ export const toSavedObjectContract = (location: PrivateLocation): PrivateLocatio
|
|||
isServiceManaged: false,
|
||||
geo: location.geo,
|
||||
namespace: location.namespace,
|
||||
spaces: location.spaces,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -25,22 +25,18 @@ export const getPrivateLocations = async (
|
|||
client: SavedObjectsClientContract
|
||||
): Promise<SyntheticsPrivateLocationsAttributes['locations']> => {
|
||||
try {
|
||||
const finder = client.createPointInTimeFinder<PrivateLocationAttributes>({
|
||||
type: privateLocationSavedObjectName,
|
||||
perPage: 1000,
|
||||
});
|
||||
const [results, legacyLocations] = await Promise.all([
|
||||
getNewPrivateLocations(client),
|
||||
getLegacyPrivateLocations(client),
|
||||
]);
|
||||
|
||||
const results: Array<SavedObject<PrivateLocationAttributes>> = [];
|
||||
|
||||
for await (const response of finder.find()) {
|
||||
results.push(...response.saved_objects);
|
||||
}
|
||||
|
||||
finder.close().catch((e) => {});
|
||||
|
||||
const legacyLocations = await getLegacyPrivateLocations(client);
|
||||
|
||||
return uniqBy([...results.map((r) => r.attributes), ...legacyLocations], 'id');
|
||||
return uniqBy(
|
||||
[
|
||||
...results.map((r) => ({ ...r.attributes, spaces: r.namespaces, id: r.id })),
|
||||
...legacyLocations,
|
||||
],
|
||||
'id'
|
||||
);
|
||||
} catch (getErr) {
|
||||
if (SavedObjectsErrorHelpers.isNotFoundError(getErr)) {
|
||||
return [];
|
||||
|
@ -49,6 +45,22 @@ export const getPrivateLocations = async (
|
|||
}
|
||||
};
|
||||
|
||||
const getNewPrivateLocations = async (client: SavedObjectsClientContract) => {
|
||||
const finder = client.createPointInTimeFinder<PrivateLocationAttributes>({
|
||||
type: privateLocationSavedObjectName,
|
||||
perPage: 1000,
|
||||
});
|
||||
|
||||
const results: Array<SavedObject<PrivateLocationAttributes>> = [];
|
||||
|
||||
for await (const response of finder.find()) {
|
||||
results.push(...response.saved_objects);
|
||||
}
|
||||
|
||||
finder.close().catch((e) => {});
|
||||
return results;
|
||||
};
|
||||
|
||||
const getLegacyPrivateLocations = async (client: SavedObjectsClientContract) => {
|
||||
try {
|
||||
const obj = await client.get<SyntheticsPrivateLocationsAttributes>(
|
||||
|
|
|
@ -217,10 +217,9 @@ export const getMonitorLocations = ({
|
|||
}) || [];
|
||||
const privateLocs =
|
||||
monitorLocations.privateLocations?.map((locationName) => {
|
||||
const loc = locationName.toLowerCase();
|
||||
const locationFound = allPrivateLocations.find(
|
||||
(location) =>
|
||||
location.label.toLowerCase() === locationName.toLowerCase() ||
|
||||
location.id.toLowerCase() === locationName.toLowerCase()
|
||||
(location) => location.label.toLowerCase() === loc || location.id.toLowerCase() === loc
|
||||
);
|
||||
if (locationFound) {
|
||||
return locationFound;
|
||||
|
|
|
@ -71,7 +71,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
it('add a test private location', async () => {
|
||||
pvtLoc = await testPrivateLocations.addPrivateLocation();
|
||||
testFleetPolicyID = pvtLoc.id;
|
||||
testFleetPolicyID = pvtLoc.agentPolicyId;
|
||||
|
||||
const apiResponse = await supertestAPI.get(SYNTHETICS_API_URLS.SERVICE_LOCATIONS);
|
||||
|
||||
|
@ -124,7 +124,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
it('adds a monitor in private location', async () => {
|
||||
const newMonitor = httpMonitorJson;
|
||||
|
||||
newMonitor.locations.push(pvtLoc);
|
||||
newMonitor.locations.push(omit(pvtLoc, ['spaces']));
|
||||
|
||||
const { body, rawBody } = await addMonitorAPI(newMonitor);
|
||||
|
||||
|
@ -138,8 +138,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
);
|
||||
|
||||
const packagePolicy = apiResponse.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id === newMonitorId + '-' + testFleetPolicyID + '-default'
|
||||
(pkgPolicy: PackagePolicy) => pkgPolicy.id === newMonitorId + '-' + pvtLoc.id + '-default'
|
||||
);
|
||||
|
||||
expect(packagePolicy?.policy_id).eql(testFleetPolicyID);
|
||||
|
@ -149,23 +148,33 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
getTestSyntheticsPolicy({
|
||||
name: httpMonitorJson.name,
|
||||
id: newMonitorId,
|
||||
location: { id: testFleetPolicyID },
|
||||
location: { id: pvtLoc.id },
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
let testFleetPolicyID2: string;
|
||||
let pvtLoc2: PrivateLocation;
|
||||
|
||||
it('edits a monitor with additional private location', async () => {
|
||||
const resPolicy = await testPrivateLocations.addFleetPolicy(testPolicyName + 1);
|
||||
testFleetPolicyID2 = resPolicy.body.item.id;
|
||||
|
||||
const pvtLoc2 = await testPrivateLocations.addPrivateLocation({
|
||||
pvtLoc2 = await testPrivateLocations.addPrivateLocation({
|
||||
policyId: testFleetPolicyID2,
|
||||
label: 'Test private location 1',
|
||||
});
|
||||
|
||||
httpMonitorJson.locations.push(pvtLoc2);
|
||||
httpMonitorJson.locations.push({
|
||||
id: pvtLoc2.id,
|
||||
label: pvtLoc2.label,
|
||||
isServiceManaged: false,
|
||||
agentPolicyId: pvtLoc2.agentPolicyId,
|
||||
geo: {
|
||||
lat: 0,
|
||||
lon: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const apiResponse = await supertestAPI
|
||||
.put(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '/' + newMonitorId)
|
||||
|
@ -191,8 +200,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
);
|
||||
|
||||
let packagePolicy = apiResponsePolicy.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id === newMonitorId + '-' + testFleetPolicyID + '-default'
|
||||
(pkgPolicy: PackagePolicy) => pkgPolicy.id === newMonitorId + '-' + pvtLoc.id + '-default'
|
||||
);
|
||||
|
||||
expect(packagePolicy.policy_id).eql(testFleetPolicyID);
|
||||
|
@ -202,13 +210,12 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
getTestSyntheticsPolicy({
|
||||
name: httpMonitorJson.name,
|
||||
id: newMonitorId,
|
||||
location: { id: testFleetPolicyID },
|
||||
location: { id: pvtLoc.id },
|
||||
})
|
||||
);
|
||||
|
||||
packagePolicy = apiResponsePolicy.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id === newMonitorId + '-' + testFleetPolicyID2 + '-default'
|
||||
(pkgPolicy: PackagePolicy) => pkgPolicy.id === newMonitorId + '-' + pvtLoc2.id + '-default'
|
||||
);
|
||||
|
||||
expect(packagePolicy.policy_id).eql(testFleetPolicyID2);
|
||||
|
@ -219,16 +226,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
id: newMonitorId,
|
||||
location: {
|
||||
name: 'Test private location 1',
|
||||
id: testFleetPolicyID2,
|
||||
id: pvtLoc2.id,
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('deletes integration for a removed location from monitor', async () => {
|
||||
httpMonitorJson.locations = httpMonitorJson.locations.filter(
|
||||
({ id }) => id !== testFleetPolicyID2
|
||||
);
|
||||
httpMonitorJson.locations = httpMonitorJson.locations.filter(({ id }) => id !== pvtLoc2.id);
|
||||
|
||||
await supertestAPI
|
||||
.put(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '/' + newMonitorId + '?internal=true')
|
||||
|
@ -241,8 +246,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
);
|
||||
|
||||
let packagePolicy = apiResponsePolicy.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id === newMonitorId + '-' + testFleetPolicyID + '-default'
|
||||
(pkgPolicy: PackagePolicy) => pkgPolicy.id === newMonitorId + '-' + pvtLoc.id + '-default'
|
||||
);
|
||||
|
||||
expect(packagePolicy.policy_id).eql(testFleetPolicyID);
|
||||
|
@ -252,13 +256,12 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
getTestSyntheticsPolicy({
|
||||
name: httpMonitorJson.name,
|
||||
id: newMonitorId,
|
||||
location: { id: testFleetPolicyID },
|
||||
location: { id: pvtLoc.id },
|
||||
})
|
||||
);
|
||||
|
||||
packagePolicy = apiResponsePolicy.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id === newMonitorId + '-' + testFleetPolicyID2 + '-default'
|
||||
(pkgPolicy: PackagePolicy) => pkgPolicy.id === newMonitorId + '-' + pvtLoc2.id + '-default'
|
||||
);
|
||||
|
||||
expect(packagePolicy).eql(undefined);
|
||||
|
@ -287,7 +290,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
...httpMonitorJson,
|
||||
name: `Test monitor ${uuidv4()}`,
|
||||
[ConfigKey.NAMESPACE]: 'default',
|
||||
locations: [pvtLoc],
|
||||
locations: [omit(pvtLoc, ['spaces'])],
|
||||
};
|
||||
|
||||
try {
|
||||
|
@ -316,7 +319,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
const packagePolicy = policyResponse.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id === monitorId + '-' + testFleetPolicyID + `-${SPACE_ID}`
|
||||
pkgPolicy.id === monitorId + '-' + pvtLoc.id + `-${SPACE_ID}`
|
||||
);
|
||||
|
||||
expect(packagePolicy.policy_id).eql(testFleetPolicyID);
|
||||
|
@ -326,7 +329,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
getTestSyntheticsPolicy({
|
||||
name: monitor.name,
|
||||
id: monitorId,
|
||||
location: { id: testFleetPolicyID },
|
||||
location: { id: pvtLoc.id },
|
||||
namespace: formatKibanaNamespace(SPACE_ID),
|
||||
spaceId: SPACE_ID,
|
||||
})
|
||||
|
@ -350,7 +353,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
...httpMonitorJson,
|
||||
locations: [
|
||||
{
|
||||
id: testFleetPolicyID,
|
||||
id: pvtLoc.id,
|
||||
label: 'Test private location 0',
|
||||
isServiceManaged: false,
|
||||
},
|
||||
|
@ -374,15 +377,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
);
|
||||
|
||||
const packagePolicy = policyResponse.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id === monitorId + '-' + testFleetPolicyID + `-default`
|
||||
(pkgPolicy: PackagePolicy) => pkgPolicy.id === monitorId + '-' + pvtLoc.id + `-default`
|
||||
);
|
||||
comparePolicies(
|
||||
packagePolicy,
|
||||
getTestSyntheticsPolicy({
|
||||
name: monitor.name,
|
||||
id: monitorId,
|
||||
location: { id: testFleetPolicyID },
|
||||
location: { id: pvtLoc.id },
|
||||
isTLSEnabled: true,
|
||||
})
|
||||
);
|
||||
|
@ -398,7 +400,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
...httpMonitorJson,
|
||||
locations: [
|
||||
{
|
||||
id: testFleetPolicyID,
|
||||
id: pvtLoc.id,
|
||||
label: 'Test private location 0',
|
||||
isServiceManaged: false,
|
||||
},
|
||||
|
@ -412,8 +414,9 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const apiResponse = await supertestAPI
|
||||
.post(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(monitor)
|
||||
.expect(200);
|
||||
.send(monitor);
|
||||
|
||||
expect(apiResponse.status).eql(200, JSON.stringify(apiResponse.body));
|
||||
|
||||
monitorId = apiResponse.body.id;
|
||||
|
||||
|
@ -422,15 +425,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
);
|
||||
|
||||
const packagePolicy = policyResponse.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id === monitorId + '-' + testFleetPolicyID + `-default`
|
||||
(pkgPolicy: PackagePolicy) => pkgPolicy.id === monitorId + '-' + pvtLoc.id + `-default`
|
||||
);
|
||||
comparePolicies(
|
||||
packagePolicy,
|
||||
getTestSyntheticsPolicy({
|
||||
name: monitor.name,
|
||||
id: monitorId,
|
||||
location: { id: testFleetPolicyID },
|
||||
location: { id: pvtLoc.id },
|
||||
})
|
||||
);
|
||||
} finally {
|
||||
|
@ -447,7 +449,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
[ConfigKey.NAMESPACE]: 'default',
|
||||
locations: [
|
||||
{
|
||||
id: testFleetPolicyID,
|
||||
id: pvtLoc.id,
|
||||
label: 'Test private location 0',
|
||||
isServiceManaged: false,
|
||||
},
|
||||
|
@ -467,8 +469,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
);
|
||||
|
||||
const packagePolicy = policyResponse.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id === monitorId + '-' + testFleetPolicyID + `-default`
|
||||
(pkgPolicy: PackagePolicy) => pkgPolicy.id === monitorId + '-' + pvtLoc.id + `-default`
|
||||
);
|
||||
|
||||
expect(packagePolicy.package.version).eql(INSTALLED_VERSION);
|
||||
|
@ -478,8 +479,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
'/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
|
||||
);
|
||||
const packagePolicyAfterUpgrade = policyResponseAfterUpgrade.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id === monitorId + '-' + testFleetPolicyID + `-default`
|
||||
(pkgPolicy: PackagePolicy) => pkgPolicy.id === monitorId + '-' + pvtLoc.id + `-default`
|
||||
);
|
||||
expect(semver.gte(packagePolicyAfterUpgrade.package.version, INSTALLED_VERSION)).eql(true);
|
||||
} finally {
|
||||
|
|
|
@ -46,6 +46,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
let icmpProjectMonitors: ProjectMonitorsRequest;
|
||||
|
||||
let testPolicyId = '';
|
||||
let loc: any;
|
||||
const setUniqueIds = (request: ProjectMonitorsRequest) => {
|
||||
return {
|
||||
...request,
|
||||
|
@ -85,8 +86,8 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
.expect(200);
|
||||
await testPrivateLocations.installSyntheticsPackage();
|
||||
|
||||
const loc = await testPrivateLocations.addPrivateLocation();
|
||||
testPolicyId = loc.id;
|
||||
loc = await testPrivateLocations.addPrivateLocation();
|
||||
testPolicyId = loc.agentPolicyId;
|
||||
await supertest
|
||||
.post(SYNTHETICS_API_URLS.PARAMS)
|
||||
.set('kbn-xsrf', 'true')
|
||||
|
@ -644,7 +645,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
lat: 0,
|
||||
lon: 0,
|
||||
},
|
||||
id: testPolicyId,
|
||||
id: loc.id,
|
||||
agentPolicyId: testPolicyId,
|
||||
isServiceManaged: false,
|
||||
label: 'Test private location 0',
|
||||
|
@ -1443,7 +1444,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const packagePolicy = apiResponsePolicy.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id ===
|
||||
`${monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID]}-${testPolicyId}`
|
||||
`${monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID]}-${loc.id}`
|
||||
);
|
||||
expect(packagePolicy.name).eql(
|
||||
`${projectMonitors.monitors[0].id}-${project}-default-Test private location 0`
|
||||
|
@ -1511,7 +1512,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const packagePolicy = apiResponsePolicy.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id ===
|
||||
`${monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID]}-${testPolicyId}`
|
||||
`${monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID]}-${loc.id}`
|
||||
);
|
||||
expect(packagePolicy.name).eql(
|
||||
`${httpProjectMonitors.monitors[1].id}-${project}-default-Test private location 0`
|
||||
|
@ -1530,7 +1531,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
configId,
|
||||
projectId: project,
|
||||
locationName: 'Test private location 0',
|
||||
locationId: testPolicyId,
|
||||
locationId: loc.id,
|
||||
})
|
||||
);
|
||||
} finally {
|
||||
|
@ -1575,7 +1576,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const packagePolicy = apiResponsePolicy.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id ===
|
||||
`${monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID]}-${testPolicyId}`
|
||||
`${monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID]}-${loc.id}`
|
||||
);
|
||||
|
||||
expect(packagePolicy.policy_id).eql(testPolicyId);
|
||||
|
@ -1597,7 +1598,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const packagePolicy2 = apiResponsePolicy2.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id ===
|
||||
`${monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID]}-${testPolicyId}`
|
||||
`${monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID]}-${loc.id}`
|
||||
);
|
||||
|
||||
expect(packagePolicy2).eql(undefined);
|
||||
|
@ -1637,7 +1638,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const packagePolicy = apiResponsePolicy.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id ===
|
||||
`${monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID]}-${testPolicyId}`
|
||||
`${monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID]}-${loc.id}`
|
||||
);
|
||||
|
||||
expect(packagePolicy.policy_id).eql(testPolicyId);
|
||||
|
@ -1721,7 +1722,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
const configId = monitorsResponse.body.monitors[0].id;
|
||||
const id = monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID];
|
||||
const policyId = `${id}-${testPolicyId}`;
|
||||
const policyId = `${id}-${loc.id}`;
|
||||
|
||||
const packagePolicy = apiResponsePolicy.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) => pkgPolicy.id === policyId
|
||||
|
@ -1737,7 +1738,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
id,
|
||||
configId,
|
||||
projectId: project,
|
||||
locationId: testPolicyId,
|
||||
locationId: loc.id,
|
||||
locationName: 'Test private location 0',
|
||||
})
|
||||
);
|
||||
|
@ -1764,7 +1765,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
const configId2 = monitorsResponse.body.monitors[0].id;
|
||||
const id2 = monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID];
|
||||
const policyId2 = `${id}-${testPolicyId}`;
|
||||
const policyId2 = `${id}-${loc.id}`;
|
||||
|
||||
const packagePolicy2 = apiResponsePolicy2.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) => pkgPolicy.id === policyId2
|
||||
|
@ -1778,7 +1779,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
id: id2,
|
||||
configId: configId2,
|
||||
projectId: project,
|
||||
locationId: testPolicyId,
|
||||
locationId: loc.id,
|
||||
locationName: 'Test private location 0',
|
||||
namespace: 'custom_namespace',
|
||||
})
|
||||
|
@ -1832,7 +1833,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
label: 'Test private location 0',
|
||||
isServiceManaged: false,
|
||||
agentPolicyId: testPolicyId,
|
||||
id: testPolicyId,
|
||||
id: loc.id,
|
||||
geo: {
|
||||
lat: 0,
|
||||
lon: 0,
|
||||
|
|
|
@ -27,6 +27,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
let projectMonitors: ProjectMonitorsRequest;
|
||||
|
||||
let testPolicyId = '';
|
||||
let loc: any;
|
||||
const testPrivateLocations = new PrivateLocationTestService(getService);
|
||||
|
||||
const setUniqueIds = (request: ProjectMonitorsRequest) => {
|
||||
|
@ -39,8 +40,8 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
before(async () => {
|
||||
await kibanaServer.savedObjects.cleanStandardList();
|
||||
await testPrivateLocations.installSyntheticsPackage();
|
||||
const loc = await testPrivateLocations.addPrivateLocation();
|
||||
testPolicyId = loc.id;
|
||||
loc = await testPrivateLocations.addPrivateLocation();
|
||||
testPolicyId = loc.agentPolicyId;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -404,9 +405,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const packagePolicy = apiResponsePolicy.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id ===
|
||||
savedObjectsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID] +
|
||||
'-' +
|
||||
testPolicyId
|
||||
savedObjectsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID] + '-' + loc.id
|
||||
);
|
||||
expect(packagePolicy.policy_id).to.be(testPolicyId);
|
||||
|
||||
|
@ -438,9 +437,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const packagePolicy2 = apiResponsePolicy2.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id ===
|
||||
savedObjectsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID] +
|
||||
'-' +
|
||||
testPolicyId
|
||||
savedObjectsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID] + '-' + loc.id
|
||||
);
|
||||
expect(packagePolicy2).to.be(undefined);
|
||||
} finally {
|
||||
|
|
|
@ -65,7 +65,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
after(async () => {
|
||||
await kibanaServer.savedObjects.cleanStandardList();
|
||||
// await kibanaServer.savedObjects.cleanStandardList();
|
||||
});
|
||||
let monitorId = 'test-id';
|
||||
|
||||
|
@ -256,7 +256,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
...updates,
|
||||
revision: 3,
|
||||
url: 'https://www.google.com',
|
||||
locations: [localLoc, pvtLoc],
|
||||
locations: [localLoc, omit(pvtLoc, 'spaces')],
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -270,7 +270,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
...updates,
|
||||
revision: 4,
|
||||
url: 'https://www.google.com',
|
||||
locations: [pvtLoc],
|
||||
locations: [omit(pvtLoc, 'spaces')],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
@ -289,7 +289,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
...updates,
|
||||
revision: 5,
|
||||
url: 'https://www.google.com',
|
||||
locations: [localLoc, pvtLoc],
|
||||
locations: [localLoc, omit(pvtLoc, 'spaces')],
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
describe('PrivateLocationAPI', function () {
|
||||
this.tags('skipCloud');
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
const supertest = getService('supertest');
|
||||
|
||||
const kServer = getService('kibanaServer');
|
||||
|
||||
|
@ -132,5 +133,64 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(deleteResponse.status).to.be(200);
|
||||
}
|
||||
});
|
||||
|
||||
it('can create private location in multiple spaces', async () => {
|
||||
const apiResponse = await testPrivateLocations.addFleetPolicy();
|
||||
const agentPolicyId = apiResponse.body.item.id;
|
||||
|
||||
const { SPACE_ID } = await monitorTestService.addsNewSpace([
|
||||
'minimal_all',
|
||||
'can_manage_private_locations',
|
||||
]);
|
||||
|
||||
const location: Omit<PrivateLocation, 'id'> = {
|
||||
label: 'Test private location 11',
|
||||
agentPolicyId: agentPolicyId!,
|
||||
geo: {
|
||||
lat: 0,
|
||||
lon: 0,
|
||||
},
|
||||
spaces: [SPACE_ID, 'default'],
|
||||
};
|
||||
const response = await supertest
|
||||
.post(SYNTHETICS_API_URLS.PRIVATE_LOCATIONS)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(location);
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
});
|
||||
|
||||
it('validation errors works in multiple spaces as well', async () => {
|
||||
const apiResponse = await testPrivateLocations.addFleetPolicy();
|
||||
const agentPolicyId = apiResponse.body.item.id;
|
||||
|
||||
const { username, password, SPACE_ID } = await monitorTestService.addsNewSpace([
|
||||
'minimal_all',
|
||||
'can_manage_private_locations',
|
||||
]);
|
||||
|
||||
const location: Omit<PrivateLocation, 'id'> = {
|
||||
label: 'Test private location 12',
|
||||
agentPolicyId: agentPolicyId!,
|
||||
geo: {
|
||||
lat: 0,
|
||||
lon: 0,
|
||||
},
|
||||
spaces: [SPACE_ID, 'default'],
|
||||
};
|
||||
const response = await supertest
|
||||
.post(SYNTHETICS_API_URLS.PRIVATE_LOCATIONS)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(location);
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
|
||||
const response1 = await supertestWithoutAuth
|
||||
.post(SYNTHETICS_API_URLS.PRIVATE_LOCATIONS)
|
||||
.auth(username, password)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({ ...location, spaces: [SPACE_ID] });
|
||||
expect(response1.status).to.be(400);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const kServer = getService('kibanaServer');
|
||||
|
||||
let testFleetPolicyID: string;
|
||||
let loc: any;
|
||||
let _browserMonitorJson: HTTPFields;
|
||||
let browserMonitorJson: HTTPFields;
|
||||
|
||||
|
@ -61,8 +62,8 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('add a test private location', async () => {
|
||||
const loc = await testPrivateLocations.addPrivateLocation();
|
||||
testFleetPolicyID = loc.id;
|
||||
loc = await testPrivateLocations.addPrivateLocation();
|
||||
testFleetPolicyID = loc.agentPolicyId;
|
||||
|
||||
const apiResponse = await supertestAPI.get(SYNTHETICS_API_URLS.SERVICE_LOCATIONS);
|
||||
|
||||
|
@ -86,7 +87,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isInvalid: false,
|
||||
},
|
||||
{
|
||||
id: testFleetPolicyID,
|
||||
id: loc.id,
|
||||
isInvalid: false,
|
||||
isServiceManaged: false,
|
||||
label: 'Test private location 0',
|
||||
|
@ -95,6 +96,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
lon: 0,
|
||||
},
|
||||
agentPolicyId: testFleetPolicyID,
|
||||
spaces: ['*'],
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -105,7 +107,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const newMonitor = browserMonitorJson;
|
||||
|
||||
const pvtLoc = {
|
||||
id: testFleetPolicyID,
|
||||
id: loc.id,
|
||||
agentPolicyId: testFleetPolicyID,
|
||||
label: 'Test private location 0',
|
||||
isServiceManaged: false,
|
||||
|
@ -136,8 +138,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
);
|
||||
|
||||
const packagePolicy = apiResponse.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id === newMonitorId + '-' + testFleetPolicyID + '-default'
|
||||
(pkgPolicy: PackagePolicy) => pkgPolicy.id === newMonitorId + '-' + loc.id + '-default'
|
||||
);
|
||||
|
||||
expect(packagePolicy?.policy_id).eql(
|
||||
|
@ -193,8 +194,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
);
|
||||
|
||||
const packagePolicy = apiResponse.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id === newMonitorId + '-' + testFleetPolicyID + '-default'
|
||||
(pkgPolicy: PackagePolicy) => pkgPolicy.id === newMonitorId + '-' + loc.id + '-default'
|
||||
);
|
||||
|
||||
expect(packagePolicy.policy_id).eql(testFleetPolicyID);
|
||||
|
@ -214,7 +214,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
it('add a http monitor using param', async () => {
|
||||
const newMonitor = httpMonitorJson;
|
||||
const pvtLoc = {
|
||||
id: testFleetPolicyID,
|
||||
id: loc.id,
|
||||
agentPolicyId: testFleetPolicyID,
|
||||
label: 'Test private location 0',
|
||||
isServiceManaged: false,
|
||||
|
@ -246,8 +246,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
);
|
||||
|
||||
const packagePolicy = apiResponse.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id === newHttpMonitorId + '-' + testFleetPolicyID + '-default'
|
||||
(pkgPolicy: PackagePolicy) => pkgPolicy.id === newHttpMonitorId + '-' + loc.id + '-default'
|
||||
);
|
||||
|
||||
expect(packagePolicy.policy_id).eql(testFleetPolicyID);
|
||||
|
@ -257,7 +256,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
id: newHttpMonitorId,
|
||||
isTLSEnabled: false,
|
||||
namespace: 'testnamespace',
|
||||
location: { id: testFleetPolicyID },
|
||||
location: { id: loc.id },
|
||||
});
|
||||
|
||||
comparePolicies(packagePolicy, pPolicy);
|
||||
|
@ -304,8 +303,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
);
|
||||
|
||||
const packagePolicy = apiResponse.body.items.find(
|
||||
(pkgPolicy: PackagePolicy) =>
|
||||
pkgPolicy.id === newMonitorId + '-' + testFleetPolicyID + '-default'
|
||||
(pkgPolicy: PackagePolicy) => pkgPolicy.id === newMonitorId + '-' + loc.id + '-default'
|
||||
);
|
||||
|
||||
expect(packagePolicy.policy_id).eql(testFleetPolicyID);
|
||||
|
@ -316,7 +314,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
name: browserMonitorJson.name,
|
||||
id: newMonitorId,
|
||||
isBrowser: true,
|
||||
location: { id: testFleetPolicyID },
|
||||
location: { id: loc.id },
|
||||
})
|
||||
);
|
||||
});
|
||||
|
|
|
@ -97,6 +97,7 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
|
|||
lon: 0,
|
||||
},
|
||||
agentPolicyId: testFleetPolicyID,
|
||||
spaces: ['default'],
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -115,6 +116,7 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
|
|||
lat: 0,
|
||||
lon: 0,
|
||||
},
|
||||
spaces: ['default'],
|
||||
};
|
||||
|
||||
newMonitor.name = invalidName;
|
||||
|
@ -123,7 +125,7 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
|
|||
.post(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
|
||||
.set(editorUser.apiKeyHeader)
|
||||
.set(samlAuth.getInternalRequestHeader())
|
||||
.send({ ...newMonitor, locations: [location] })
|
||||
.send({ ...newMonitor, locations: [omit(location, 'spaces')] })
|
||||
.expect(400);
|
||||
|
||||
expect(apiResponse.body).eql({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue