App Settings Page: Host status webhook UI (#1899)
* Skeleton UI * Rebase to main * Work towards config API request modification * Nest and unnest when formatting for server and frontend * Changelog * Add validation to UI, fix ? vertical spacing * Rebase e2e * 1 of 2 passing unit tests for config * Update REST-API.md to include webhook_settings * Destructure / flatten config webhook in unit test * Merge advance options e2e conflict * x and y example not x and x * Fix observer e2e * Add new data to read only example request Co-authored-by: Noah Talerman <noahtal@umich.edu> Co-authored-by: Martavis Parker <martavis@auraticdevelopment.com>
This commit is contained in:
co-authored by
Noah Talerman
Martavis Parker
parent
13e5e6a6db
commit
d32479622c
@@ -28,6 +28,19 @@ const authTypeOptions = [
|
||||
{ label: "Username and Password", value: "authtype_username_password" },
|
||||
{ label: "None", value: "authtype_none" },
|
||||
];
|
||||
const percentageOfHosts = [
|
||||
{ label: "1%", value: 1 },
|
||||
{ label: "5%", value: 5 },
|
||||
{ label: "10%", value: 10 },
|
||||
{ label: "25%", value: 25 },
|
||||
];
|
||||
const numberOfDays = [
|
||||
{ label: "1 day", value: 1 },
|
||||
{ label: "3 days", value: 3 },
|
||||
{ label: "7 days", value: 7 },
|
||||
{ label: "14 days", value: 14 },
|
||||
];
|
||||
|
||||
const baseClass = "app-config-form";
|
||||
const formFields = [
|
||||
"authentication_method",
|
||||
@@ -58,6 +71,10 @@ const formFields = [
|
||||
"host_expiry_window",
|
||||
"live_query_disabled",
|
||||
"agent_options",
|
||||
"enable_host_status_webhook",
|
||||
"destination_url",
|
||||
"host_percentage",
|
||||
"days_count",
|
||||
"enable_analytics",
|
||||
];
|
||||
class AppConfigForm extends Component {
|
||||
@@ -90,6 +107,10 @@ class AppConfigForm extends Component {
|
||||
host_expiry_window: formFieldInterface.isRequired,
|
||||
live_query_disabled: formFieldInterface.isRequired,
|
||||
agent_options: formFieldInterface.isRequired,
|
||||
enable_host_status_webhook: formFieldInterface.isRequired,
|
||||
destination_url: formFieldInterface,
|
||||
host_percentage: formFieldInterface,
|
||||
days_count: formFieldInterface,
|
||||
enable_analytics: formFieldInterface.isRequired,
|
||||
}).isRequired,
|
||||
enrollSecret: PropTypes.arrayOf(enrollSecretInterface).isRequired,
|
||||
@@ -101,10 +122,28 @@ class AppConfigForm extends Component {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
showHostStatusWebhookPreviewModal: false,
|
||||
showUsageStatsPreviewModal: false,
|
||||
};
|
||||
}
|
||||
|
||||
onToggleAdvancedOptions = (evt) => {
|
||||
evt.preventDefault();
|
||||
|
||||
const { showAdvancedOptions } = this.state;
|
||||
|
||||
this.setState({ showAdvancedOptions: !showAdvancedOptions });
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
toggleHostStatusWebhookPreviewModal = () => {
|
||||
const { showHostStatusWebhookPreviewModal } = this.state;
|
||||
this.setState({
|
||||
showHostStatusWebhookPreviewModal: !showHostStatusWebhookPreviewModal,
|
||||
});
|
||||
};
|
||||
|
||||
toggleUsageStatsPreviewModal = () => {
|
||||
const { showUsageStatsPreviewModal } = this.state;
|
||||
this.setState({
|
||||
@@ -181,6 +220,45 @@ class AppConfigForm extends Component {
|
||||
);
|
||||
};
|
||||
|
||||
renderHostStatusWebhookPreviewModal = () => {
|
||||
const { toggleHostStatusWebhookPreviewModal } = this;
|
||||
const { showHostStatusWebhookPreviewModal } = this.state;
|
||||
|
||||
if (!showHostStatusWebhookPreviewModal) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const json = {
|
||||
message:
|
||||
"More than X% of your hosts have not checked into Fleet for more than Y days. You’ve been sent this message because the Host status webhook is enabled in your Fleet instance.",
|
||||
data: {
|
||||
unseen_hosts: 1,
|
||||
total_hosts: 2,
|
||||
days_unseen: 3,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="Host status webhook"
|
||||
onExit={toggleHostStatusWebhookPreviewModal}
|
||||
className={`${baseClass}__host-status-webhook-preview-modal`}
|
||||
>
|
||||
<p>
|
||||
An example request sent to your configured <b>Destination URL</b>.
|
||||
</p>
|
||||
<div className={`${baseClass}__host-status-webhook-preview`}>
|
||||
<pre dangerouslySetInnerHTML={{ __html: syntaxHighlight(json) }} />
|
||||
</div>
|
||||
<div className="flex-end">
|
||||
<Button type="button" onClick={toggleHostStatusWebhookPreviewModal}>
|
||||
Done
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
renderUsageStatsPreviewModal = () => {
|
||||
const { toggleUsageStatsPreviewModal } = this;
|
||||
const { showUsageStatsPreviewModal } = this.state;
|
||||
@@ -217,7 +295,9 @@ class AppConfigForm extends Component {
|
||||
const {
|
||||
renderAdvancedOptions,
|
||||
renderSmtpSection,
|
||||
toggleHostStatusWebhookPreviewModal,
|
||||
toggleUsageStatsPreviewModal,
|
||||
renderHostStatusWebhookPreviewModal,
|
||||
renderUsageStatsPreviewModal,
|
||||
} = this;
|
||||
|
||||
@@ -398,7 +478,7 @@ class AppConfigForm extends Component {
|
||||
<div className={`${baseClass}__details`}>
|
||||
<IconToolTip
|
||||
text={
|
||||
"The hostname / IP address and corresponding port of your organization's SMTP server."
|
||||
"The hostname / IP address and corresponding port of your organization's SMTP server."
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
@@ -479,6 +559,89 @@ class AppConfigForm extends Component {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`${baseClass}__section`}>
|
||||
<h2>
|
||||
<a id="host-status-webhook">Host status webhook</a>
|
||||
</h2>
|
||||
<div className={`${baseClass}__host-status-webhook`}>
|
||||
<p className={`${baseClass}__section-description`}>
|
||||
Send an alert if a portion of your hosts go offline.
|
||||
</p>
|
||||
<Checkbox {...fields.enable_host_status_webhook}>
|
||||
Enable host status webhook
|
||||
</Checkbox>
|
||||
<p className={`${baseClass}__section-description`}>
|
||||
A request will be sent to your configured <b>Destination URL</b>{" "}
|
||||
if the configured <b>Percentage of hosts</b> have not checked
|
||||
into Fleet for the configured <b>Number of days</b>.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className={`${baseClass}__inputs ${baseClass}__inputs--webhook`}
|
||||
>
|
||||
<Button
|
||||
type="button"
|
||||
variant="inverse"
|
||||
onClick={toggleHostStatusWebhookPreviewModal}
|
||||
>
|
||||
Preview request
|
||||
</Button>
|
||||
</div>
|
||||
<div className={`${baseClass}__inputs`}>
|
||||
<InputField
|
||||
{...fields.destination_url}
|
||||
placeholder="https://server.com/example"
|
||||
label="Destination URL"
|
||||
/>
|
||||
</div>
|
||||
<div className={`${baseClass}__details`}>
|
||||
<IconToolTip
|
||||
isHtml
|
||||
text={
|
||||
"\
|
||||
<center><p>Provide a URL to deliver <br/>the webhook request to.</p></center>\
|
||||
"
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={`${baseClass}__inputs ${baseClass}__host-percentage`}
|
||||
>
|
||||
<Dropdown
|
||||
{...fields.host_percentage}
|
||||
label="Percentage of hosts"
|
||||
options={percentageOfHosts}
|
||||
/>
|
||||
</div>
|
||||
<div className={`${baseClass}__details`}>
|
||||
<IconToolTip
|
||||
isHtml
|
||||
text={
|
||||
"\
|
||||
<center><p>Select the minimum percentage of hosts that<br/>must fail to check into Fleet in order to trigger<br/>the webhook request.</p></center>\
|
||||
"
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className={`${baseClass}__inputs ${baseClass}__days-count`}>
|
||||
<Dropdown
|
||||
{...fields.days_count}
|
||||
label="Number of days"
|
||||
options={numberOfDays}
|
||||
/>
|
||||
</div>
|
||||
<div className={`${baseClass}__details`}>
|
||||
<IconToolTip
|
||||
isHtml
|
||||
text={
|
||||
"\
|
||||
<center><p>Select the minimum number of days that the<br/>configured <b>Percentage of hosts</b> must fail to<br/>check into Fleet in order to trigger the<br/>webhook request.</p></center>\
|
||||
"
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`${baseClass}__section`}>
|
||||
<h2>
|
||||
<a id="usage-stats">Usage statistics</a>
|
||||
@@ -527,6 +690,7 @@ class AppConfigForm extends Component {
|
||||
</Button>
|
||||
</form>
|
||||
{renderUsageStatsPreviewModal()}
|
||||
{renderHostStatusWebhookPreviewModal()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
&__inputs {
|
||||
width: 60%;
|
||||
float: left;
|
||||
padding: 0 $pad-xxlarge 0 0;
|
||||
padding-right: $pad-small;
|
||||
box-sizing: border-box;
|
||||
|
||||
.input-field {
|
||||
@@ -152,11 +152,15 @@
|
||||
&--usage {
|
||||
margin-top: 24px;
|
||||
}
|
||||
&--webhook {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
&__details {
|
||||
float: right;
|
||||
width: 40%;
|
||||
height: 87px;
|
||||
|
||||
.icon-tooltip {
|
||||
margin: $pad-xlarge 0;
|
||||
@@ -216,7 +220,8 @@
|
||||
); // because the outline extended beyond the sticky page description
|
||||
}
|
||||
|
||||
&__usage-stats-preview-modal {
|
||||
&__usage-stats-preview-modal,
|
||||
&__host-status-webhook-preview-modal {
|
||||
.flex-end {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
|
||||
@@ -22,6 +22,10 @@ export default (formData) => {
|
||||
host_expiry_enabled: hostExpiryEnabled,
|
||||
host_expiry_window: hostExpiryWindow = 0,
|
||||
agent_options: agentOptions,
|
||||
enable_host_status_webhook: enableHostStatusWebhook,
|
||||
destination_url: destinationUrl,
|
||||
host_percentage: hostPercentage,
|
||||
days_count: daysCount,
|
||||
} = formData;
|
||||
|
||||
if (enableSSO) {
|
||||
@@ -68,6 +72,20 @@ export default (formData) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (enableHostStatusWebhook) {
|
||||
if (!destinationUrl) {
|
||||
errors.destination_url = "Destination URL must be present";
|
||||
}
|
||||
|
||||
if (!hostPercentage) {
|
||||
errors.host_percentage = "Host percentage must be present";
|
||||
}
|
||||
|
||||
if (!daysCount) {
|
||||
errors.days_count = "Days count must be present";
|
||||
}
|
||||
}
|
||||
|
||||
if (hostExpiryEnabled) {
|
||||
if (isNaN(hostExpiryWindow) || Number(hostExpiryWindow) <= 0) {
|
||||
errors.host_expiry_window =
|
||||
|
||||
Reference in New Issue
Block a user