Skip to content

Commit

Permalink
use management section in features to display alert & action management
Browse files Browse the repository at this point in the history
  • Loading branch information
gmmorris committed Jul 8, 2020
1 parent f77c74f commit 230a1e5
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 121 deletions.
9 changes: 9 additions & 0 deletions x-pack/plugins/actions/server/feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,17 @@ export const ACTIONS_FEATURE = {
icon: 'bell',
navLinkId: 'actions',
app: [],
management: {
insightsAndAlerting: ['triggersActions'],
},
privileges: {
all: {
app: [],
api: [],
catalogue: [],
management: {
insightsAndAlerting: ['triggersActions'],
},
savedObject: {
all: [ACTION_SAVED_OBJECT_TYPE, ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE],
read: [],
Expand All @@ -30,6 +36,9 @@ export const ACTIONS_FEATURE = {
app: [],
api: [],
catalogue: [],
management: {
insightsAndAlerting: ['triggersActions'],
},
savedObject: {
// action execution requires 'read' over `actions`, but 'all' over `action_task_params`
all: [ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE],
Expand Down
13 changes: 11 additions & 2 deletions x-pack/plugins/alerting_builtins/server/feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,16 @@ export const BUILT_IN_ALERTS_FEATURE = {
icon: 'bell',
navLinkId: 'builtInAlerts',
app: [],
management: {
insightsAndAlerting: ['triggersActions'],
},
privileges: {
all: {
app: [],
catalogue: [],
management: {
insightsAndAlerting: ['triggersActions'],
},
alerting: {
all: [IndexThreshold],
read: [],
Expand All @@ -29,11 +35,14 @@ export const BUILT_IN_ALERTS_FEATURE = {
read: [],
},
api: ['actions-read', 'actions-all'],
ui: ['alerting:show', 'actions:show', 'actions:save', 'actions:delete'],
ui: ['actions:save', 'actions:delete'],
},
read: {
app: [],
catalogue: [],
management: {
insightsAndAlerting: ['triggersActions'],
},
alerting: {
all: [],
read: [IndexThreshold],
Expand All @@ -43,7 +52,7 @@ export const BUILT_IN_ALERTS_FEATURE = {
read: ['action'],
},
api: ['actions-read'],
ui: ['alerting:show', 'actions:show'],
ui: [],
},
},
};
10 changes: 10 additions & 0 deletions x-pack/plugins/alerts/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,16 @@ export class AlertingPlugin {
this.spaces = plugins.spaces?.spacesService;
this.security = plugins.security;

core.capabilities.registerProvider(() => {
return {
management: {
insightsAndAlerting: {
triggersActions: true,
},
},
};
});

this.isESOUsingEphemeralEncryptionKey =
plugins.encryptedSavedObjects.usingEphemeralEncryptionKey;

Expand Down
18 changes: 6 additions & 12 deletions x-pack/plugins/triggers_actions_ui/public/application/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ import {
ScopedHistory,
} from 'kibana/public';
import { Section, routeToAlertDetails } from './constants';
import { AppContextProvider, useAppDependencies } from './app_context';
import { hasShowAlertsCapability } from './lib/capabilities';
import { AppContextProvider } from './app_context';
import { ActionTypeModel, AlertTypeModel } from '../types';
import { TypeRegistry } from './type_registry';
import { ChartsPluginStart } from '../../../../../src/plugins/charts/public';
Expand Down Expand Up @@ -63,22 +62,17 @@ export const App = (appDeps: AppDeps) => {
};

export const AppWithoutRouter = ({ sectionsRegex }: { sectionsRegex: string }) => {
const { capabilities } = useAppDependencies();
const canShowAlerts = hasShowAlertsCapability(capabilities);
const DEFAULT_SECTION: Section = canShowAlerts ? 'alerts' : 'connectors';
return (
<Switch>
<Route
path={`/:section(${sectionsRegex})`}
component={suspendedComponentWithProps(TriggersActionsUIHome, 'xl')}
/>
{canShowAlerts && (
<Route
path={routeToAlertDetails}
component={suspendedComponentWithProps(AlertDetailsRoute, 'xl')}
/>
)}
<Redirect from={'/'} to={`${DEFAULT_SECTION}`} />
<Route
path={routeToAlertDetails}
component={suspendedComponentWithProps(AlertDetailsRoute, 'xl')}
/>
<Redirect from={'/'} to="alerts" />
</Switch>
);
};
38 changes: 15 additions & 23 deletions x-pack/plugins/triggers_actions_ui/public/application/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,23 +45,17 @@ export const TriggersActionsUIHome: React.FunctionComponent<RouteComponentProps<
const { chrome, capabilities, setBreadcrumbs, docLinks, http } = useAppDependencies();

const canShowActions = hasShowActionsCapability(capabilities);
const canShowAlerts = hasShowAlertsCapability(capabilities);
const tabs: Array<{
id: Section;
name: React.ReactNode;
}> = [];

if (canShowAlerts) {
tabs.push({
id: 'alerts',
name: (
<FormattedMessage
id="xpack.triggersActionsUI.home.alertsTabTitle"
defaultMessage="Alerts"
/>
),
});
}
tabs.push({
id: 'alerts',
name: (
<FormattedMessage id="xpack.triggersActionsUI.home.alertsTabTitle" defaultMessage="Alerts" />
),
});

if (canShowActions) {
tabs.push({
Expand Down Expand Up @@ -151,17 +145,15 @@ export const TriggersActionsUIHome: React.FunctionComponent<RouteComponentProps<
)}
/>
)}
{canShowAlerts && (
<Route
exact
path={routeToAlerts}
component={() => (
<HealthCheck docLinks={docLinks} http={http}>
<AlertsList />
</HealthCheck>
)}
/>
)}
<Route
exact
path={routeToAlerts}
component={() => (
<HealthCheck docLinks={docLinks} http={http}>
<AlertsList />
</HealthCheck>
)}
/>
</Switch>
</EuiPageContent>
</EuiPageBody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,8 @@ function hasCapability(capabilities: Capabilities, capability: string) {
return apps.some((app) => capabilities[app]?.[capability]);
}

function createCapabilityCheck(capability: string) {
return (capabilities: Capabilities) => hasCapability(capabilities, capability);
}

export const hasShowAlertsCapability = createCapabilityCheck('alerting:show');
export const hasShowAlertsCapability = (capabilities: Capabilities) =>
hasCapability(capabilities, 'alerting:show');

export const hasShowActionsCapability = (capabilities: Capabilities) => capabilities?.actions?.show;
export const hasSaveActionsCapability = (capabilities: Capabilities) => capabilities?.actions?.save;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
EuiBetaBadge,
EuiToolTip,
EuiButtonIcon,
EuiEmptyPrompt,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
Expand Down Expand Up @@ -344,12 +345,25 @@ export const ActionsConnectorsList: React.FunctionComponent = () => {
);

const noPermissionPrompt = (
<h2>
<FormattedMessage
id="xpack.triggersActionsUI.sections.actionsConnectorsList.noPermissionToCreateTitle"
defaultMessage="No permissions to create connector"
/>
</h2>
<EuiEmptyPrompt
iconType="securityApp"
title={
<h1>
<FormattedMessage
id="xpack.triggersActionsUI.sections.actionsConnectorsList.noPermissionToCreateTitle"
defaultMessage="No permissions to create connector"
/>
</h1>
}
body={
<p data-test-subj="permissionDeniedMessage">
<FormattedMessage
id="xpack.triggersActionsUI.sections.actionsConnectorsList.noPermissionToCreateDescription"
defaultMessage="Contact your system administrator."
/>
</p>
}
/>
);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
EuiSpacer,
EuiLink,
EuiLoadingSpinner,
EuiEmptyPrompt,
} from '@elastic/eui';
import { useHistory } from 'react-router-dom';

Expand Down Expand Up @@ -92,7 +93,7 @@ export const AlertsList: React.FunctionComponent = () => {
useEffect(() => {
loadAlertsData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [page, searchText, typesFilter, actionTypesFilter]);
}, [alertTypesState, page, searchText, typesFilter, actionTypesFilter]);

useEffect(() => {
(async () => {
Expand Down Expand Up @@ -135,30 +136,33 @@ export const AlertsList: React.FunctionComponent = () => {
}, []);

async function loadAlertsData() {
setAlertsState({ ...alertsState, isLoading: true });
try {
const alertsResponse = await loadAlerts({
http,
page,
searchText,
typesFilter,
actionTypesFilter,
});
setAlertsState({
isLoading: false,
data: alertsResponse.data,
totalItemCount: alertsResponse.total,
});
} catch (e) {
toastNotifications.addDanger({
title: i18n.translate(
'xpack.triggersActionsUI.sections.alertsList.unableToLoadAlertsMessage',
{
defaultMessage: 'Unable to load alerts',
}
),
});
setAlertsState({ ...alertsState, isLoading: false });
const hasAnyAuthorizedAlertType = alertTypesState.data.size > 0;
if (hasAnyAuthorizedAlertType) {
setAlertsState({ ...alertsState, isLoading: true });
try {
const alertsResponse = await loadAlerts({
http,
page,
searchText,
typesFilter,
actionTypesFilter,
});
setAlertsState({
isLoading: false,
data: alertsResponse.data,
totalItemCount: alertsResponse.total,
});
} catch (e) {
toastNotifications.addDanger({
title: i18n.translate(
'xpack.triggersActionsUI.sections.alertsList.unableToLoadAlertsMessage',
{
defaultMessage: 'Unable to load alerts',
}
),
});
setAlertsState({ ...alertsState, isLoading: false });
}
}
}

Expand Down Expand Up @@ -247,6 +251,9 @@ export const AlertsList: React.FunctionComponent = () => {
];

const authorizedAlertTypes = [...alertTypesState.data.values()];
const authorizedToCreateAnyAlerts = authorizedAlertTypes.some(
(alertType) => alertType.authorizedConsumers[ALERTS_FEATURE_ID]?.all
);

const toolsRight = [
<TypeFilter
Expand All @@ -266,9 +273,7 @@ export const AlertsList: React.FunctionComponent = () => {
/>,
];

if (
authorizedAlertTypes.some((alertType) => alertType.authorizedConsumers[ALERTS_FEATURE_ID]?.all)
) {
if (authorizedToCreateAnyAlerts) {
toolsRight.push(
<EuiButton
key="create-alert"
Expand Down Expand Up @@ -434,8 +439,10 @@ export const AlertsList: React.FunctionComponent = () => {
<EuiLoadingSpinner size="xl" />
</EuiFlexItem>
</EuiFlexGroup>
) : (
) : authorizedToCreateAnyAlerts ? (
<EmptyPrompt onCTAClicked={() => setAlertFlyoutVisibility(true)} />
) : (
noPermissionPrompt
)}
<AlertsContextProvider
value={{
Expand All @@ -461,6 +468,28 @@ export const AlertsList: React.FunctionComponent = () => {
);
};

const noPermissionPrompt = (
<EuiEmptyPrompt
iconType="securityApp"
title={
<h1>
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertsList.noPermissionToCreateTitle"
defaultMessage="No permissions to create alerts"
/>
</h1>
}
body={
<p data-test-subj="permissionDeniedMessage">
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertsList.noPermissionToCreateDescription"
defaultMessage="Contact your system administrator."
/>
</p>
}
/>
);

function filterAlertsById(alerts: Alert[], ids: string[]): Alert[] {
return alerts.filter((alert) => ids.includes(alert.id));
}
Expand Down
Loading

0 comments on commit 230a1e5

Please sign in to comment.