diff --git a/x-pack/plugins/actions/server/feature.ts b/x-pack/plugins/actions/server/feature.ts
index c06acb676145..321509a7b9de 100644
--- a/x-pack/plugins/actions/server/feature.ts
+++ b/x-pack/plugins/actions/server/feature.ts
@@ -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: [],
@@ -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],
diff --git a/x-pack/plugins/alerting_builtins/server/feature.ts b/x-pack/plugins/alerting_builtins/server/feature.ts
index dd2fe2552ee5..12a19a350f3e 100644
--- a/x-pack/plugins/alerting_builtins/server/feature.ts
+++ b/x-pack/plugins/alerting_builtins/server/feature.ts
@@ -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: [],
@@ -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],
@@ -43,7 +52,7 @@ export const BUILT_IN_ALERTS_FEATURE = {
read: ['action'],
},
api: ['actions-read'],
- ui: ['alerting:show', 'actions:show'],
+ ui: [],
},
},
};
diff --git a/x-pack/plugins/alerts/server/plugin.ts b/x-pack/plugins/alerts/server/plugin.ts
index 6ca65ac152ee..3abfcba584d7 100644
--- a/x-pack/plugins/alerts/server/plugin.ts
+++ b/x-pack/plugins/alerts/server/plugin.ts
@@ -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;
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx
index 80f8e4a3a2c7..1f9352d8405d 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx
@@ -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';
@@ -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 (
- {canShowAlerts && (
-
- )}
-
+
+
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/home.tsx b/x-pack/plugins/triggers_actions_ui/public/application/home.tsx
index eeb8a7771733..ef849dbf1be0 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/home.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/home.tsx
@@ -45,23 +45,17 @@ export const TriggersActionsUIHome: React.FunctionComponent = [];
- if (canShowAlerts) {
- tabs.push({
- id: 'alerts',
- name: (
-
- ),
- });
- }
+ tabs.push({
+ id: 'alerts',
+ name: (
+
+ ),
+ });
if (canShowActions) {
tabs.push({
@@ -151,17 +145,15 @@ export const TriggersActionsUIHome: React.FunctionComponent
)}
- {canShowAlerts && (
- (
-
-
-
- )}
- />
- )}
+ (
+
+
+
+ )}
+ />
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts
index 065a782ee96a..03085b2e8ca2 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts
@@ -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;
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx
index f92d0d4642b3..b6b4e9fd2ad7 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx
@@ -17,6 +17,7 @@ import {
EuiBetaBadge,
EuiToolTip,
EuiButtonIcon,
+ EuiEmptyPrompt,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
@@ -344,12 +345,25 @@ export const ActionsConnectorsList: React.FunctionComponent = () => {
);
const noPermissionPrompt = (
-
-
-
+
+
+
+ }
+ body={
+
+
+
+ }
+ />
);
return (
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx
index 4056cdaa0235..2b2897a2181b 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx
@@ -18,6 +18,7 @@ import {
EuiSpacer,
EuiLink,
EuiLoadingSpinner,
+ EuiEmptyPrompt,
} from '@elastic/eui';
import { useHistory } from 'react-router-dom';
@@ -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 () => {
@@ -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 });
+ }
}
}
@@ -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 = [
{
/>,
];
- if (
- authorizedAlertTypes.some((alertType) => alertType.authorizedConsumers[ALERTS_FEATURE_ID]?.all)
- ) {
+ if (authorizedToCreateAnyAlerts) {
toolsRight.push(
{
- ) : (
+ ) : authorizedToCreateAnyAlerts ? (
setAlertFlyoutVisibility(true)} />
+ ) : (
+ noPermissionPrompt
)}
{
);
};
+const noPermissionPrompt = (
+
+
+
+ }
+ body={
+
+
+
+ }
+ />
+);
+
function filterAlertsById(alerts: Alert[], ids: string[]): Alert[] {
return alerts.filter((alert) => ids.includes(alert.id));
}
diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts
index db93e48ab369..c5bc48e0f96e 100644
--- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts
@@ -4,15 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { CoreStart, PluginInitializerContext, Plugin as CorePlugin } from 'src/core/public';
+import {
+ CoreStart,
+ CoreSetup,
+ PluginInitializerContext,
+ Plugin as CorePlugin,
+} from 'src/core/public';
import { i18n } from '@kbn/i18n';
import { registerBuiltInActionTypes } from './application/components/builtin_action_types';
import { registerBuiltInAlertTypes } from './application/components/builtin_alert_types';
-import { hasShowActionsCapability, hasShowAlertsCapability } from './application/lib/capabilities';
import { ActionTypeModel, AlertTypeModel } from './types';
import { TypeRegistry } from './application/type_registry';
-import { ManagementStart, ManagementSectionId } from '../../../../src/plugins/management/public';
+import { ManagementSetup, ManagementSectionId } from '../../../../src/plugins/management/public';
import { boot } from './application/boot';
import { ChartsPluginStart } from '../../../../src/plugins/charts/public';
import { PluginStartContract as AlertingStart } from '../../alerts/public';
@@ -28,17 +32,25 @@ export interface TriggersAndActionsUIPublicPluginStart {
alertTypeRegistry: TypeRegistry;
}
+interface PluginsSetup {
+ management: ManagementSetup;
+}
+
interface PluginsStart {
data: DataPublicPluginStart;
charts: ChartsPluginStart;
- management: ManagementStart;
alerts?: AlertingStart;
navigateToApp: CoreStart['application']['navigateToApp'];
}
export class Plugin
implements
- CorePlugin {
+ CorePlugin<
+ TriggersAndActionsUIPublicPluginSetup,
+ TriggersAndActionsUIPublicPluginStart,
+ PluginsSetup,
+ PluginsStart
+ > {
private actionTypeRegistry: TypeRegistry;
private alertTypeRegistry: TypeRegistry;
@@ -50,7 +62,10 @@ export class Plugin
this.alertTypeRegistry = alertTypeRegistry;
}
- public setup(): TriggersAndActionsUIPublicPluginSetup {
+ public setup(
+ core: CoreSetup,
+ plugins: PluginsSetup
+ ): TriggersAndActionsUIPublicPluginSetup {
registerBuiltInActionTypes({
actionTypeRegistry: this.actionTypeRegistry,
});
@@ -59,50 +74,44 @@ export class Plugin
alertTypeRegistry: this.alertTypeRegistry,
});
+ plugins.management.sections.getSection(ManagementSectionId.InsightsAndAlerting).registerApp({
+ id: 'triggersActions',
+ title: i18n.translate('xpack.triggersActionsUI.managementSection.displayName', {
+ defaultMessage: 'Alerts and Actions',
+ }),
+ order: 0,
+ mount: async (params) => {
+ const [coreStart, startPlugins] = await core.getStartServices();
+ boot({
+ dataPlugin: startPlugins.data,
+ charts: startPlugins.charts,
+ alerts: startPlugins.alerts,
+ element: params.element,
+ toastNotifications: core.notifications.toasts,
+ http: core.http,
+ uiSettings: core.uiSettings,
+ docLinks: coreStart.docLinks,
+ chrome: coreStart.chrome,
+ savedObjects: coreStart.savedObjects.client,
+ I18nContext: coreStart.i18n.Context,
+ capabilities: coreStart.application.capabilities,
+ navigateToApp: coreStart.application.navigateToApp,
+ setBreadcrumbs: params.setBreadcrumbs,
+ history: params.history,
+ actionTypeRegistry: this.actionTypeRegistry,
+ alertTypeRegistry: this.alertTypeRegistry,
+ });
+ return () => {};
+ },
+ });
+
return {
actionTypeRegistry: this.actionTypeRegistry,
alertTypeRegistry: this.alertTypeRegistry,
};
}
- public start(core: CoreStart, plugins: PluginsStart): TriggersAndActionsUIPublicPluginStart {
- const { capabilities } = core.application;
-
- const canShowActions = hasShowActionsCapability(capabilities);
- const canShowAlerts = hasShowAlertsCapability(capabilities);
-
- // Don't register routes when user doesn't have access to the application
- if (canShowActions || canShowAlerts) {
- plugins.management.sections.getSection(ManagementSectionId.InsightsAndAlerting).registerApp({
- id: 'triggersActions',
- title: i18n.translate('xpack.triggersActionsUI.managementSection.displayName', {
- defaultMessage: 'Alerts and Actions',
- }),
- order: 0,
- mount: (params) => {
- boot({
- dataPlugin: plugins.data,
- charts: plugins.charts,
- alerts: plugins.alerts,
- element: params.element,
- toastNotifications: core.notifications.toasts,
- http: core.http,
- uiSettings: core.uiSettings,
- docLinks: core.docLinks,
- chrome: core.chrome,
- savedObjects: core.savedObjects.client,
- I18nContext: core.i18n.Context,
- capabilities: core.application.capabilities,
- navigateToApp: core.application.navigateToApp,
- setBreadcrumbs: params.setBreadcrumbs,
- history: params.history,
- actionTypeRegistry: this.actionTypeRegistry,
- alertTypeRegistry: this.alertTypeRegistry,
- });
- return () => {};
- },
- });
- }
+ public start(): TriggersAndActionsUIPublicPluginStart {
return {
actionTypeRegistry: this.actionTypeRegistry,
alertTypeRegistry: this.alertTypeRegistry,