-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Fleet] Implement Settings new design for fleet server hosts (#118385)
- Loading branch information
1 parent
4c37441
commit 8066b0a
Showing
17 changed files
with
689 additions
and
186 deletions.
There are no files selected for viewing
42 changes: 42 additions & 0 deletions
42
...plications/fleet/sections/settings/components/fleet_server_hosts_flyout/index.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 { addParameters } from '@storybook/react'; | ||
import React from 'react'; | ||
|
||
import { FleetServerHostsFlyout as Component } from '.'; | ||
|
||
addParameters({ | ||
docs: { | ||
inlineStories: false, | ||
}, | ||
}); | ||
export default { | ||
component: Component, | ||
title: 'Sections/Fleet/Settings', | ||
}; | ||
|
||
interface Args { | ||
width: number; | ||
} | ||
|
||
const args: Args = { | ||
width: 1200, | ||
}; | ||
|
||
export const FleetServerHostsFlyout = ({ width }: Args) => { | ||
return ( | ||
<div style={{ width }}> | ||
<Component | ||
onClose={() => {}} | ||
fleetServerHosts={['https://host1.fr:8220', 'https://host2-with-a-longer-name.fr:8220']} | ||
/> | ||
</div> | ||
); | ||
}; | ||
|
||
FleetServerHostsFlyout.args = args; |
106 changes: 106 additions & 0 deletions
106
...ublic/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/* | ||
* 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 from 'react'; | ||
import { FormattedMessage } from '@kbn/i18n/react'; | ||
import { | ||
EuiFlyout, | ||
EuiFlyoutBody, | ||
EuiFlyoutHeader, | ||
EuiTitle, | ||
EuiText, | ||
EuiLink, | ||
EuiFlyoutFooter, | ||
EuiFlexGroup, | ||
EuiFlexItem, | ||
EuiButtonEmpty, | ||
EuiButton, | ||
} from '@elastic/eui'; | ||
|
||
import { HostsInput } from '../hosts_input'; | ||
import { useStartServices } from '../../../../hooks'; | ||
|
||
import { useFleetServerHostsForm } from './use_fleet_server_host_form'; | ||
|
||
const FLYOUT_MAX_WIDTH = 800; | ||
|
||
export interface FleetServerHostsFlyoutProps { | ||
onClose: () => void; | ||
fleetServerHosts: string[]; | ||
} | ||
|
||
export const FleetServerHostsFlyout: React.FunctionComponent<FleetServerHostsFlyoutProps> = ({ | ||
onClose, | ||
fleetServerHosts, | ||
}) => { | ||
const { docLinks } = useStartServices(); | ||
|
||
const form = useFleetServerHostsForm(fleetServerHosts, onClose); | ||
|
||
return ( | ||
<EuiFlyout maxWidth={FLYOUT_MAX_WIDTH} onClose={onClose}> | ||
<EuiFlyoutHeader hasBorder={true}> | ||
<EuiTitle size="m"> | ||
<h2 id="FleetPackagePolicyPreviousVersionFlyoutTitle"> | ||
<FormattedMessage | ||
id="xpack.fleet.settings.fleetServerHostsFlyout.title" | ||
defaultMessage="Fleet Server hosts" | ||
/> | ||
</h2> | ||
</EuiTitle> | ||
</EuiFlyoutHeader> | ||
<EuiFlyoutBody> | ||
<EuiText color="subdued"> | ||
<FormattedMessage | ||
id="xpack.fleet.settings.fleetServerHostsFlyout.description" | ||
defaultMessage="Specify the URLs that your agents will use to connect to a Fleet Server. If multiple URLs exist, Fleet shows the first provided URL for enrollment purposes. Fleet Server uses port 8220 by default. Refer to the {link}." | ||
values={{ | ||
link: ( | ||
<EuiLink | ||
href={docLinks.links.fleet.settingsFleetServerHostSettings} | ||
target="_blank" | ||
external | ||
> | ||
<FormattedMessage | ||
id="xpack.fleet.settings.fleetServerHostsFlyout.userGuideLink" | ||
defaultMessage="Fleet and Elastic Agent Guide" | ||
/> | ||
</EuiLink> | ||
), | ||
}} | ||
/> | ||
</EuiText> | ||
<HostsInput {...form.fleetServerHostsInput.props} id="fleet-server-inputs" /> | ||
</EuiFlyoutBody> | ||
<EuiFlyoutFooter> | ||
<EuiFlexGroup justifyContent="spaceBetween"> | ||
<EuiFlexItem grow={false}> | ||
<EuiButtonEmpty onClick={() => onClose()} flush="left"> | ||
<FormattedMessage | ||
id="xpack.fleet.settings.fleetServerHostsFlyout.cancelButtonLabel" | ||
defaultMessage="Cancel" | ||
/> | ||
</EuiButtonEmpty> | ||
</EuiFlexItem> | ||
<EuiFlexItem grow={false}> | ||
<EuiButton | ||
fill | ||
isLoading={form.isLoading} | ||
isDisabled={form.isLoading} | ||
onClick={form.submit} | ||
> | ||
<FormattedMessage | ||
id="xpack.fleet.settings.fleetServerHostsFlyout.saveButton" | ||
defaultMessage="Save and apply settings" | ||
/> | ||
</EuiButton> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
</EuiFlyoutFooter> | ||
</EuiFlyout> | ||
); | ||
}; |
142 changes: 142 additions & 0 deletions
142
...eet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
/* | ||
* 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, { useCallback } from 'react'; | ||
|
||
import { i18n } from '@kbn/i18n'; | ||
|
||
import { sendPutSettings, useComboInput, useStartServices } from '../../../../hooks'; | ||
import { isDiffPathProtocol } from '../../../../../../../common'; | ||
import { useConfirmModal } from '../../hooks/use_confirm_modal'; | ||
|
||
const URL_REGEX = /^(https?):\/\/[^\s$.?#].[^\s]*$/gm; | ||
|
||
function validateFleetServerHosts(value: string[]) { | ||
if (value.length === 0) { | ||
return [ | ||
{ | ||
message: i18n.translate('xpack.fleet.settings.fleetServerHostsEmptyError', { | ||
defaultMessage: 'At least one URL is required', | ||
}), | ||
}, | ||
]; | ||
} | ||
|
||
const res: Array<{ message: string; index: number }> = []; | ||
const hostIndexes: { [key: string]: number[] } = {}; | ||
value.forEach((val, idx) => { | ||
if (!val.match(URL_REGEX)) { | ||
res.push({ | ||
message: i18n.translate('xpack.fleet.settings.fleetServerHostsError', { | ||
defaultMessage: 'Invalid URL', | ||
}), | ||
index: idx, | ||
}); | ||
} | ||
const curIndexes = hostIndexes[val] || []; | ||
hostIndexes[val] = [...curIndexes, idx]; | ||
}); | ||
|
||
Object.values(hostIndexes) | ||
.filter(({ length }) => length > 1) | ||
.forEach((indexes) => { | ||
indexes.forEach((index) => | ||
res.push({ | ||
message: i18n.translate('xpack.fleet.settings.fleetServerHostsDuplicateError', { | ||
defaultMessage: 'Duplicate URL', | ||
}), | ||
index, | ||
}) | ||
); | ||
}); | ||
|
||
if (res.length) { | ||
return res; | ||
} | ||
|
||
if (value.length && isDiffPathProtocol(value)) { | ||
return [ | ||
{ | ||
message: i18n.translate( | ||
'xpack.fleet.settings.fleetServerHostsDifferentPathOrProtocolError', | ||
{ | ||
defaultMessage: 'Protocol and path must be the same for each URL', | ||
} | ||
), | ||
}, | ||
]; | ||
} | ||
} | ||
|
||
export function useFleetServerHostsForm( | ||
fleetServerHostsDefaultValue: string[], | ||
onSuccess: () => void | ||
) { | ||
const [isLoading, setIsLoading] = React.useState(false); | ||
const { notifications } = useStartServices(); | ||
const { confirm } = useConfirmModal(); | ||
|
||
const fleetServerHostsInput = useComboInput( | ||
'fleetServerHostsInput', | ||
fleetServerHostsDefaultValue, | ||
validateFleetServerHosts | ||
); | ||
|
||
const fleetServerHostsInputValidate = fleetServerHostsInput.validate; | ||
const validate = useCallback( | ||
() => fleetServerHostsInputValidate(), | ||
[fleetServerHostsInputValidate] | ||
); | ||
|
||
const submit = useCallback(async () => { | ||
try { | ||
if (!validate) { | ||
return; | ||
} | ||
if ( | ||
!(await confirm( | ||
i18n.translate('xpack.fleet.settings.fleetServerHostsFlyout.confirmModalTitle', { | ||
defaultMessage: 'Save and deploy changes?', | ||
}), | ||
i18n.translate('xpack.fleet.settings.fleetServerHostsFlyout.confirmModalDescription', { | ||
defaultMessage: | ||
'This action will update all of your agent policies and all of your agents. Are you sure you wish to continue?', | ||
}) | ||
)) | ||
) { | ||
return; | ||
} | ||
setIsLoading(true); | ||
const settingsResponse = await sendPutSettings({ | ||
fleet_server_hosts: fleetServerHostsInput.value, | ||
}); | ||
if (settingsResponse.error) { | ||
throw settingsResponse.error; | ||
} | ||
notifications.toasts.addSuccess( | ||
i18n.translate('xpack.fleet.settings.fleetServerHostsFlyout.successToastTitle', { | ||
defaultMessage: 'Settings saved', | ||
}) | ||
); | ||
setIsLoading(false); | ||
onSuccess(); | ||
} catch (error) { | ||
setIsLoading(false); | ||
notifications.toasts.addError(error, { | ||
title: i18n.translate('xpack.fleet.settings.fleetServerHostsFlyout.errorToastTitle', { | ||
defaultMessage: 'An error happened while saving settings', | ||
}), | ||
}); | ||
} | ||
}, [fleetServerHostsInput.value, validate, notifications, confirm, onSuccess]); | ||
|
||
return { | ||
isLoading, | ||
submit, | ||
fleetServerHostsInput, | ||
}; | ||
} |
52 changes: 52 additions & 0 deletions
52
...leet/public/applications/fleet/sections/settings/components/hosts_input/index.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/* | ||
* 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 { useState } from '@storybook/addons'; | ||
import { addParameters } from '@storybook/react'; | ||
import React from 'react'; | ||
|
||
import { HostsInput as Component } from '.'; | ||
|
||
addParameters({ | ||
options: { | ||
enableShortcuts: false, | ||
}, | ||
}); | ||
|
||
export default { | ||
component: Component, | ||
title: 'Sections/Fleet/Settings', | ||
}; | ||
|
||
interface Args { | ||
width: number; | ||
label: string; | ||
helpText: string; | ||
} | ||
|
||
const args: Args = { | ||
width: 250, | ||
label: 'Demo label', | ||
helpText: 'Demo helpText', | ||
}; | ||
|
||
export const HostsInput = ({ width, label, helpText }: Args) => { | ||
const [value, setValue] = useState<string[]>([]); | ||
return ( | ||
<div style={{ width }}> | ||
<Component | ||
id="test-host-input" | ||
helpText={helpText} | ||
value={value} | ||
onChange={setValue} | ||
label={label} | ||
/> | ||
</div> | ||
); | ||
}; | ||
|
||
HostsInput.args = args; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.