@@ -231,13 +230,12 @@ exports[`UrlFormatEditor should render normally 1`] = `
- External link
-
+ role="presentation"
+ />
- (opens in a new tab or window)
+ (external, opens in a new tab or window)
diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx
index 77befc4dc334f..6092a59333290 100644
--- a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx
+++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx
@@ -515,6 +515,7 @@ function DiscoverDocumentsComponent({
additionalFieldGroups={additionalFieldGroups}
dataGridDensityState={density}
onUpdateDataGridDensity={onUpdateDensity}
+ onUpdateESQLQuery={stateContainer.actions.updateESQLQuery}
query={query}
cellActionsTriggerId={DISCOVER_CELL_ACTIONS_TRIGGER.id}
cellActionsMetadata={cellActionsMetadata}
diff --git a/src/plugins/discover/public/application/main/state_management/discover_state.ts b/src/plugins/discover/public/application/main/state_management/discover_state.ts
index 0e1a662567bb8..56d1ca44c3853 100644
--- a/src/plugins/discover/public/application/main/state_management/discover_state.ts
+++ b/src/plugins/discover/public/application/main/state_management/discover_state.ts
@@ -25,7 +25,8 @@ import type { SavedSearch } from '@kbn/saved-search-plugin/public';
import { v4 as uuidv4 } from 'uuid';
import { merge } from 'rxjs';
import { getInitialESQLQuery } from '@kbn/esql-utils';
-import { AggregateQuery, Query, TimeRange } from '@kbn/es-query';
+import { AggregateQuery, isOfAggregateQueryType, Query, TimeRange } from '@kbn/es-query';
+import { isFunction } from 'lodash';
import { loadSavedSearch as loadSavedSearchFn } from './utils/load_saved_search';
import { restoreStateFromSavedSearch } from '../../../services/saved_searches/restore_from_saved_search';
import { FetchStatus } from '../../types';
@@ -219,6 +220,10 @@ export interface DiscoverStateContainer {
* This is to prevent duplicate ids messing with our system
*/
updateAdHocDataViewId: () => Promise;
+ /**
+ * Updates the ES|QL query string
+ */
+ updateESQLQuery: (queryOrUpdater: string | ((prevQuery: string) => string)) => void;
};
}
@@ -572,6 +577,22 @@ export function getDiscoverStateContainer({
}
};
+ const updateESQLQuery = (queryOrUpdater: string | ((prevQuery: string) => string)) => {
+ addLog('updateESQLQuery');
+ const { query: currentQuery } = appStateContainer.getState();
+
+ if (!isOfAggregateQueryType(currentQuery)) {
+ throw new Error(
+ 'Cannot update a non-ES|QL query. Make sure this function is only called once in ES|QL mode.'
+ );
+ }
+
+ const queryUpdater = isFunction(queryOrUpdater) ? queryOrUpdater : () => queryOrUpdater;
+ const query = { esql: queryUpdater(currentQuery.esql) };
+
+ appStateContainer.update({ query });
+ };
+
return {
globalState: globalStateContainer,
appState: appStateContainer,
@@ -597,6 +618,7 @@ export function getDiscoverStateContainer({
setDataView,
undoSavedSearchChanges,
updateAdHocDataViewId,
+ updateESQLQuery,
},
};
}
diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx
index 0fcb775d02184..3b0a2df2582aa 100644
--- a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx
+++ b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx
@@ -15,9 +15,11 @@ import {
} from '@kbn/unified-data-table';
import { useProfileAccessor } from '../../context_awareness';
import { DiscoverAppState } from '../../application/main/state_management/discover_app_state_container';
+import { DiscoverStateContainer } from '../../application/main/state_management/discover_state';
export interface DiscoverGridProps extends UnifiedDataTableProps {
query?: DiscoverAppState['query'];
+ onUpdateESQLQuery?: DiscoverStateContainer['actions']['updateESQLQuery'];
}
/**
@@ -25,10 +27,12 @@ export interface DiscoverGridProps extends UnifiedDataTableProps {
* @constructor
*/
export const DiscoverGrid: React.FC = ({
- rowAdditionalLeadingControls: customRowAdditionalLeadingControls,
+ onUpdateESQLQuery,
query,
+ rowAdditionalLeadingControls: customRowAdditionalLeadingControls,
...props
}) => {
+ const { dataView } = props;
const getRowIndicatorProvider = useProfileAccessor('getRowIndicatorProvider');
const getRowIndicator = useMemo(() => {
return getRowIndicatorProvider(() => undefined)({ dataView: props.dataView });
@@ -39,14 +43,16 @@ export const DiscoverGrid: React.FC = ({
);
const rowAdditionalLeadingControls = useMemo(() => {
return getRowAdditionalLeadingControlsAccessor(() => customRowAdditionalLeadingControls)({
- dataView: props.dataView,
+ dataView,
query,
+ updateESQLQuery: onUpdateESQLQuery,
});
}, [
+ customRowAdditionalLeadingControls,
+ dataView,
getRowAdditionalLeadingControlsAccessor,
- props.dataView,
+ onUpdateESQLQuery,
query,
- customRowAdditionalLeadingControls,
]);
return (
diff --git a/src/plugins/discover/public/components/logs_explorer_tabs/logs_explorer_tabs.test.tsx b/src/plugins/discover/public/components/logs_explorer_tabs/logs_explorer_tabs.test.tsx
index 1782dc7ad6ac7..e353fe1971ec9 100644
--- a/src/plugins/discover/public/components/logs_explorer_tabs/logs_explorer_tabs.test.tsx
+++ b/src/plugins/discover/public/components/logs_explorer_tabs/logs_explorer_tabs.test.tsx
@@ -10,10 +10,21 @@
import userEvent from '@testing-library/user-event';
import { render, screen } from '@testing-library/react';
import React from 'react';
+import { DISCOVER_APP_ID } from '@kbn/deeplinks-analytics';
+import {
+ ALL_DATASETS_LOCATOR_ID,
+ OBSERVABILITY_LOGS_EXPLORER_APP_ID,
+} from '@kbn/deeplinks-observability';
import { discoverServiceMock } from '../../__mocks__/services';
import { LogsExplorerTabs, LogsExplorerTabsProps } from './logs_explorer_tabs';
import { DISCOVER_APP_LOCATOR } from '../../../common';
-import { ALL_DATASETS_LOCATOR_ID } from '@kbn/deeplinks-observability';
+
+const mockSetLastUsedViewer = jest.fn();
+jest.mock('react-use/lib/useLocalStorage', () => {
+ return jest.fn((key: string, _initialValue: string) => {
+ return [undefined, mockSetLastUsedViewer]; // Always use undefined as the initial value
+ });
+});
const createMockLocator = (id: string) => ({
navigate: jest.fn(),
@@ -46,11 +57,12 @@ describe('LogsExplorerTabs', () => {
},
} as unknown as typeof discoverServiceMock;
- render();
+ const { unmount } = render();
return {
mockDiscoverLocator,
mockLogsExplorerLocator,
+ unmount,
};
};
@@ -86,4 +98,14 @@ describe('LogsExplorerTabs', () => {
await userEvent.click(getDiscoverTab());
expect(mockDiscoverLocator.navigate).toHaveBeenCalledWith({});
});
+
+ it('should update the last used viewer in local storage for selectedTab', async () => {
+ const { unmount } = renderTabs('discover');
+ expect(mockSetLastUsedViewer).toHaveBeenCalledWith(DISCOVER_APP_ID);
+
+ unmount();
+ mockSetLastUsedViewer.mockClear();
+ renderTabs('logs-explorer');
+ expect(mockSetLastUsedViewer).toHaveBeenCalledWith(OBSERVABILITY_LOGS_EXPLORER_APP_ID);
+ });
});
diff --git a/src/plugins/discover/public/components/logs_explorer_tabs/logs_explorer_tabs.tsx b/src/plugins/discover/public/components/logs_explorer_tabs/logs_explorer_tabs.tsx
index 1eec001464d2a..c7082c21344ac 100644
--- a/src/plugins/discover/public/components/logs_explorer_tabs/logs_explorer_tabs.tsx
+++ b/src/plugins/discover/public/components/logs_explorer_tabs/logs_explorer_tabs.tsx
@@ -8,9 +8,16 @@
*/
import { EuiTab, EuiTabs, useEuiTheme } from '@elastic/eui';
-import { AllDatasetsLocatorParams, ALL_DATASETS_LOCATOR_ID } from '@kbn/deeplinks-observability';
+import { DISCOVER_APP_ID } from '@kbn/deeplinks-analytics';
+import {
+ AllDatasetsLocatorParams,
+ ALL_DATASETS_LOCATOR_ID,
+ OBS_LOGS_EXPLORER_LOGS_VIEWER_KEY,
+ OBSERVABILITY_LOGS_EXPLORER_APP_ID,
+} from '@kbn/deeplinks-observability';
import { i18n } from '@kbn/i18n';
-import React, { MouseEvent } from 'react';
+import React, { MouseEvent, useEffect } from 'react';
+import useLocalStorage from 'react-use/lib/useLocalStorage';
import { DiscoverAppLocatorParams, DISCOVER_APP_LOCATOR } from '../../../common';
import type { DiscoverServices } from '../../build_services';
@@ -29,6 +36,10 @@ export const LogsExplorerTabs = ({ services, selectedTab }: LogsExplorerTabsProp
const discoverUrl = discoverLocator?.getRedirectUrl(emptyParams);
const logsExplorerUrl = logsExplorerLocator?.getRedirectUrl(emptyParams);
+ const [lastUsedViewer, setLastUsedViewer] = useLocalStorage<
+ typeof DISCOVER_APP_ID | typeof OBSERVABILITY_LOGS_EXPLORER_APP_ID
+ >(OBS_LOGS_EXPLORER_LOGS_VIEWER_KEY, OBSERVABILITY_LOGS_EXPLORER_APP_ID);
+
const navigateToDiscover = createNavigateHandler(() => {
if (selectedTab !== 'discover') {
discoverLocator?.navigate(emptyParams);
@@ -41,6 +52,16 @@ export const LogsExplorerTabs = ({ services, selectedTab }: LogsExplorerTabsProp
}
});
+ useEffect(() => {
+ if (selectedTab === 'discover' && lastUsedViewer !== DISCOVER_APP_ID) {
+ setLastUsedViewer(DISCOVER_APP_ID);
+ }
+
+ if (selectedTab === 'logs-explorer' && lastUsedViewer !== OBSERVABILITY_LOGS_EXPLORER_APP_ID) {
+ setLastUsedViewer(OBSERVABILITY_LOGS_EXPLORER_APP_ID);
+ }
+ }, [setLastUsedViewer, lastUsedViewer, selectedTab]);
+
return (
(params) => {
const additionalControls = prev(params) || [];
- const { query } = params;
+ const { updateESQLQuery, query } = params;
const isDegradedDocsControlEnabled = isOfAggregateQueryType(query)
? queryContainsMetadataIgnored(query)
: true;
+ const addIgnoredMetadataToQuery = updateESQLQuery
+ ? () => {
+ updateESQLQuery((prevQuery) => {
+ const { root } = parse(prevQuery);
+ // Add _ignored field to metadata directive if not present
+ mutate.commands.from.metadata.upsert(root, IGNORED_FIELD);
+
+ return BasicPrettyPrinter.print(root);
+ });
+ }
+ : undefined;
+
return [
...additionalControls,
- createDegradedDocsControl({ enabled: isDegradedDocsControlEnabled }),
+ createDegradedDocsControl({
+ enabled: isDegradedDocsControlEnabled,
+ addIgnoredMetadataToQuery,
+ }),
createStacktraceControl(),
];
};
diff --git a/src/plugins/discover/public/context_awareness/types.ts b/src/plugins/discover/public/context_awareness/types.ts
index 63c23bbb3d4b1..5797a9023f93e 100644
--- a/src/plugins/discover/public/context_awareness/types.ts
+++ b/src/plugins/discover/public/context_awareness/types.ts
@@ -23,6 +23,7 @@ import type { Trigger } from '@kbn/ui-actions-plugin/public';
import { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
import type { DiscoverDataSource } from '../../common/data_sources';
import type { DiscoverAppState } from '../application/main/state_management/discover_app_state_container';
+import { DiscoverStateContainer } from '../application/main/state_management/discover_state';
/**
* Supports customizing the Discover document viewer flyout
@@ -133,6 +134,10 @@ export interface RowControlsExtensionParams {
* The current data view
*/
dataView: DataView;
+ /**
+ * The current query
+ */
+ updateESQLQuery?: DiscoverStateContainer['actions']['updateESQLQuery'];
/**
* The current query
*/
diff --git a/src/plugins/discover/tsconfig.json b/src/plugins/discover/tsconfig.json
index fb6ec66985647..1f3ed529d804b 100644
--- a/src/plugins/discover/tsconfig.json
+++ b/src/plugins/discover/tsconfig.json
@@ -100,7 +100,8 @@
"@kbn/management-settings-ids",
"@kbn/react-hooks",
"@kbn/logs-data-access-plugin",
- "@kbn/core-lifecycle-browser"
+ "@kbn/core-lifecycle-browser",
+ "@kbn/esql-ast"
],
"exclude": [
"target/**/*"
diff --git a/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx b/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx
index db9b322c077cd..77ed3a1bc2f1d 100644
--- a/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx
+++ b/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx
@@ -42,13 +42,12 @@ test('ErrorEmbeddable renders an embeddable with markdown message', async () =>
- External link
-
+ role="presentation"
+ />
- (opens in a new tab or window)
+ (external, opens in a new tab or window)
`);
diff --git a/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts
index 06866afc65caa..3c40e197dbad3 100644
--- a/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts
+++ b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts
@@ -159,6 +159,7 @@ export const applicationUsageSchema = {
monitoring: commonSchema,
'observability-log-explorer': commonSchema,
'observability-logs-explorer': commonSchema,
+ 'last-used-logs-viewer': commonSchema,
'observability-overview': commonSchema,
observabilityOnboarding: commonSchema,
observabilityAIAssistant: commonSchema,
diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts
index e5ddfbe4dd037..6b3db9460eb7c 100644
--- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts
+++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts
@@ -22,6 +22,10 @@ export const stackManagementSchema: MakeSchemaFrom = {
_meta: { description: 'Non-default value of setting.' },
},
},
+ 'securitySolution:maxUnassociatedNotes': {
+ type: 'integer',
+ _meta: { description: 'The maximum number of allowed unassociated notes' },
+ },
'securitySolution:defaultThreatIndex': {
type: 'keyword',
_meta: { description: 'Default value of the setting was changed.' },
diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts
index 2acb487e7ed08..92076ebc302e2 100644
--- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts
+++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts
@@ -183,5 +183,6 @@ export interface UsageStats {
'aiAssistant:preferredAIAssistantType': string;
'observability:profilingFetchTopNFunctionsFromStacktraces': boolean;
'securitySolution:excludedDataTiersForRuleExecution': string[];
+ 'securitySolution:maxUnassociatedNotes': number;
'observability:searchExcludedDataTiers': string[];
}
diff --git a/src/plugins/links/public/components/dashboard_link/dashboard_link_component.test.tsx b/src/plugins/links/public/components/dashboard_link/dashboard_link_component.test.tsx
index d60ba248d14fc..b245870a8757a 100644
--- a/src/plugins/links/public/components/dashboard_link/dashboard_link_component.test.tsx
+++ b/src/plugins/links/public/components/dashboard_link/dashboard_link_component.test.tsx
@@ -10,7 +10,7 @@
import React from 'react';
import { DEFAULT_DASHBOARD_DRILLDOWN_OPTIONS } from '@kbn/presentation-util-plugin/public';
-import { createEvent, fireEvent, render, screen, within } from '@testing-library/react';
+import { createEvent, fireEvent, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { LINKS_VERTICAL_LAYOUT } from '../../../common/content_management';
@@ -75,7 +75,7 @@ describe('Dashboard link component', () => {
expect(link).toHaveTextContent('Dashboard 1');
// does not render external link icon
- const externalIcon = within(link).queryByText('External link');
+ const externalIcon = link.querySelector('[data-euiicon-type="popout"]');
expect(externalIcon).toBeNull();
// calls `navigate` on click
@@ -122,8 +122,8 @@ describe('Dashboard link component', () => {
const link = screen.getByTestId('dashboardLink--foo');
expect(link).toBeInTheDocument();
// external link icon is rendered
- const externalIcon = within(link).getByText('External link');
- expect(externalIcon?.getAttribute('data-euiicon-type')).toBe('popout');
+ const externalIcon = link.querySelector('[data-euiicon-type="popout"]');
+ expect(externalIcon).toBeInTheDocument();
// calls `window.open`
await userEvent.click(link);
diff --git a/src/plugins/links/public/components/external_link/external_link_component.test.tsx b/src/plugins/links/public/components/external_link/external_link_component.test.tsx
index 4230e28b702e7..b80cf30e89f39 100644
--- a/src/plugins/links/public/components/external_link/external_link_component.test.tsx
+++ b/src/plugins/links/public/components/external_link/external_link_component.test.tsx
@@ -10,7 +10,7 @@
import React from 'react';
import userEvent from '@testing-library/user-event';
-import { createEvent, fireEvent, render, screen, within } from '@testing-library/react';
+import { createEvent, fireEvent, render, screen } from '@testing-library/react';
import { LINKS_VERTICAL_LAYOUT } from '../../../common/content_management';
import { ExternalLinkComponent } from './external_link_component';
import { coreServices } from '../../services/kibana_services';
@@ -39,8 +39,8 @@ describe('external link component', () => {
const link = await screen.findByTestId('externalLink--foo');
expect(link).toBeInTheDocument();
- const externalIcon = within(link).getByText('External link');
- expect(externalIcon.getAttribute('data-euiicon-type')).toBe('popout');
+ const externalIcon = link.querySelector('[data-euiicon-type="popout"]');
+ expect(externalIcon).toBeInTheDocument();
await userEvent.click(link);
expect(window.open).toHaveBeenCalledWith('https://example.com', '_blank');
});
@@ -52,8 +52,8 @@ describe('external link component', () => {
};
render();
const link = await screen.findByTestId('externalLink--foo');
- const externalIcon = within(link).getByText('External link');
- expect(externalIcon?.getAttribute('data-euiicon-type')).toBe('popout');
+ const externalIcon = link.querySelector('[data-euiicon-type="popout"]');
+ expect(externalIcon).toBeInTheDocument();
});
test('modified click does not trigger event.preventDefault', async () => {
diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap
index 829472941701c..0c5045a1c8662 100644
--- a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap
+++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap
@@ -37,13 +37,12 @@ exports[`NotFoundErrors component renders correctly for index-pattern type 1`] =
- External link
-
+ role="presentation"
+ />
- (opens in a new tab or window)
+ (external, opens in a new tab or window)
to fix it — otherwise click the delete button above.
@@ -89,13 +88,12 @@ exports[`NotFoundErrors component renders correctly for index-pattern-field type
- External link
-
+ role="presentation"
+ />
- (opens in a new tab or window)
+ (external, opens in a new tab or window)
to fix it — otherwise click the delete button above.
@@ -141,13 +139,12 @@ exports[`NotFoundErrors component renders correctly for search type 1`] = `
- External link
-
+ role="presentation"
+ />
- (opens in a new tab or window)
+ (external, opens in a new tab or window)
to fix it — otherwise click the delete button above.
@@ -191,13 +188,12 @@ exports[`NotFoundErrors component renders correctly for unknown type 1`] = `
- External link
-
+ role="presentation"
+ />
- (opens in a new tab or window)
+ (external, opens in a new tab or window)
to fix it — otherwise click the delete button above.
diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.test.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.test.tsx
index 72604fbda1fc3..41919c9172e56 100644
--- a/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.test.tsx
+++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.test.tsx
@@ -26,7 +26,7 @@ describe('NotFoundErrors component', () => {
const callOut = mounted.find('EuiCallOut');
expect(callOut.render()).toMatchSnapshot();
expect(mounted.text()).toMatchInlineSnapshot(
- `"There is a problem with this saved objectThe saved search associated with this object no longer exists.If you know what this error means, you can use the Saved objects APIsExternal link(opens in a new tab or window) to fix it — otherwise click the delete button above."`
+ `"There is a problem with this saved objectThe saved search associated with this object no longer exists.If you know what this error means, you can use the Saved objects APIs(external, opens in a new tab or window) to fix it — otherwise click the delete button above."`
);
});
@@ -35,7 +35,7 @@ describe('NotFoundErrors component', () => {
const callOut = mounted.find('EuiCallOut');
expect(callOut.render()).toMatchSnapshot();
expect(mounted.text()).toMatchInlineSnapshot(
- `"There is a problem with this saved objectThe data view associated with this object no longer exists.If you know what this error means, you can use the Saved objects APIsExternal link(opens in a new tab or window) to fix it — otherwise click the delete button above."`
+ `"There is a problem with this saved objectThe data view associated with this object no longer exists.If you know what this error means, you can use the Saved objects APIs(external, opens in a new tab or window) to fix it — otherwise click the delete button above."`
);
});
@@ -44,7 +44,7 @@ describe('NotFoundErrors component', () => {
const callOut = mounted.find('EuiCallOut');
expect(callOut.render()).toMatchSnapshot();
expect(mounted.text()).toMatchInlineSnapshot(
- `"There is a problem with this saved objectA field associated with this object no longer exists in the data view.If you know what this error means, you can use the Saved objects APIsExternal link(opens in a new tab or window) to fix it — otherwise click the delete button above."`
+ `"There is a problem with this saved objectA field associated with this object no longer exists in the data view.If you know what this error means, you can use the Saved objects APIs(external, opens in a new tab or window) to fix it — otherwise click the delete button above."`
);
});
@@ -53,7 +53,7 @@ describe('NotFoundErrors component', () => {
const callOut = mounted.find('EuiCallOut');
expect(callOut.render()).toMatchSnapshot();
expect(mounted.text()).toMatchInlineSnapshot(
- `"There is a problem with this saved objectIf you know what this error means, you can use the Saved objects APIsExternal link(opens in a new tab or window) to fix it — otherwise click the delete button above."`
+ `"There is a problem with this saved objectIf you know what this error means, you can use the Saved objects APIs(external, opens in a new tab or window) to fix it — otherwise click the delete button above."`
);
});
});
diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json
index 830cffc17cf1c..a3e46f5684135 100644
--- a/src/plugins/telemetry/schema/oss_plugins.json
+++ b/src/plugins/telemetry/schema/oss_plugins.json
@@ -5111,6 +5111,137 @@
}
}
},
+ "last-used-logs-viewer": {
+ "properties": {
+ "appId": {
+ "type": "keyword",
+ "_meta": {
+ "description": "The application being tracked"
+ }
+ },
+ "viewId": {
+ "type": "keyword",
+ "_meta": {
+ "description": "Always `main`"
+ }
+ },
+ "clicks_total": {
+ "type": "long",
+ "_meta": {
+ "description": "General number of clicks in the application since we started counting them"
+ }
+ },
+ "clicks_7_days": {
+ "type": "long",
+ "_meta": {
+ "description": "General number of clicks in the application over the last 7 days"
+ }
+ },
+ "clicks_30_days": {
+ "type": "long",
+ "_meta": {
+ "description": "General number of clicks in the application over the last 30 days"
+ }
+ },
+ "clicks_90_days": {
+ "type": "long",
+ "_meta": {
+ "description": "General number of clicks in the application over the last 90 days"
+ }
+ },
+ "minutes_on_screen_total": {
+ "type": "float",
+ "_meta": {
+ "description": "Minutes the application is active and on-screen since we started counting them."
+ }
+ },
+ "minutes_on_screen_7_days": {
+ "type": "float",
+ "_meta": {
+ "description": "Minutes the application is active and on-screen over the last 7 days"
+ }
+ },
+ "minutes_on_screen_30_days": {
+ "type": "float",
+ "_meta": {
+ "description": "Minutes the application is active and on-screen over the last 30 days"
+ }
+ },
+ "minutes_on_screen_90_days": {
+ "type": "float",
+ "_meta": {
+ "description": "Minutes the application is active and on-screen over the last 90 days"
+ }
+ },
+ "views": {
+ "type": "array",
+ "items": {
+ "properties": {
+ "appId": {
+ "type": "keyword",
+ "_meta": {
+ "description": "The application being tracked"
+ }
+ },
+ "viewId": {
+ "type": "keyword",
+ "_meta": {
+ "description": "The application view being tracked"
+ }
+ },
+ "clicks_total": {
+ "type": "long",
+ "_meta": {
+ "description": "General number of clicks in the application sub view since we started counting them"
+ }
+ },
+ "clicks_7_days": {
+ "type": "long",
+ "_meta": {
+ "description": "General number of clicks in the active application sub view over the last 7 days"
+ }
+ },
+ "clicks_30_days": {
+ "type": "long",
+ "_meta": {
+ "description": "General number of clicks in the active application sub view over the last 30 days"
+ }
+ },
+ "clicks_90_days": {
+ "type": "long",
+ "_meta": {
+ "description": "General number of clicks in the active application sub view over the last 90 days"
+ }
+ },
+ "minutes_on_screen_total": {
+ "type": "float",
+ "_meta": {
+ "description": "Minutes the application sub view is active and on-screen since we started counting them."
+ }
+ },
+ "minutes_on_screen_7_days": {
+ "type": "float",
+ "_meta": {
+ "description": "Minutes the application is active and on-screen active application sub view over the last 7 days"
+ }
+ },
+ "minutes_on_screen_30_days": {
+ "type": "float",
+ "_meta": {
+ "description": "Minutes the application is active and on-screen active application sub view over the last 30 days"
+ }
+ },
+ "minutes_on_screen_90_days": {
+ "type": "float",
+ "_meta": {
+ "description": "Minutes the application is active and on-screen active application sub view over the last 90 days"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"observability-overview": {
"properties": {
"appId": {
@@ -9757,6 +9888,12 @@
}
}
},
+ "securitySolution:maxUnassociatedNotes": {
+ "type": "integer",
+ "_meta": {
+ "description": "The maximum number of allowed unassociated notes"
+ }
+ },
"securitySolution:defaultThreatIndex": {
"type": "keyword",
"_meta": {
@@ -9919,7 +10056,7 @@
"description": "Non-default value of setting."
}
},
- "securitySolution:enableVisualizationsInFlyout":{
+ "securitySolution:enableVisualizationsInFlyout": {
"type": "boolean",
"_meta": {
"description": "Non-default value of setting."
diff --git a/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx b/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx
index b3c49e27c6011..0d6aa1e75fe80 100644
--- a/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx
+++ b/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx
@@ -11,6 +11,7 @@ import React, { useCallback, useMemo } from 'react';
import { EuiSelectableOption } from '@elastic/eui';
import { FieldIcon, getFieldIconProps, comboBoxFieldOptionMatcher } from '@kbn/field-utils';
import { css } from '@emotion/react';
+import { isESQLColumnGroupable } from '@kbn/esql-utils';
import { type DataView, DataViewField } from '@kbn/data-views-plugin/common';
import type { DatatableColumn } from '@kbn/expressions-plugin/common';
import { convertDatatableColumnToDataViewFieldSpec } from '@kbn/data-view-utils';
@@ -34,10 +35,10 @@ export interface BreakdownFieldSelectorProps {
const mapToDropdownFields = (dataView: DataView, esqlColumns?: DatatableColumn[]) => {
if (esqlColumns) {
return (
+ // filter out unsupported field types and counter time series metrics
esqlColumns
+ .filter(isESQLColumnGroupable)
.map((column) => new DataViewField(convertDatatableColumnToDataViewFieldSpec(column)))
- // filter out unsupported field types
- .filter((field) => field.type !== 'unknown')
);
}
diff --git a/src/plugins/unified_histogram/public/services/lens_vis_service.attributes.test.ts b/src/plugins/unified_histogram/public/services/lens_vis_service.attributes.test.ts
index bc76a0163c8be..75734387a9368 100644
--- a/src/plugins/unified_histogram/public/services/lens_vis_service.attributes.test.ts
+++ b/src/plugins/unified_histogram/public/services/lens_vis_service.attributes.test.ts
@@ -674,7 +674,8 @@ describe('LensVisService attributes', () => {
},
],
"query": Object {
- "esql": "from logstash-* | limit 10",
+ "esql": "from logstash-* | limit 10
+ | EVAL timestamp=DATE_TRUNC(10 minute, timestamp) | stats results = count(*) by timestamp | rename timestamp as \`timestamp every 10 minute\`",
},
"visualization": Object {
"gridConfig": Object {
@@ -706,7 +707,7 @@ describe('LensVisService attributes', () => {
"timeField": "timestamp",
"timeInterval": undefined,
},
- "suggestionType": "lensSuggestion",
+ "suggestionType": "histogramForESQL",
}
`);
});
diff --git a/src/plugins/unified_histogram/public/services/lens_vis_service.suggestions.test.ts b/src/plugins/unified_histogram/public/services/lens_vis_service.suggestions.test.ts
index 1719adebe7a49..09ee2a68ec248 100644
--- a/src/plugins/unified_histogram/public/services/lens_vis_service.suggestions.test.ts
+++ b/src/plugins/unified_histogram/public/services/lens_vis_service.suggestions.test.ts
@@ -254,6 +254,37 @@ describe('LensVisService suggestions', () => {
expect(lensVis.visContext?.attributes.state.query).toStrictEqual(histogramQuery);
});
+ test('should return histogramSuggestion even if suggestions returned by the api', async () => {
+ const lensVis = await getLensVisMock({
+ filters: [],
+ query: { esql: 'from the-data-view | limit 100' },
+ dataView: dataViewMock,
+ timeInterval: 'auto',
+ timeRange: {
+ from: '2023-09-03T08:00:00.000Z',
+ to: '2023-09-04T08:56:28.274Z',
+ },
+ breakdownField: undefined,
+ columns: [
+ {
+ id: 'var0',
+ name: 'var0',
+ meta: {
+ type: 'number',
+ },
+ },
+ ],
+ isPlainRecord: true,
+ allSuggestions: allSuggestionsMock,
+ hasHistogramSuggestionForESQL: true,
+ });
+
+ expect(lensVis.currentSuggestionContext?.type).toBe(
+ UnifiedHistogramSuggestionType.histogramForESQL
+ );
+ expect(lensVis.currentSuggestionContext?.suggestion).toBeDefined();
+ });
+
test('should return histogramSuggestion if no suggestions returned by the api with a geo point breakdown field correctly', async () => {
const lensVis = await getLensVisMock({
filters: [],
diff --git a/src/plugins/unified_histogram/public/services/lens_vis_service.ts b/src/plugins/unified_histogram/public/services/lens_vis_service.ts
index 25bb8be6f6242..e48ebc6459071 100644
--- a/src/plugins/unified_histogram/public/services/lens_vis_service.ts
+++ b/src/plugins/unified_histogram/public/services/lens_vis_service.ts
@@ -235,7 +235,7 @@ export class LensVisService {
let currentSuggestion: Suggestion | undefined;
// takes lens suggestions if provided
- const availableSuggestionsWithType: Array<{
+ let availableSuggestionsWithType: Array<{
suggestion: UnifiedHistogramSuggestionContext['suggestion'];
type: UnifiedHistogramSuggestionType;
}> = [];
@@ -254,6 +254,9 @@ export class LensVisService {
breakdownField,
});
if (histogramSuggestionForESQL) {
+ // In case if histogram suggestion, we want to empty the array and push the new suggestion
+ // to ensure that only the histogram suggestion is available
+ availableSuggestionsWithType = [];
availableSuggestionsWithType.push({
suggestion: histogramSuggestionForESQL,
type: UnifiedHistogramSuggestionType.histogramForESQL,
diff --git a/src/plugins/vis_types/vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap b/src/plugins/vis_types/vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap
index 05da269b239b0..c9a3c41edca93 100644
--- a/src/plugins/vis_types/vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap
+++ b/src/plugins/vis_types/vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap
@@ -2,4 +2,4 @@
exports[`VisLegend Component Legend closed should match the snapshot 1`] = `""`;
-exports[`VisLegend Component Legend open should match the snapshot 1`] = `"
"`;
+exports[`VisLegend Component Legend open should match the snapshot 1`] = `"
"`;
diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts
index 72d1f97011274..02355c97823cf 100644
--- a/test/plugin_functional/test_suites/core_plugins/rendering.ts
+++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts
@@ -302,6 +302,16 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
'xpack.ml.ad.enabled (boolean)',
'xpack.ml.dfa.enabled (boolean)',
'xpack.ml.nlp.enabled (boolean)',
+ 'xpack.ml.nlp.modelDeployment.allowStaticAllocations (boolean)',
+ 'xpack.ml.nlp.modelDeployment.vCPURange.high.max (number)',
+ 'xpack.ml.nlp.modelDeployment.vCPURange.high.min (number)',
+ 'xpack.ml.nlp.modelDeployment.vCPURange.high.static (number?)',
+ 'xpack.ml.nlp.modelDeployment.vCPURange.low.max (number)',
+ 'xpack.ml.nlp.modelDeployment.vCPURange.low.min (number)',
+ 'xpack.ml.nlp.modelDeployment.vCPURange.low.static (number?)',
+ 'xpack.ml.nlp.modelDeployment.vCPURange.medium.max (number)',
+ 'xpack.ml.nlp.modelDeployment.vCPURange.medium.min (number)',
+ 'xpack.ml.nlp.modelDeployment.vCPURange.medium.static (number?)',
'xpack.osquery.actionEnabled (boolean?)',
'xpack.remote_clusters.ui.enabled (boolean?)',
/**
diff --git a/tsconfig.base.json b/tsconfig.base.json
index 12df74345a444..dbd9b7b8b1e56 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -174,10 +174,10 @@
"@kbn/cloud-links-plugin/*": ["x-pack/plugins/cloud_integrations/cloud_links/*"],
"@kbn/cloud-plugin": ["x-pack/plugins/cloud"],
"@kbn/cloud-plugin/*": ["x-pack/plugins/cloud/*"],
- "@kbn/cloud-security-posture": ["x-pack/packages/kbn-cloud-security-posture"],
- "@kbn/cloud-security-posture/*": ["x-pack/packages/kbn-cloud-security-posture/*"],
- "@kbn/cloud-security-posture-common": ["x-pack/packages/kbn-cloud-security-posture-common"],
- "@kbn/cloud-security-posture-common/*": ["x-pack/packages/kbn-cloud-security-posture-common/*"],
+ "@kbn/cloud-security-posture": ["x-pack/packages/kbn-cloud-security-posture/public"],
+ "@kbn/cloud-security-posture/*": ["x-pack/packages/kbn-cloud-security-posture/public/*"],
+ "@kbn/cloud-security-posture-common": ["x-pack/packages/kbn-cloud-security-posture/common"],
+ "@kbn/cloud-security-posture-common/*": ["x-pack/packages/kbn-cloud-security-posture/common/*"],
"@kbn/cloud-security-posture-graph": ["x-pack/packages/kbn-cloud-security-posture/graph"],
"@kbn/cloud-security-posture-graph/*": ["x-pack/packages/kbn-cloud-security-posture/graph/*"],
"@kbn/cloud-security-posture-plugin": ["x-pack/plugins/cloud_security_posture"],
@@ -926,6 +926,8 @@
"@kbn/feature-usage-test-plugin/*": ["x-pack/test/plugin_api_integration/plugins/feature_usage_test/*"],
"@kbn/features-plugin": ["x-pack/plugins/features"],
"@kbn/features-plugin/*": ["x-pack/plugins/features/*"],
+ "@kbn/features-provider-plugin": ["x-pack/test/security_api_integration/plugins/features_provider"],
+ "@kbn/features-provider-plugin/*": ["x-pack/test/security_api_integration/plugins/features_provider/*"],
"@kbn/fec-alerts-test-plugin": ["x-pack/test/functional_execution_context/plugins/alerts"],
"@kbn/fec-alerts-test-plugin/*": ["x-pack/test/functional_execution_context/plugins/alerts/*"],
"@kbn/field-formats-example-plugin": ["examples/field_formats_example"],
@@ -1550,6 +1552,8 @@
"@kbn/security-api-key-management/*": ["x-pack/packages/security/api_key_management/*"],
"@kbn/security-authorization-core": ["x-pack/packages/security/authorization_core"],
"@kbn/security-authorization-core/*": ["x-pack/packages/security/authorization_core/*"],
+ "@kbn/security-authorization-core-common": ["x-pack/packages/security/authorization_core_common"],
+ "@kbn/security-authorization-core-common/*": ["x-pack/packages/security/authorization_core_common/*"],
"@kbn/security-form-components": ["x-pack/packages/security/form_components"],
"@kbn/security-form-components/*": ["x-pack/packages/security/form_components/*"],
"@kbn/security-hardening": ["packages/kbn-security-hardening"],
diff --git a/x-pack/packages/kbn-cloud-security-posture/README.md b/x-pack/packages/kbn-cloud-security-posture/README.md
index 29a14fbeb825b..cf5450f059773 100644
--- a/x-pack/packages/kbn-cloud-security-posture/README.md
+++ b/x-pack/packages/kbn-cloud-security-posture/README.md
@@ -4,6 +4,20 @@ This package includes
- Hooks that's used on Flyout component that's used in Alerts page on Security Solution Plugins as well as components on CSP plugin
- Utilities and types thats used for the Hooks above as well as in CSP plugins
+The code is under the `public` folder.
+
+# @kbn/cloud-security-posture-common
+
+Common types of `cloud-security-posture` plugin.
+
+The code is under the `common` folder.
+
+# @kbn/cloud-security-posture-graph
+
+Reusable graph component to present entities' relationships and exploration.
+
+The code is under the `graph` folder.
+
## Storybook
General look of the component can be checked visually running the following storybook:
@@ -11,6 +25,6 @@ General look of the component can be checked visually running the following stor
Note that all the interactions are mocked.
-## Maintainers
+# Maintainers
Maintained by the Cloud Security Team
\ No newline at end of file
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/README.md b/x-pack/packages/kbn-cloud-security-posture/common/README.md
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/README.md
rename to x-pack/packages/kbn-cloud-security-posture/common/README.md
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/constants.ts b/x-pack/packages/kbn-cloud-security-posture/common/constants.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/constants.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/constants.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/index.ts b/x-pack/packages/kbn-cloud-security-posture/common/index.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/index.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/index.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/jest.config.js b/x-pack/packages/kbn-cloud-security-posture/common/jest.config.js
similarity index 77%
rename from x-pack/packages/kbn-cloud-security-posture-common/jest.config.js
rename to x-pack/packages/kbn-cloud-security-posture/common/jest.config.js
index d6f06d2bcc21c..62d5a239b9dc9 100644
--- a/x-pack/packages/kbn-cloud-security-posture-common/jest.config.js
+++ b/x-pack/packages/kbn-cloud-security-posture/common/jest.config.js
@@ -7,6 +7,6 @@
module.exports = {
preset: '@kbn/test',
- rootDir: '../../..',
- roots: ['/x-pack/packages/kbn-cloud-security-posture-common'],
+ rootDir: '../../../..',
+ roots: ['/x-pack/packages/kbn-cloud-security-posture/common'],
};
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/kibana.jsonc b/x-pack/packages/kbn-cloud-security-posture/common/kibana.jsonc
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/kibana.jsonc
rename to x-pack/packages/kbn-cloud-security-posture/common/kibana.jsonc
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/package.json b/x-pack/packages/kbn-cloud-security-posture/common/package.json
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/package.json
rename to x-pack/packages/kbn-cloud-security-posture/common/package.json
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/schema/graph/index.ts b/x-pack/packages/kbn-cloud-security-posture/common/schema/graph/index.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/schema/graph/index.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/schema/graph/index.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/schema/graph/latest.ts b/x-pack/packages/kbn-cloud-security-posture/common/schema/graph/latest.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/schema/graph/latest.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/schema/graph/latest.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/schema/graph/v1.ts b/x-pack/packages/kbn-cloud-security-posture/common/schema/graph/v1.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/schema/graph/v1.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/schema/graph/v1.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/schema/rules/index.ts b/x-pack/packages/kbn-cloud-security-posture/common/schema/rules/index.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/schema/rules/index.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/schema/rules/index.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/schema/rules/latest.ts b/x-pack/packages/kbn-cloud-security-posture/common/schema/rules/latest.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/schema/rules/latest.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/schema/rules/latest.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/schema/rules/v1.ts b/x-pack/packages/kbn-cloud-security-posture/common/schema/rules/v1.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/schema/rules/v1.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/schema/rules/v1.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/schema/rules/v2.ts b/x-pack/packages/kbn-cloud-security-posture/common/schema/rules/v2.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/schema/rules/v2.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/schema/rules/v2.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/schema/rules/v3.ts b/x-pack/packages/kbn-cloud-security-posture/common/schema/rules/v3.ts
similarity index 98%
rename from x-pack/packages/kbn-cloud-security-posture-common/schema/rules/v3.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/schema/rules/v3.ts
index a00bf1a8077e6..353fe093d64a1 100644
--- a/x-pack/packages/kbn-cloud-security-posture-common/schema/rules/v3.ts
+++ b/x-pack/packages/kbn-cloud-security-posture/common/schema/rules/v3.ts
@@ -37,6 +37,7 @@ export const cspBenchmarkRuleMetadataSchema = schema.object({
profile_applicability: schema.string(),
rationale: schema.string(),
references: schema.maybe(schema.string()),
+ reference: schema.maybe(schema.string()),
rego_rule_id: schema.string(),
remediation: schema.string(),
section: schema.string(),
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/schema/rules/v4.ts b/x-pack/packages/kbn-cloud-security-posture/common/schema/rules/v4.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/schema/rules/v4.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/schema/rules/v4.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/schema/rules/v5.ts b/x-pack/packages/kbn-cloud-security-posture/common/schema/rules/v5.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/schema/rules/v5.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/schema/rules/v5.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/schema/vulnerabilities/csp_vulnerability_finding.ts b/x-pack/packages/kbn-cloud-security-posture/common/schema/vulnerabilities/csp_vulnerability_finding.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/schema/vulnerabilities/csp_vulnerability_finding.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/schema/vulnerabilities/csp_vulnerability_finding.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/schema/vulnerabilities/latest.ts b/x-pack/packages/kbn-cloud-security-posture/common/schema/vulnerabilities/latest.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/schema/vulnerabilities/latest.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/schema/vulnerabilities/latest.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/tsconfig.json b/x-pack/packages/kbn-cloud-security-posture/common/tsconfig.json
similarity index 88%
rename from x-pack/packages/kbn-cloud-security-posture-common/tsconfig.json
rename to x-pack/packages/kbn-cloud-security-posture/common/tsconfig.json
index c6bdf82c1d223..c7cf1e9208bfc 100644
--- a/x-pack/packages/kbn-cloud-security-posture-common/tsconfig.json
+++ b/x-pack/packages/kbn-cloud-security-posture/common/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "../../../tsconfig.base.json",
+ "extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/types/benchmark.ts b/x-pack/packages/kbn-cloud-security-posture/common/types/benchmark.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/types/benchmark.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/types/benchmark.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/types/findings.ts b/x-pack/packages/kbn-cloud-security-posture/common/types/findings.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/types/findings.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/types/findings.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/types/graph/index.ts b/x-pack/packages/kbn-cloud-security-posture/common/types/graph/index.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/types/graph/index.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/types/graph/index.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/types/graph/latest.ts b/x-pack/packages/kbn-cloud-security-posture/common/types/graph/latest.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/types/graph/latest.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/types/graph/latest.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/types/graph/v1.ts b/x-pack/packages/kbn-cloud-security-posture/common/types/graph/v1.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/types/graph/v1.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/types/graph/v1.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/types/status.ts b/x-pack/packages/kbn-cloud-security-posture/common/types/status.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/types/status.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/types/status.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/types/vulnerabilities.ts b/x-pack/packages/kbn-cloud-security-posture/common/types/vulnerabilities.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/types/vulnerabilities.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/types/vulnerabilities.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/utils/get_abbreviated_number.test.ts b/x-pack/packages/kbn-cloud-security-posture/common/utils/get_abbreviated_number.test.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/utils/get_abbreviated_number.test.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/utils/get_abbreviated_number.test.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/utils/get_abbreviated_number.ts b/x-pack/packages/kbn-cloud-security-posture/common/utils/get_abbreviated_number.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/utils/get_abbreviated_number.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/utils/get_abbreviated_number.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/utils/helpers.test.ts b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.test.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/utils/helpers.test.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.test.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/utils/helpers.ts b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/utils/helpers.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture-common/utils/ui_metrics.ts b/x-pack/packages/kbn-cloud-security-posture/common/utils/ui_metrics.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture-common/utils/ui_metrics.ts
rename to x-pack/packages/kbn-cloud-security-posture/common/utils/ui_metrics.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/README.md b/x-pack/packages/kbn-cloud-security-posture/graph/README.md
index 2e495ce4e6be8..c67ca622fe414 100644
--- a/x-pack/packages/kbn-cloud-security-posture/graph/README.md
+++ b/x-pack/packages/kbn-cloud-security-posture/graph/README.md
@@ -2,12 +2,12 @@
## Motivation
-The idea behind this package is to have a reusable graph component, embedding the features available to alerts flyout in
+The idea behind this package is to have a reusable graph component, embedding the features available to the alert's flyout in
security solution plugin.
## How to use this
-Standalone examples will follow. In the meantime checkout storybook to view the graphs progress.
+Standalone examples will follow. In the meantime check out storybook to view the graph's progress.
## The most important public api members
diff --git a/x-pack/packages/kbn-cloud-security-posture/storybook/config/constants.ts b/x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/constants.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/storybook/config/constants.ts
rename to x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/constants.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/storybook/config/index.ts b/x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/index.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/storybook/config/index.ts
rename to x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/index.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/storybook/config/main.ts b/x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/main.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/storybook/config/main.ts
rename to x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/main.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/storybook/config/manager.ts b/x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/manager.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/storybook/config/manager.ts
rename to x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/manager.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/storybook/config/preview.ts b/x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/preview.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/storybook/config/preview.ts
rename to x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/preview.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/storybook/config/styles.css b/x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/styles.css
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/storybook/config/styles.css
rename to x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/styles.css
diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/tsconfig.json b/x-pack/packages/kbn-cloud-security-posture/graph/tsconfig.json
index 55cafeb069d0f..d97809a59772d 100644
--- a/x-pack/packages/kbn-cloud-security-posture/graph/tsconfig.json
+++ b/x-pack/packages/kbn-cloud-security-posture/graph/tsconfig.json
@@ -12,6 +12,7 @@
],
"kbn_references": [
"@kbn/cloud-security-posture-common",
- "@kbn/utility-types"
+ "@kbn/utility-types",
+ "@kbn/storybook"
]
}
diff --git a/x-pack/packages/kbn-cloud-security-posture/index.ts b/x-pack/packages/kbn-cloud-security-posture/public/index.ts
similarity index 87%
rename from x-pack/packages/kbn-cloud-security-posture/index.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/index.ts
index b7e45a546f3d5..c39a86f5ec64b 100644
--- a/x-pack/packages/kbn-cloud-security-posture/index.ts
+++ b/x-pack/packages/kbn-cloud-security-posture/public/index.ts
@@ -5,9 +5,9 @@
* 2.0.
*/
-export * from './type';
-export * from './constants/component_constants';
-export * from './constants/navigation';
+export * from './src/types';
+export * from './src/constants/component_constants';
+export * from './src/constants/navigation';
export type { NavFilter } from './src/hooks/use_navigate_findings';
export { showErrorToast } from './src/utils/show_error_toast';
export { encodeQuery, decodeQuery } from './src/utils/query_utils';
diff --git a/x-pack/packages/kbn-cloud-security-posture/jest.config.js b/x-pack/packages/kbn-cloud-security-posture/public/jest.config.js
similarity index 74%
rename from x-pack/packages/kbn-cloud-security-posture/jest.config.js
rename to x-pack/packages/kbn-cloud-security-posture/public/jest.config.js
index 7273e0452cf60..1c1a8c84561c1 100644
--- a/x-pack/packages/kbn-cloud-security-posture/jest.config.js
+++ b/x-pack/packages/kbn-cloud-security-posture/public/jest.config.js
@@ -7,6 +7,6 @@
module.exports = {
preset: '@kbn/test',
- rootDir: '../../..',
- roots: ['/x-pack/packages/kbn-cloud-security-posture'],
+ rootDir: '../../../..',
+ roots: ['/x-pack/packages/kbn-cloud-security-posture/public'],
};
diff --git a/x-pack/packages/kbn-cloud-security-posture/kibana.jsonc b/x-pack/packages/kbn-cloud-security-posture/public/kibana.jsonc
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/kibana.jsonc
rename to x-pack/packages/kbn-cloud-security-posture/public/kibana.jsonc
diff --git a/x-pack/packages/kbn-cloud-security-posture/package.json b/x-pack/packages/kbn-cloud-security-posture/public/package.json
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/package.json
rename to x-pack/packages/kbn-cloud-security-posture/public/package.json
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/components/csp_evaluation_badge.tsx b/x-pack/packages/kbn-cloud-security-posture/public/src/components/csp_evaluation_badge.tsx
similarity index 95%
rename from x-pack/packages/kbn-cloud-security-posture/src/components/csp_evaluation_badge.tsx
rename to x-pack/packages/kbn-cloud-security-posture/public/src/components/csp_evaluation_badge.tsx
index 8048414c8f8a5..f84ac26d68767 100644
--- a/x-pack/packages/kbn-cloud-security-posture/src/components/csp_evaluation_badge.tsx
+++ b/x-pack/packages/kbn-cloud-security-posture/public/src/components/csp_evaluation_badge.tsx
@@ -9,7 +9,7 @@ import React from 'react';
import { EuiBadge, type EuiBadgeProps } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { css } from '@emotion/react';
-import { statusColors } from '../../constants/component_constants';
+import { statusColors } from '../constants/component_constants';
interface Props {
type?: 'passed' | 'failed';
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/components/vulnerability_badges.tsx b/x-pack/packages/kbn-cloud-security-posture/public/src/components/vulnerability_badges.tsx
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/src/components/vulnerability_badges.tsx
rename to x-pack/packages/kbn-cloud-security-posture/public/src/components/vulnerability_badges.tsx
diff --git a/x-pack/packages/kbn-cloud-security-posture/constants/component_constants.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/constants/component_constants.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/constants/component_constants.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/constants/component_constants.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/constants/navigation.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/constants/navigation.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/constants/navigation.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/constants/navigation.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/hooks/use_csp_setup_status_api.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_csp_setup_status_api.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/src/hooks/use_csp_setup_status_api.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_csp_setup_status_api.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/hooks/use_data_view.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_data_view.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/src/hooks/use_data_view.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_data_view.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/hooks/use_get_benchmark_rules_state_api.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_get_benchmark_rules_state_api.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/src/hooks/use_get_benchmark_rules_state_api.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_get_benchmark_rules_state_api.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/hooks/use_misconfiguration_findings.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_misconfiguration_findings.ts
similarity index 99%
rename from x-pack/packages/kbn-cloud-security-posture/src/hooks/use_misconfiguration_findings.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_misconfiguration_findings.ts
index fe25224cf417a..40880b132537d 100644
--- a/x-pack/packages/kbn-cloud-security-posture/src/hooks/use_misconfiguration_findings.ts
+++ b/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_misconfiguration_findings.ts
@@ -15,7 +15,7 @@ import type {
LatestFindingsRequest,
LatestFindingsResponse,
UseCspOptions,
-} from '../../type';
+} from '../types';
import { useGetCspBenchmarkRulesStatesApi } from './use_get_benchmark_rules_state_api';
import {
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/hooks/use_misconfiguration_preview.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_misconfiguration_preview.ts
similarity index 98%
rename from x-pack/packages/kbn-cloud-security-posture/src/hooks/use_misconfiguration_preview.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_misconfiguration_preview.ts
index 75bd0d3952bd7..067cd22a9e1a9 100644
--- a/x-pack/packages/kbn-cloud-security-posture/src/hooks/use_misconfiguration_preview.ts
+++ b/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_misconfiguration_preview.ts
@@ -14,7 +14,7 @@ import type {
LatestFindingsRequest,
LatestFindingsResponse,
UseCspOptions,
-} from '../../type';
+} from '../types';
import { useGetCspBenchmarkRulesStatesApi } from './use_get_benchmark_rules_state_api';
import {
buildMisconfigurationsFindingsQuery,
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/hooks/use_navigate_findings.test.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_navigate_findings.test.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/src/hooks/use_navigate_findings.test.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_navigate_findings.test.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/hooks/use_navigate_findings.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_navigate_findings.ts
similarity index 97%
rename from x-pack/packages/kbn-cloud-security-posture/src/hooks/use_navigate_findings.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_navigate_findings.ts
index a8420b17dd4f9..454c9a0056a58 100644
--- a/x-pack/packages/kbn-cloud-security-posture/src/hooks/use_navigate_findings.ts
+++ b/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_navigate_findings.ts
@@ -14,7 +14,7 @@ import {
} from '@kbn/cloud-security-posture-common';
import type { CoreStart } from '@kbn/core/public';
import { useKibana } from '@kbn/kibana-react-plugin/public';
-import { findingsNavigation } from '../../constants/navigation';
+import { findingsNavigation } from '../constants/navigation';
import { useDataView } from './use_data_view';
import { CspClientPluginStartDeps } from '../..';
import { encodeQuery } from '../utils/query_utils';
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/hooks/use_vulnerabilities_findings.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_vulnerabilities_findings.ts
similarity index 99%
rename from x-pack/packages/kbn-cloud-security-posture/src/hooks/use_vulnerabilities_findings.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_vulnerabilities_findings.ts
index ba13ec983893f..062f5c7740c73 100644
--- a/x-pack/packages/kbn-cloud-security-posture/src/hooks/use_vulnerabilities_findings.ts
+++ b/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_vulnerabilities_findings.ts
@@ -16,7 +16,7 @@ import {
} from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import type { CspVulnerabilityFinding } from '@kbn/cloud-security-posture-common/schema/vulnerabilities/latest';
import type { CoreStart } from '@kbn/core/public';
-import type { CspClientPluginStartDeps, UseCspOptions } from '../../type';
+import type { CspClientPluginStartDeps, UseCspOptions } from '../types';
import { showErrorToast } from '../..';
import { getVulnerabilitiesAggregationCount, getVulnerabilitiesQuery } from '../utils/hooks_utils';
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/hooks/use_vulnerabilities_preview.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_vulnerabilities_preview.ts
similarity index 99%
rename from x-pack/packages/kbn-cloud-security-posture/src/hooks/use_vulnerabilities_preview.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_vulnerabilities_preview.ts
index 82b3f41d26819..ac34636720143 100644
--- a/x-pack/packages/kbn-cloud-security-posture/src/hooks/use_vulnerabilities_preview.ts
+++ b/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_vulnerabilities_preview.ts
@@ -16,7 +16,7 @@ import {
} from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import type { CspVulnerabilityFinding } from '@kbn/cloud-security-posture-common/schema/vulnerabilities/latest';
import type { CoreStart } from '@kbn/core/public';
-import type { CspClientPluginStartDeps, UseCspOptions } from '../../type';
+import type { CspClientPluginStartDeps, UseCspOptions } from '../types';
import { showErrorToast } from '../..';
import { getVulnerabilitiesAggregationCount, getVulnerabilitiesQuery } from '../utils/hooks_utils';
diff --git a/x-pack/packages/kbn-cloud-security-posture/type.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/types.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/type.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/types.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/utils/get_vulnerabilitiy_colors.test.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/utils/get_vulnerabilitiy_colors.test.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/src/utils/get_vulnerabilitiy_colors.test.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/utils/get_vulnerabilitiy_colors.test.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/utils/get_vulnerability_colors.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/utils/get_vulnerability_colors.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/src/utils/get_vulnerability_colors.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/utils/get_vulnerability_colors.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/utils/get_vulnerability_text.test.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/utils/get_vulnerability_text.test.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/src/utils/get_vulnerability_text.test.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/utils/get_vulnerability_text.test.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/utils/get_vulnerability_text.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/utils/get_vulnerability_text.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/src/utils/get_vulnerability_text.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/utils/get_vulnerability_text.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/utils/hooks_utils.test.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/utils/hooks_utils.test.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/src/utils/hooks_utils.test.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/utils/hooks_utils.test.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/utils/hooks_utils.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/utils/hooks_utils.ts
similarity index 99%
rename from x-pack/packages/kbn-cloud-security-posture/src/utils/hooks_utils.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/utils/hooks_utils.ts
index d06f4efbde026..a621e1d01add8 100644
--- a/x-pack/packages/kbn-cloud-security-posture/src/utils/hooks_utils.ts
+++ b/x-pack/packages/kbn-cloud-security-posture/public/src/utils/hooks_utils.ts
@@ -13,7 +13,7 @@ import {
} from '@kbn/cloud-security-posture-common';
import type { CspBenchmarkRulesStates } from '@kbn/cloud-security-posture-common/schema/rules/latest';
import { buildMutedRulesFilter } from '@kbn/cloud-security-posture-common';
-import type { UseCspOptions } from '../../type';
+import type { UseCspOptions } from '../types';
const MISCONFIGURATIONS_SOURCE_FIELDS = ['result.*', 'rule.*', 'resource.*'];
interface AggregationBucket {
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/utils/query_utils.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/utils/query_utils.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/src/utils/query_utils.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/utils/query_utils.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/utils/show_error_toast.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/utils/show_error_toast.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/src/utils/show_error_toast.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/utils/show_error_toast.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/utils/vulnerability_helpers.test.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/utils/vulnerability_helpers.test.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/src/utils/vulnerability_helpers.test.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/utils/vulnerability_helpers.test.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/src/utils/vulnerability_helpers.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/utils/vulnerability_helpers.ts
similarity index 100%
rename from x-pack/packages/kbn-cloud-security-posture/src/utils/vulnerability_helpers.ts
rename to x-pack/packages/kbn-cloud-security-posture/public/src/utils/vulnerability_helpers.ts
diff --git a/x-pack/packages/kbn-cloud-security-posture/tsconfig.json b/x-pack/packages/kbn-cloud-security-posture/public/tsconfig.json
similarity index 92%
rename from x-pack/packages/kbn-cloud-security-posture/tsconfig.json
rename to x-pack/packages/kbn-cloud-security-posture/public/tsconfig.json
index 38799e07182d9..e7f69a99c5199 100644
--- a/x-pack/packages/kbn-cloud-security-posture/tsconfig.json
+++ b/x-pack/packages/kbn-cloud-security-posture/public/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "../../../tsconfig.base.json",
+ "extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
},
@@ -35,6 +35,5 @@
"@kbn/ui-theme",
"@kbn/i18n-react",
"@kbn/rison",
- "@kbn/storybook",
]
}
diff --git a/x-pack/packages/kbn-langchain/server/language_models/chat_openai.ts b/x-pack/packages/kbn-langchain/server/language_models/chat_openai.ts
index c20de3be57e07..f679193c23f92 100644
--- a/x-pack/packages/kbn-langchain/server/language_models/chat_openai.ts
+++ b/x-pack/packages/kbn-langchain/server/language_models/chat_openai.ts
@@ -147,7 +147,13 @@ export class ActionsClientChatOpenAI extends ChatOpenAI {
const actionResult = await this.#actionsClient.execute(requestBody);
if (actionResult.status === 'error') {
- throw new Error(`${LLM_TYPE}: ${actionResult?.message} - ${actionResult?.serviceMessage}`);
+ const error = new Error(
+ `${LLM_TYPE}: ${actionResult?.message} - ${actionResult?.serviceMessage}`
+ );
+ if (actionResult?.serviceMessage) {
+ error.name = actionResult?.serviceMessage;
+ }
+ throw error;
}
if (!this.streaming) {
diff --git a/x-pack/packages/kbn-langchain/server/language_models/chat_vertex/chat_vertex.ts b/x-pack/packages/kbn-langchain/server/language_models/chat_vertex/chat_vertex.ts
index 5627abe717291..745c273c79583 100644
--- a/x-pack/packages/kbn-langchain/server/language_models/chat_vertex/chat_vertex.ts
+++ b/x-pack/packages/kbn-langchain/server/language_models/chat_vertex/chat_vertex.ts
@@ -98,11 +98,14 @@ export class ActionsClientChatVertexAI extends ChatVertexAI {
};
const actionResult = await this.#actionsClient.execute(requestBody);
-
if (actionResult.status === 'error') {
- throw new Error(
+ const error = new Error(
`ActionsClientChatVertexAI: action result status is error: ${actionResult?.message} - ${actionResult?.serviceMessage}`
);
+ if (actionResult?.serviceMessage) {
+ error.name = actionResult?.serviceMessage;
+ }
+ throw error;
}
const readable = get('data', actionResult) as Readable;
diff --git a/x-pack/packages/kbn-langchain/server/language_models/chat_vertex/connection.ts b/x-pack/packages/kbn-langchain/server/language_models/chat_vertex/connection.ts
index dd3c1e1abdda0..8ce776890acfa 100644
--- a/x-pack/packages/kbn-langchain/server/language_models/chat_vertex/connection.ts
+++ b/x-pack/packages/kbn-langchain/server/language_models/chat_vertex/connection.ts
@@ -93,9 +93,13 @@ export class ActionsClientChatConnection extends ChatConnection {
};
if (actionResult.status === 'error') {
- throw new Error(
+ const error = new Error(
`ActionsClientChatVertexAI: action result status is error: ${actionResult?.message} - ${actionResult?.serviceMessage}`
);
+ if (actionResult?.serviceMessage) {
+ error.name = actionResult?.serviceMessage;
+ }
+ throw error;
}
if (actionResult.data.candidates && actionResult.data.candidates.length > 0) {
diff --git a/x-pack/packages/kbn-langchain/server/language_models/gemini_chat.ts b/x-pack/packages/kbn-langchain/server/language_models/gemini_chat.ts
index 197360c2f06e6..700e26d5a0a14 100644
--- a/x-pack/packages/kbn-langchain/server/language_models/gemini_chat.ts
+++ b/x-pack/packages/kbn-langchain/server/language_models/gemini_chat.ts
@@ -87,9 +87,13 @@ export class ActionsClientGeminiChatModel extends ChatGoogleGenerativeAI {
};
if (actionResult.status === 'error') {
- throw new Error(
+ const error = new Error(
`ActionsClientGeminiChatModel: action result status is error: ${actionResult?.message} - ${actionResult?.serviceMessage}`
);
+ if (actionResult?.serviceMessage) {
+ error.name = actionResult?.serviceMessage;
+ }
+ throw error;
}
if (actionResult.data.candidates && actionResult.data.candidates.length > 0) {
@@ -162,9 +166,13 @@ export class ActionsClientGeminiChatModel extends ChatGoogleGenerativeAI {
const actionResult = await this.#actionsClient.execute(requestBody);
if (actionResult.status === 'error') {
- throw new Error(
+ const error = new Error(
`ActionsClientGeminiChatModel: action result status is error: ${actionResult?.message} - ${actionResult?.serviceMessage}`
);
+ if (actionResult?.serviceMessage) {
+ error.name = actionResult?.serviceMessage;
+ }
+ throw error;
}
const readable = get('data', actionResult) as Readable;
diff --git a/x-pack/packages/kbn-langchain/server/language_models/llm.ts b/x-pack/packages/kbn-langchain/server/language_models/llm.ts
index 8ebf62e8c31f0..2a634ccb490cf 100644
--- a/x-pack/packages/kbn-langchain/server/language_models/llm.ts
+++ b/x-pack/packages/kbn-langchain/server/language_models/llm.ts
@@ -108,9 +108,13 @@ export class ActionsClientLlm extends LLM {
const actionResult = await this.#actionsClient.execute(requestBody);
if (actionResult.status === 'error') {
- throw new Error(
+ const error = new Error(
`${LLM_TYPE}: action result status is error: ${actionResult?.message} - ${actionResult?.serviceMessage}`
);
+ if (actionResult?.serviceMessage) {
+ error.name = actionResult?.serviceMessage;
+ }
+ throw error;
}
const content = get('data.message', actionResult);
diff --git a/x-pack/packages/kbn-langchain/server/language_models/simple_chat_model.ts b/x-pack/packages/kbn-langchain/server/language_models/simple_chat_model.ts
index 5133b1ae6543a..a66d088345b22 100644
--- a/x-pack/packages/kbn-langchain/server/language_models/simple_chat_model.ts
+++ b/x-pack/packages/kbn-langchain/server/language_models/simple_chat_model.ts
@@ -127,9 +127,13 @@ export class ActionsClientSimpleChatModel extends SimpleChatModel {
const actionResult = await this.#actionsClient.execute(requestBody);
if (actionResult.status === 'error') {
- throw new Error(
+ const error = new Error(
`ActionsClientSimpleChatModel: action result status is error: ${actionResult?.message} - ${actionResult?.serviceMessage}`
);
+ if (actionResult?.serviceMessage) {
+ error.name = actionResult?.serviceMessage;
+ }
+ throw error;
}
if (!this.streaming) {
@@ -217,9 +221,13 @@ export class ActionsClientSimpleChatModel extends SimpleChatModel {
const actionResult = await this.#actionsClient.execute(requestBody);
if (actionResult.status === 'error') {
- throw new Error(
+ const error = new Error(
`ActionsClientSimpleChatModel: action result status is error: ${actionResult?.message} - ${actionResult?.serviceMessage}`
);
+ if (actionResult?.serviceMessage) {
+ error.name = actionResult?.serviceMessage;
+ }
+ throw error;
}
const readable = get('data', actionResult) as Readable;
diff --git a/x-pack/packages/ml/field_stats_flyout/eui_combo_box_with_field_stats.tsx b/x-pack/packages/ml/field_stats_flyout/eui_combo_box_with_field_stats.tsx
deleted file mode 100644
index a09710da8e398..0000000000000
--- a/x-pack/packages/ml/field_stats_flyout/eui_combo_box_with_field_stats.tsx
+++ /dev/null
@@ -1,67 +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 type { FC } from 'react';
-import React, { useMemo } from 'react';
-import type { EuiComboBoxProps } from '@elastic/eui/src/components/combo_box/combo_box';
-import type { EuiComboBoxOptionOption } from '@elastic/eui';
-import { EuiComboBox } from '@elastic/eui';
-import { css } from '@emotion/react';
-import { useFieldStatsTrigger } from './use_field_stats_trigger';
-
-export const optionCss = css`
- .euiComboBoxOption__enterBadge {
- display: none;
- }
- .euiFlexGroup {
- gap: 0px;
- }
- .euiComboBoxOption__content {
- margin-left: 2px;
- }
-`;
-
-/**
- * Props for the EuiComboBoxWithFieldStats component.
- */
-export type EuiComboBoxWithFieldStatsProps = EuiComboBoxProps<
- string | number | string[] | undefined
->;
-
-/**
- * React component that wraps the EuiComboBox component and adds field statistics functionality.
- *
- * @component
- * @example
- * ```tsx
- *
- * ```
- * @param {EuiComboBoxWithFieldStatsProps} props - The component props.
- */
-export const EuiComboBoxWithFieldStats: FC = (props) => {
- const { options, ...restProps } = props;
- const { renderOption } = useFieldStatsTrigger();
- const comboBoxOptions: EuiComboBoxOptionOption[] = useMemo(
- () =>
- Array.isArray(options)
- ? options.map((o) => ({
- ...o,
- css: optionCss,
- }))
- : [],
- [options]
- );
-
- return (
-
- );
-};
diff --git a/x-pack/packages/ml/field_stats_flyout/field_stats_flyout_provider.tsx b/x-pack/packages/ml/field_stats_flyout/field_stats_flyout_provider.tsx
index 9dd947f0872f3..678dec7d36f42 100644
--- a/x-pack/packages/ml/field_stats_flyout/field_stats_flyout_provider.tsx
+++ b/x-pack/packages/ml/field_stats_flyout/field_stats_flyout_provider.tsx
@@ -142,6 +142,7 @@ export const FieldStatsFlyoutProvider: FC = (prop
// Get all field names for each returned doc and flatten it
// to a list of unique field names used across all docs.
const fieldsWithData = new Set(docs.map(Object.keys).flat(1));
+
manager.set(cacheKey, fieldsWithData);
if (!unmounted) {
setPopulatedFields(fieldsWithData);
diff --git a/x-pack/packages/ml/field_stats_flyout/field_stats_info_button.tsx b/x-pack/packages/ml/field_stats_flyout/field_stats_info_button.tsx
index 936f9550cdda1..7863f358708d6 100644
--- a/x-pack/packages/ml/field_stats_flyout/field_stats_info_button.tsx
+++ b/x-pack/packages/ml/field_stats_flyout/field_stats_info_button.tsx
@@ -88,6 +88,7 @@ export const FieldStatsInfoButton: FC = (props) => {
defaultMessage: '(no data found in 1000 sample records)',
})
: '';
+
return (
@@ -135,14 +136,15 @@ export const FieldStatsInfoButton: FC = (props) => {
grow={false}
css={{
paddingRight: themeVars.euiTheme.euiSizeXS,
- paddingBottom: themeVars.euiTheme.euiSizeXS,
}}
>
-
+ {!hideTrigger ? (
+
+ ) : null}
= (props) => {
aria-label={label}
title={label}
className="euiComboBoxOption__content"
- css={{ paddingBottom: themeVars.euiTheme.euiSizeXS }}
>
{label}
diff --git a/x-pack/packages/ml/field_stats_flyout/index.ts b/x-pack/packages/ml/field_stats_flyout/index.ts
index db4d3c5ee7b15..53ed8c7ce877b 100644
--- a/x-pack/packages/ml/field_stats_flyout/index.ts
+++ b/x-pack/packages/ml/field_stats_flyout/index.ts
@@ -21,7 +21,6 @@ export {
type FieldStatsInfoButtonProps,
} from './field_stats_info_button';
export { useFieldStatsTrigger } from './use_field_stats_trigger';
-export {
- EuiComboBoxWithFieldStats,
- type EuiComboBoxWithFieldStatsProps,
-} from './eui_combo_box_with_field_stats';
+
+export { OptionListWithFieldStats } from './options_list_with_stats/option_list_with_stats';
+export type { DropDownLabel } from './options_list_with_stats/types';
diff --git a/x-pack/packages/ml/field_stats_flyout/options_list_with_stats/option_list_popover.tsx b/x-pack/packages/ml/field_stats_flyout/options_list_with_stats/option_list_popover.tsx
new file mode 100644
index 0000000000000..77b5f8a0d8b15
--- /dev/null
+++ b/x-pack/packages/ml/field_stats_flyout/options_list_with_stats/option_list_popover.tsx
@@ -0,0 +1,158 @@
+/*
+ * 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 type { FC } from 'react';
+import React, { useMemo, useState, useEffect } from 'react';
+import { isDefined } from '@kbn/ml-is-defined';
+import type {
+ EuiComboBoxOptionOption,
+ EuiComboBoxSingleSelectionShape,
+ EuiSelectableOption,
+} from '@elastic/eui';
+import { EuiFlexItem, EuiSelectable, htmlIdGenerator } from '@elastic/eui';
+import { css } from '@emotion/react';
+import { type DropDownLabel } from './types';
+import { useFieldStatsFlyoutContext } from '../use_field_stats_flyout_context';
+import { OptionsListPopoverFooter } from './option_list_popover_footer';
+
+interface OptionsListPopoverProps {
+ options: DropDownLabel[];
+ renderOption: (option: DropDownLabel) => React.ReactNode;
+ singleSelection?: boolean | EuiComboBoxSingleSelectionShape;
+ onChange?:
+ | ((newSuggestions: DropDownLabel[]) => void)
+ | ((
+ newSuggestions: Array>
+ ) => void);
+ setPopoverOpen: (open: boolean) => void;
+ isLoading?: boolean;
+}
+
+interface OptionsListPopoverSuggestionsProps {
+ options: DropDownLabel[];
+ renderOption: (option: DropDownLabel) => React.ReactNode;
+ singleSelection?: boolean | EuiComboBoxSingleSelectionShape;
+ onChange?:
+ | ((newSuggestions: DropDownLabel[]) => void)
+ | ((
+ newSuggestions: Array>
+ ) => void);
+ setPopoverOpen: (open: boolean) => void;
+}
+const OptionsListPopoverSuggestions: FC = ({
+ options,
+ renderOption,
+ singleSelection,
+ onChange,
+ setPopoverOpen,
+}) => {
+ const [selectableOptions, setSelectableOptions] = useState([]); // will be set in following useEffect
+ useEffect(() => {
+ /* This useEffect makes selectableOptions responsive to search, show only selected, and clear selections */
+ const _selectableOptions = (options ?? []).map((suggestion) => {
+ const key = suggestion.label ?? suggestion.field?.id;
+ return {
+ ...suggestion,
+ key,
+ checked: undefined,
+ 'data-test-subj': `optionsListControlSelection-${key}`,
+ };
+ });
+ setSelectableOptions(_selectableOptions);
+ }, [options]);
+
+ return (
+ >}
+ renderOption={renderOption}
+ listProps={{ onFocusBadge: false }}
+ onChange={(opts, _, changedOption) => {
+ const option = changedOption as DropDownLabel;
+ if (singleSelection) {
+ if (onChange) {
+ onChange([option as EuiComboBoxOptionOption]);
+ setPopoverOpen(false);
+ }
+ } else {
+ if (onChange) {
+ onChange([option as EuiComboBoxOptionOption]);
+ setPopoverOpen(false);
+ }
+ }
+ }}
+ >
+ {(list, search) => (
+ <>
+ {search}
+ {list}
+ >
+ )}
+
+ );
+};
+
+export const OptionsListPopover = ({
+ options,
+ renderOption,
+ singleSelection,
+ onChange,
+ setPopoverOpen,
+ isLoading,
+}: OptionsListPopoverProps) => {
+ const { populatedFields } = useFieldStatsFlyoutContext();
+
+ const [showEmptyFields, setShowEmptyFields] = useState(false);
+ const id = useMemo(() => htmlIdGenerator()(), []);
+
+ const filteredOptions = useMemo(() => {
+ return showEmptyFields
+ ? options
+ : options.filter((option) => {
+ if (isDefined(option['data-is-empty'])) {
+ return !option['data-is-empty'];
+ }
+ if (
+ Object.hasOwn(option, 'isGroupLabel') ||
+ Object.hasOwn(option, 'isGroupLabelOption')
+ ) {
+ const key = option.key ?? option.searchableLabel;
+ return key ? populatedFields?.has(key) : false;
+ }
+ if (option.field) {
+ return populatedFields?.has(option.field.id);
+ }
+ return true;
+ });
+ }, [options, showEmptyFields, populatedFields]);
+ return (
+
+
+
+
+
+
+ );
+};
diff --git a/x-pack/packages/ml/field_stats_flyout/options_list_with_stats/option_list_popover_footer.tsx b/x-pack/packages/ml/field_stats_flyout/options_list_with_stats/option_list_popover_footer.tsx
new file mode 100644
index 0000000000000..0bed94223b0c5
--- /dev/null
+++ b/x-pack/packages/ml/field_stats_flyout/options_list_with_stats/option_list_popover_footer.tsx
@@ -0,0 +1,51 @@
+/*
+ * 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 type { FC } from 'react';
+import { EuiPopoverFooter, EuiSwitch, EuiProgress, useEuiBackgroundColor } from '@elastic/eui';
+import { css } from '@emotion/react';
+import { i18n } from '@kbn/i18n';
+import { euiThemeVars } from '@kbn/ui-theme';
+
+export const OptionsListPopoverFooter: FC<{
+ showEmptyFields: boolean;
+ setShowEmptyFields: (showEmptyFields: boolean) => void;
+ isLoading?: boolean;
+}> = ({ showEmptyFields, setShowEmptyFields, isLoading }) => {
+ return (
+
+ {isLoading ? (
+ // @ts-expect-error css should be ok
+
+
+
+ ) : null}
+
+ setShowEmptyFields(e.target.checked)}
+ />
+
+ );
+};
diff --git a/x-pack/packages/ml/field_stats_flyout/options_list_with_stats/option_list_with_stats.tsx b/x-pack/packages/ml/field_stats_flyout/options_list_with_stats/option_list_with_stats.tsx
new file mode 100644
index 0000000000000..244b2d6a511a9
--- /dev/null
+++ b/x-pack/packages/ml/field_stats_flyout/options_list_with_stats/option_list_with_stats.tsx
@@ -0,0 +1,146 @@
+/*
+ * 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 type { FC } from 'react';
+import React, { useMemo, useState } from 'react';
+import type { EuiComboBoxOptionOption, EuiComboBoxSingleSelectionShape } from '@elastic/eui';
+import { EuiInputPopover, htmlIdGenerator, EuiFormControlLayout, EuiFieldText } from '@elastic/eui';
+import { css } from '@emotion/react';
+import { i18n } from '@kbn/i18n';
+import { useFieldStatsTrigger } from '../use_field_stats_trigger';
+import { OptionsListPopover } from './option_list_popover';
+import type { DropDownLabel } from './types';
+
+const MIN_POPOVER_WIDTH = 400;
+
+export const optionCss = css`
+ display: flex;
+ align-items: center;
+ .euiComboBoxOption__enterBadge {
+ display: none;
+ }
+ .euiFlexGroup {
+ gap: 0px;
+ }
+ .euiComboBoxOption__content {
+ margin-left: 2px;
+ }
+`;
+
+interface OptionListWithFieldStatsProps {
+ options: DropDownLabel[];
+ placeholder?: string;
+ 'aria-label'?: string;
+ singleSelection?: boolean | EuiComboBoxSingleSelectionShape;
+ onChange:
+ | ((newSuggestions: DropDownLabel[]) => void)
+ | ((newSuggestions: EuiComboBoxOptionOption[]) => void);
+ selectedOptions?: Array<{ label: string }>;
+ fullWidth?: boolean;
+ isDisabled?: boolean;
+ isLoading?: boolean;
+ isClearable?: boolean;
+ isInvalid?: boolean;
+ 'data-test-subj'?: string;
+}
+
+export const OptionListWithFieldStats: FC = ({
+ options,
+ placeholder,
+ singleSelection = false,
+ onChange,
+ selectedOptions,
+ fullWidth,
+ isDisabled,
+ isLoading,
+ isClearable = true,
+ 'aria-label': ariaLabel,
+ 'data-test-subj': dataTestSubj,
+}) => {
+ const { renderOption } = useFieldStatsTrigger();
+ const [isPopoverOpen, setPopoverOpen] = useState(false);
+
+ const popoverId = useMemo(() => htmlIdGenerator()(), []);
+ const comboBoxOptions: DropDownLabel[] = useMemo(
+ () =>
+ Array.isArray(options)
+ ? options.map(({ isEmpty, hideTrigger: hideInspectButton, ...o }) => ({
+ ...o,
+ css: optionCss,
+ // Change data-is-empty- because EUI is passing all props to dom element
+ // so isEmpty is invalid, but we need this info to render option correctly
+ 'data-is-empty': isEmpty,
+ 'data-hide-inspect': hideInspectButton,
+ }))
+ : [],
+ [options]
+ );
+ const hasSelections = useMemo(() => selectedOptions?.length ?? 0 > 0, [selectedOptions]);
+
+ const value = singleSelection && selectedOptions?.[0]?.label ? selectedOptions?.[0]?.label : '';
+ return (
+
+ {}}
+ value={value}
+ />
+
+ }
+ hasArrow={false}
+ repositionOnScroll
+ isOpen={isPopoverOpen}
+ panelPaddingSize="none"
+ panelMinWidth={MIN_POPOVER_WIDTH}
+ initialFocus={'[data-test-subj=optionsList-control-search-input]'}
+ closePopover={setPopoverOpen.bind(null, false)}
+ panelProps={{
+ 'aria-label': i18n.translate('xpack.ml.controls.optionsList.popover.ariaLabel', {
+ defaultMessage: 'Popover for {ariaLabel}',
+ values: { ariaLabel },
+ }),
+ }}
+ >
+ {isPopoverOpen ? (
+
+ ) : null}
+
+ );
+};
diff --git a/x-pack/packages/ml/field_stats_flyout/options_list_with_stats/types.ts b/x-pack/packages/ml/field_stats_flyout/options_list_with_stats/types.ts
new file mode 100644
index 0000000000000..ef95daa38ea03
--- /dev/null
+++ b/x-pack/packages/ml/field_stats_flyout/options_list_with_stats/types.ts
@@ -0,0 +1,31 @@
+/*
+ * 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 type { EuiComboBoxOptionOption, EuiSelectableOption } from '@elastic/eui';
+import type { Aggregation, Field } from '@kbn/ml-anomaly-utils';
+
+interface BaseOption {
+ key?: string;
+ label: string | React.ReactNode;
+ isEmpty?: boolean;
+ hideTrigger?: boolean;
+ 'data-is-empty'?: boolean;
+ 'data-hide-inspect'?: boolean;
+ isGroupLabelOption?: boolean;
+ isGroupLabel?: boolean;
+ field?: Field;
+ agg?: Aggregation;
+ searchableLabel?: string;
+}
+export type SelectableOption = EuiSelectableOption>;
+export type DropDownLabel =
+ | (EuiComboBoxOptionOption & BaseOption)
+ | SelectableOption;
+
+export function isSelectableOption(option: unknown): option is SelectableOption {
+ return typeof option === 'object' && option !== null && Object.hasOwn(option, 'label');
+}
diff --git a/x-pack/packages/ml/field_stats_flyout/tsconfig.json b/x-pack/packages/ml/field_stats_flyout/tsconfig.json
index b0920fac0ad2a..0010d79432e34 100644
--- a/x-pack/packages/ml/field_stats_flyout/tsconfig.json
+++ b/x-pack/packages/ml/field_stats_flyout/tsconfig.json
@@ -32,5 +32,6 @@
"@kbn/ml-query-utils",
"@kbn/ml-is-defined",
"@kbn/field-types",
+ "@kbn/ui-theme",
]
}
diff --git a/x-pack/packages/ml/field_stats_flyout/use_field_stats_trigger.tsx b/x-pack/packages/ml/field_stats_flyout/use_field_stats_trigger.tsx
index 78c2f6772049a..546dc36ce9e4b 100644
--- a/x-pack/packages/ml/field_stats_flyout/use_field_stats_trigger.tsx
+++ b/x-pack/packages/ml/field_stats_flyout/use_field_stats_trigger.tsx
@@ -7,13 +7,27 @@
import type { ReactNode } from 'react';
import React, { useCallback } from 'react';
-import type { EuiComboBoxOptionOption } from '@elastic/eui';
+import { type EuiComboBoxOptionOption } from '@elastic/eui';
import type { Field } from '@kbn/ml-anomaly-utils';
-import { optionCss } from './eui_combo_box_with_field_stats';
+import { css } from '@emotion/react';
+import { EVENT_RATE_FIELD_ID } from '@kbn/ml-anomaly-utils/fields';
+import type { DropDownLabel } from '.';
import { useFieldStatsFlyoutContext } from '.';
import type { FieldForStats } from './field_stats_info_button';
import { FieldStatsInfoButton } from './field_stats_info_button';
+import { isSelectableOption } from './options_list_with_stats/types';
+export const optionCss = css`
+ .euiComboBoxOption__enterBadge {
+ display: none;
+ }
+ .euiFlexGroup {
+ gap: 0px;
+ }
+ .euiComboBoxOption__content {
+ margin-left: 2px;
+ }
+`;
interface Option extends EuiComboBoxOptionOption {
field: Field;
}
@@ -30,7 +44,7 @@ interface Option extends EuiComboBoxOptionOption {
* - `optionCss`: CSS styles for the options in the combo box.
* - `populatedFields`: A set of populated fields.
*/
-export const useFieldStatsTrigger = () => {
+export function useFieldStatsTrigger() {
const { setIsFlyoutVisible, setFieldName, populatedFields } = useFieldStatsFlyoutContext();
const closeFlyout = useCallback(() => setIsFlyoutVisible(false), [setIsFlyoutVisible]);
@@ -46,18 +60,26 @@ export const useFieldStatsTrigger = () => {
);
const renderOption = useCallback(
- (option: EuiComboBoxOptionOption, searchValue: string): ReactNode => {
- const field = (option as Option).field;
- return option.isGroupLabelOption || !field ? (
- option.label
- ) : (
-
- );
+ (option: T): ReactNode => {
+ if (isSelectableOption(option)) {
+ const field = (option as Option).field;
+ const isInternalEventRateFieldId = field?.id === EVENT_RATE_FIELD_ID;
+ const isEmpty = isInternalEventRateFieldId
+ ? false
+ : !populatedFields?.has(field?.id ?? field?.name);
+ const shouldHideInpectButton = option.hideTrigger ?? option['data-hide-inspect'];
+ return option.isGroupLabel || !field ? (
+ option.label
+ ) : (
+
+ );
+ }
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[handleFieldStatsButtonClick, populatedFields?.size]
@@ -71,4 +93,4 @@ export const useFieldStatsTrigger = () => {
optionCss,
populatedFields,
};
-};
+}
diff --git a/x-pack/packages/security/authorization_core/index.ts b/x-pack/packages/security/authorization_core/index.ts
index ccb68eb3bbcec..dc85fee1f0657 100644
--- a/x-pack/packages/security/authorization_core/index.ts
+++ b/x-pack/packages/security/authorization_core/index.ts
@@ -6,10 +6,5 @@
*/
export { Actions } from './src/actions';
-export { privilegesFactory } from './src/privileges';
-export type {
- CasesSupportedOperations,
- PrivilegesService,
- RawKibanaPrivileges,
- RawKibanaFeaturePrivileges,
-} from './src/privileges';
+export { privilegesFactory, getReplacedByForPrivilege } from './src/privileges';
+export type { CasesSupportedOperations, PrivilegesService } from './src/privileges';
diff --git a/x-pack/packages/security/authorization_core/src/actions/alerting.test.ts b/x-pack/packages/security/authorization_core/src/actions/alerting.test.ts
index 8f3d48a91005c..1db1030da510a 100644
--- a/x-pack/packages/security/authorization_core/src/actions/alerting.test.ts
+++ b/x-pack/packages/security/authorization_core/src/actions/alerting.test.ts
@@ -51,3 +51,21 @@ describe('#get', () => {
);
});
});
+
+test('#isValid', () => {
+ const alertingActions = new AlertingActions();
+ expect(alertingActions.isValid('alerting:foo-ruleType/consumer/alertingType/bar-operation')).toBe(
+ true
+ );
+
+ expect(
+ alertingActions.isValid('api:alerting:foo-ruleType/consumer/alertingType/bar-operation')
+ ).toBe(false);
+ expect(alertingActions.isValid('api:foo-ruleType/consumer/alertingType/bar-operation')).toBe(
+ false
+ );
+
+ expect(alertingActions.isValid('alerting_foo-ruleType/consumer/alertingType/bar-operation')).toBe(
+ false
+ );
+});
diff --git a/x-pack/packages/security/authorization_core/src/actions/alerting.ts b/x-pack/packages/security/authorization_core/src/actions/alerting.ts
index c1de9a1c65d21..18abac73ef8b7 100644
--- a/x-pack/packages/security/authorization_core/src/actions/alerting.ts
+++ b/x-pack/packages/security/authorization_core/src/actions/alerting.ts
@@ -40,4 +40,12 @@ export class AlertingActions implements AlertingActionsType {
return `${this.prefix}${ruleTypeId}/${consumer}/${alertingEntity}/${operation}`;
}
+
+ /**
+ * Checks if the action is a valid alerting action.
+ * @param action The action string to check.
+ */
+ public isValid(action: string) {
+ return action.startsWith(this.prefix);
+ }
}
diff --git a/x-pack/packages/security/authorization_core/src/actions/ui.test.ts b/x-pack/packages/security/authorization_core/src/actions/ui.test.ts
index 0d6419d8fd2b8..7f1c412eaa5a5 100644
--- a/x-pack/packages/security/authorization_core/src/actions/ui.test.ts
+++ b/x-pack/packages/security/authorization_core/src/actions/ui.test.ts
@@ -32,3 +32,15 @@ describe('#get', () => {
expect(uiActions.get('foo', 'fooCapability', 'subFoo')).toBe('ui:foo/fooCapability/subFoo');
});
});
+
+test('#isValid', () => {
+ const uiActions = new UIActions();
+ expect(uiActions.isValid('ui:alpha')).toBe(true);
+ expect(uiActions.isValid('ui:beta')).toBe(true);
+
+ expect(uiActions.isValid('api:alpha')).toBe(false);
+ expect(uiActions.isValid('api:beta')).toBe(false);
+
+ expect(uiActions.isValid('ui_alpha')).toBe(false);
+ expect(uiActions.isValid('ui_beta')).toBe(false);
+});
diff --git a/x-pack/packages/security/authorization_core/src/actions/ui.ts b/x-pack/packages/security/authorization_core/src/actions/ui.ts
index 2c9986e1c8ce5..688bf5bbe15ee 100644
--- a/x-pack/packages/security/authorization_core/src/actions/ui.ts
+++ b/x-pack/packages/security/authorization_core/src/actions/ui.ts
@@ -40,4 +40,12 @@ export class UIActions implements UIActionsType {
return `${this.prefix}${featureId}/${uiCapabilityParts.join('/')}`;
}
+
+ /**
+ * Checks if the action is a valid UI action.
+ * @param action The action string to check.
+ */
+ public isValid(action: string) {
+ return action.startsWith(this.prefix);
+ }
}
diff --git a/x-pack/packages/security/authorization_core/src/privileges/index.ts b/x-pack/packages/security/authorization_core/src/privileges/index.ts
index 7113b1b348bec..70e134751794d 100644
--- a/x-pack/packages/security/authorization_core/src/privileges/index.ts
+++ b/x-pack/packages/security/authorization_core/src/privileges/index.ts
@@ -7,5 +7,4 @@
export type { PrivilegesService } from './privileges';
export type { CasesSupportedOperations } from './feature_privilege_builder';
-export { privilegesFactory } from './privileges';
-export type { RawKibanaPrivileges, RawKibanaFeaturePrivileges } from './raw_kibana_privileges';
+export { privilegesFactory, getReplacedByForPrivilege } from './privileges';
diff --git a/x-pack/packages/security/authorization_core/src/privileges/privileges.test.ts b/x-pack/packages/security/authorization_core/src/privileges/privileges.test.ts
index 118d63503db22..f9d490bfcb09b 100644
--- a/x-pack/packages/security/authorization_core/src/privileges/privileges.test.ts
+++ b/x-pack/packages/security/authorization_core/src/privileges/privileges.test.ts
@@ -8,7 +8,7 @@
import { KibanaFeature } from '@kbn/features-plugin/server';
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
-import { privilegesFactory } from './privileges';
+import { getReplacedByForPrivilege, privilegesFactory } from './privileges';
import { licenseMock } from '../__fixtures__/licensing.mock';
import { Actions } from '../actions';
@@ -472,6 +472,184 @@ describe('features', () => {
});
});
+ test('actions should respect `replacedBy` specified by the deprecated privileges', () => {
+ const features: KibanaFeature[] = [
+ new KibanaFeature({
+ deprecated: { notice: 'It is deprecated, sorry.' },
+ id: 'alpha',
+ name: 'Feature Alpha',
+ app: [],
+ category: { id: 'alpha', label: 'alpha' },
+ alerting: ['rule-type-1'],
+ privileges: {
+ all: {
+ savedObject: {
+ all: ['all-alpha-all-so'],
+ read: ['all-alpha-read-so'],
+ },
+ ui: ['all-alpha-ui'],
+ app: ['all-alpha-app'],
+ api: ['all-alpha-api'],
+ alerting: { rule: { all: ['rule-type-1'] } },
+ replacedBy: [{ feature: 'beta', privileges: ['all'] }],
+ },
+ read: {
+ savedObject: {
+ all: ['read-alpha-all-so'],
+ read: ['read-alpha-read-so'],
+ },
+ ui: ['read-alpha-ui'],
+ app: ['read-alpha-app'],
+ api: ['read-alpha-api'],
+ replacedBy: {
+ default: [{ feature: 'beta', privileges: ['read'] }],
+ minimal: [{ feature: 'beta', privileges: ['minimal_read'] }],
+ },
+ },
+ },
+ }),
+ new KibanaFeature({
+ id: 'beta',
+ name: 'Feature Beta',
+ app: [],
+ category: { id: 'beta', label: 'beta' },
+ alerting: ['rule-type-1'],
+ privileges: {
+ all: {
+ savedObject: {
+ all: ['all-beta-all-so'],
+ read: ['all-beta-read-so'],
+ },
+ ui: ['all-beta-ui'],
+ app: ['all-beta-app'],
+ api: ['all-beta-api'],
+ alerting: { rule: { all: ['rule-type-1'] } },
+ },
+ read: {
+ savedObject: {
+ all: ['read-beta-all-so'],
+ read: ['read-beta-read-so'],
+ },
+ ui: ['read-beta-ui'],
+ app: ['read-beta-app'],
+ api: ['read-beta-api'],
+ },
+ },
+ }),
+ ];
+
+ const mockFeaturesPlugin = featuresPluginMock.createSetup();
+ mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
+ const privileges = privilegesFactory(actions, mockFeaturesPlugin, mockLicenseServiceBasic);
+
+ const alertingOperations = [
+ ...[
+ 'get',
+ 'getRuleState',
+ 'getAlertSummary',
+ 'getExecutionLog',
+ 'getActionErrorLog',
+ 'find',
+ 'getRuleExecutionKPI',
+ 'getBackfill',
+ 'findBackfill',
+ ],
+ ...[
+ 'create',
+ 'delete',
+ 'update',
+ 'updateApiKey',
+ 'enable',
+ 'disable',
+ 'muteAll',
+ 'unmuteAll',
+ 'muteAlert',
+ 'unmuteAlert',
+ 'snooze',
+ 'bulkEdit',
+ 'bulkDelete',
+ 'bulkEnable',
+ 'bulkDisable',
+ 'unsnooze',
+ 'runSoon',
+ 'scheduleBackfill',
+ 'deleteBackfill',
+ ],
+ ];
+
+ const expectedAllPrivileges = [
+ actions.login,
+ actions.api.get('all-alpha-api'),
+ actions.app.get('all-alpha-app'),
+ actions.ui.get('navLinks', 'all-alpha-app'),
+ actions.savedObject.get('all-alpha-all-so', 'bulk_get'),
+ actions.savedObject.get('all-alpha-all-so', 'get'),
+ actions.savedObject.get('all-alpha-all-so', 'find'),
+ actions.savedObject.get('all-alpha-all-so', 'open_point_in_time'),
+ actions.savedObject.get('all-alpha-all-so', 'close_point_in_time'),
+ actions.savedObject.get('all-alpha-all-so', 'create'),
+ actions.savedObject.get('all-alpha-all-so', 'bulk_create'),
+ actions.savedObject.get('all-alpha-all-so', 'update'),
+ actions.savedObject.get('all-alpha-all-so', 'bulk_update'),
+ actions.savedObject.get('all-alpha-all-so', 'delete'),
+ actions.savedObject.get('all-alpha-all-so', 'bulk_delete'),
+ actions.savedObject.get('all-alpha-all-so', 'share_to_space'),
+ actions.savedObject.get('all-alpha-read-so', 'bulk_get'),
+ actions.savedObject.get('all-alpha-read-so', 'get'),
+ actions.savedObject.get('all-alpha-read-so', 'find'),
+ actions.savedObject.get('all-alpha-read-so', 'open_point_in_time'),
+ actions.savedObject.get('all-alpha-read-so', 'close_point_in_time'),
+ actions.ui.get('alpha', 'all-alpha-ui'),
+ ...alertingOperations.map((operation) =>
+ actions.alerting.get('rule-type-1', 'alpha', 'rule', operation)
+ ),
+ // To maintain compatibility with the new UI capabilities and new alerting entities that are
+ // feature specific: all.replacedBy: [{ feature: 'beta', privileges: ['all'] }]
+ actions.ui.get('navLinks', 'all-beta-app'),
+ actions.ui.get('beta', 'all-beta-ui'),
+ ...alertingOperations.map((operation) =>
+ actions.alerting.get('rule-type-1', 'beta', 'rule', operation)
+ ),
+ ];
+
+ const expectedReadPrivileges = [
+ actions.login,
+ actions.api.get('read-alpha-api'),
+ actions.app.get('read-alpha-app'),
+ actions.ui.get('navLinks', 'read-alpha-app'),
+ actions.savedObject.get('read-alpha-all-so', 'bulk_get'),
+ actions.savedObject.get('read-alpha-all-so', 'get'),
+ actions.savedObject.get('read-alpha-all-so', 'find'),
+ actions.savedObject.get('read-alpha-all-so', 'open_point_in_time'),
+ actions.savedObject.get('read-alpha-all-so', 'close_point_in_time'),
+ actions.savedObject.get('read-alpha-all-so', 'create'),
+ actions.savedObject.get('read-alpha-all-so', 'bulk_create'),
+ actions.savedObject.get('read-alpha-all-so', 'update'),
+ actions.savedObject.get('read-alpha-all-so', 'bulk_update'),
+ actions.savedObject.get('read-alpha-all-so', 'delete'),
+ actions.savedObject.get('read-alpha-all-so', 'bulk_delete'),
+ actions.savedObject.get('read-alpha-all-so', 'share_to_space'),
+ actions.savedObject.get('read-alpha-read-so', 'bulk_get'),
+ actions.savedObject.get('read-alpha-read-so', 'get'),
+ actions.savedObject.get('read-alpha-read-so', 'find'),
+ actions.savedObject.get('read-alpha-read-so', 'open_point_in_time'),
+ actions.savedObject.get('read-alpha-read-so', 'close_point_in_time'),
+ actions.ui.get('alpha', 'read-alpha-ui'),
+ // To maintain compatibility with the new UI capabilities that are feature specific
+ // read.replacedBy: [{ feature: 'beta', privileges: ['read'] }]
+ actions.ui.get('navLinks', 'read-beta-app'),
+ actions.ui.get('beta', 'read-beta-ui'),
+ ];
+
+ const actual = privileges.get();
+ expect(actual).toHaveProperty('features.alpha', {
+ all: [...expectedAllPrivileges],
+ read: [...expectedReadPrivileges],
+ minimal_all: [...expectedAllPrivileges],
+ minimal_read: [...expectedReadPrivileges],
+ });
+ });
+
test(`features with no privileges aren't listed`, () => {
const features: KibanaFeature[] = [
new KibanaFeature({
@@ -3510,4 +3688,360 @@ describe('subFeatures', () => {
]);
});
});
+
+ test('actions should respect `replacedBy` specified by the deprecated sub-feature privileges', () => {
+ const features: KibanaFeature[] = [
+ new KibanaFeature({
+ deprecated: { notice: 'It is deprecated, sorry.' },
+ id: 'alpha',
+ name: 'Feature Alpha',
+ app: [],
+ category: { id: 'alpha', label: 'alpha' },
+ privileges: {
+ all: {
+ savedObject: {
+ all: ['all-alpha-all-so'],
+ read: ['all-alpha-read-so'],
+ },
+ ui: ['all-alpha-ui'],
+ app: ['all-alpha-app'],
+ api: ['all-alpha-api'],
+ replacedBy: [{ feature: 'beta', privileges: ['all'] }],
+ },
+ read: {
+ savedObject: {
+ all: ['read-alpha-all-so'],
+ read: ['read-alpha-read-so'],
+ },
+ ui: ['read-alpha-ui'],
+ app: ['read-alpha-app'],
+ api: ['read-alpha-api'],
+ replacedBy: {
+ default: [{ feature: 'beta', privileges: ['read', 'sub_beta'] }],
+ minimal: [{ feature: 'beta', privileges: ['minimal_read'] }],
+ },
+ },
+ },
+ subFeatures: [
+ {
+ name: 'sub-feature-alpha',
+ privilegeGroups: [
+ {
+ groupType: 'independent',
+ privileges: [
+ {
+ id: 'sub_alpha',
+ name: 'Sub Feature Alpha',
+ includeIn: 'all',
+ savedObject: {
+ all: ['sub-alpha-all-so'],
+ read: ['sub-alpha-read-so'],
+ },
+ ui: ['sub-alpha-ui'],
+ app: ['sub-alpha-app'],
+ api: ['sub-alpha-api'],
+ replacedBy: [
+ { feature: 'beta', privileges: ['minimal_read'] },
+ { feature: 'beta', privileges: ['sub_beta'] },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ }),
+ new KibanaFeature({
+ id: 'beta',
+ name: 'Feature Beta',
+ app: [],
+ category: { id: 'beta', label: 'beta' },
+ privileges: {
+ all: {
+ savedObject: {
+ all: ['all-beta-all-so'],
+ read: ['all-beta-read-so'],
+ },
+ ui: ['all-beta-ui'],
+ app: ['all-beta-app'],
+ api: ['all-beta-api'],
+ },
+ read: {
+ savedObject: {
+ all: ['read-beta-all-so'],
+ read: ['read-beta-read-so'],
+ },
+ ui: ['read-beta-ui'],
+ app: ['read-beta-app'],
+ api: ['read-beta-api'],
+ },
+ },
+ subFeatures: [
+ {
+ name: 'sub-feature-beta',
+ privilegeGroups: [
+ {
+ groupType: 'independent',
+ privileges: [
+ {
+ id: 'sub_beta',
+ name: 'Sub Feature Beta',
+ includeIn: 'all',
+ savedObject: {
+ all: ['sub-beta-all-so'],
+ read: ['sub-beta-read-so'],
+ },
+ ui: ['sub-beta-ui'],
+ app: ['sub-beta-app'],
+ api: ['sub-beta-api'],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ }),
+ ];
+
+ const mockFeaturesPlugin = featuresPluginMock.createSetup();
+ mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
+ const privileges = privilegesFactory(actions, mockFeaturesPlugin, mockLicenseServiceGold);
+
+ const expectedAllPrivileges = [
+ actions.login,
+ actions.api.get('all-alpha-api'),
+ actions.api.get('sub-alpha-api'),
+ actions.app.get('all-alpha-app'),
+ actions.app.get('sub-alpha-app'),
+ actions.ui.get('navLinks', 'all-alpha-app'),
+ actions.ui.get('navLinks', 'sub-alpha-app'),
+ actions.savedObject.get('all-alpha-all-so', 'bulk_get'),
+ actions.savedObject.get('all-alpha-all-so', 'get'),
+ actions.savedObject.get('all-alpha-all-so', 'find'),
+ actions.savedObject.get('all-alpha-all-so', 'open_point_in_time'),
+ actions.savedObject.get('all-alpha-all-so', 'close_point_in_time'),
+ actions.savedObject.get('all-alpha-all-so', 'create'),
+ actions.savedObject.get('all-alpha-all-so', 'bulk_create'),
+ actions.savedObject.get('all-alpha-all-so', 'update'),
+ actions.savedObject.get('all-alpha-all-so', 'bulk_update'),
+ actions.savedObject.get('all-alpha-all-so', 'delete'),
+ actions.savedObject.get('all-alpha-all-so', 'bulk_delete'),
+ actions.savedObject.get('all-alpha-all-so', 'share_to_space'),
+ actions.savedObject.get('sub-alpha-all-so', 'bulk_get'),
+ actions.savedObject.get('sub-alpha-all-so', 'get'),
+ actions.savedObject.get('sub-alpha-all-so', 'find'),
+ actions.savedObject.get('sub-alpha-all-so', 'open_point_in_time'),
+ actions.savedObject.get('sub-alpha-all-so', 'close_point_in_time'),
+ actions.savedObject.get('sub-alpha-all-so', 'create'),
+ actions.savedObject.get('sub-alpha-all-so', 'bulk_create'),
+ actions.savedObject.get('sub-alpha-all-so', 'update'),
+ actions.savedObject.get('sub-alpha-all-so', 'bulk_update'),
+ actions.savedObject.get('sub-alpha-all-so', 'delete'),
+ actions.savedObject.get('sub-alpha-all-so', 'bulk_delete'),
+ actions.savedObject.get('sub-alpha-all-so', 'share_to_space'),
+ actions.savedObject.get('all-alpha-read-so', 'bulk_get'),
+ actions.savedObject.get('all-alpha-read-so', 'get'),
+ actions.savedObject.get('all-alpha-read-so', 'find'),
+ actions.savedObject.get('all-alpha-read-so', 'open_point_in_time'),
+ actions.savedObject.get('all-alpha-read-so', 'close_point_in_time'),
+ actions.savedObject.get('sub-alpha-read-so', 'bulk_get'),
+ actions.savedObject.get('sub-alpha-read-so', 'get'),
+ actions.savedObject.get('sub-alpha-read-so', 'find'),
+ actions.savedObject.get('sub-alpha-read-so', 'open_point_in_time'),
+ actions.savedObject.get('sub-alpha-read-so', 'close_point_in_time'),
+ actions.ui.get('alpha', 'all-alpha-ui'),
+ actions.ui.get('alpha', 'sub-alpha-ui'),
+ // To maintain compatibility with the new UI capabilities that are feature specific:
+ // all.replacedBy: [{ feature: 'beta', privileges: ['all'] }],
+ actions.ui.get('navLinks', 'all-beta-app'),
+ actions.ui.get('navLinks', 'sub-beta-app'),
+ actions.ui.get('beta', 'all-beta-ui'),
+ actions.ui.get('beta', 'sub-beta-ui'),
+ ];
+
+ const expectedMinimalAllPrivileges = [
+ actions.login,
+ actions.api.get('all-alpha-api'),
+ actions.app.get('all-alpha-app'),
+ actions.ui.get('navLinks', 'all-alpha-app'),
+ actions.savedObject.get('all-alpha-all-so', 'bulk_get'),
+ actions.savedObject.get('all-alpha-all-so', 'get'),
+ actions.savedObject.get('all-alpha-all-so', 'find'),
+ actions.savedObject.get('all-alpha-all-so', 'open_point_in_time'),
+ actions.savedObject.get('all-alpha-all-so', 'close_point_in_time'),
+ actions.savedObject.get('all-alpha-all-so', 'create'),
+ actions.savedObject.get('all-alpha-all-so', 'bulk_create'),
+ actions.savedObject.get('all-alpha-all-so', 'update'),
+ actions.savedObject.get('all-alpha-all-so', 'bulk_update'),
+ actions.savedObject.get('all-alpha-all-so', 'delete'),
+ actions.savedObject.get('all-alpha-all-so', 'bulk_delete'),
+ actions.savedObject.get('all-alpha-all-so', 'share_to_space'),
+ actions.savedObject.get('all-alpha-read-so', 'bulk_get'),
+ actions.savedObject.get('all-alpha-read-so', 'get'),
+ actions.savedObject.get('all-alpha-read-so', 'find'),
+ actions.savedObject.get('all-alpha-read-so', 'open_point_in_time'),
+ actions.savedObject.get('all-alpha-read-so', 'close_point_in_time'),
+ actions.ui.get('alpha', 'all-alpha-ui'),
+ // To maintain compatibility with the new UI capabilities that are feature specific.
+ // Actions from the beta feature top-level and sub-feature privileges are included because
+ // used simple `replacedBy` format:
+ // all.replacedBy: [{ feature: 'beta', privileges: ['all'] }],
+ actions.ui.get('navLinks', 'all-beta-app'),
+ actions.ui.get('navLinks', 'sub-beta-app'),
+ actions.ui.get('beta', 'all-beta-ui'),
+ actions.ui.get('beta', 'sub-beta-ui'),
+ ];
+
+ const expectedReadPrivileges = [
+ actions.login,
+ actions.api.get('read-alpha-api'),
+ actions.app.get('read-alpha-app'),
+ actions.ui.get('navLinks', 'read-alpha-app'),
+ actions.savedObject.get('read-alpha-all-so', 'bulk_get'),
+ actions.savedObject.get('read-alpha-all-so', 'get'),
+ actions.savedObject.get('read-alpha-all-so', 'find'),
+ actions.savedObject.get('read-alpha-all-so', 'open_point_in_time'),
+ actions.savedObject.get('read-alpha-all-so', 'close_point_in_time'),
+ actions.savedObject.get('read-alpha-all-so', 'create'),
+ actions.savedObject.get('read-alpha-all-so', 'bulk_create'),
+ actions.savedObject.get('read-alpha-all-so', 'update'),
+ actions.savedObject.get('read-alpha-all-so', 'bulk_update'),
+ actions.savedObject.get('read-alpha-all-so', 'delete'),
+ actions.savedObject.get('read-alpha-all-so', 'bulk_delete'),
+ actions.savedObject.get('read-alpha-all-so', 'share_to_space'),
+ actions.savedObject.get('read-alpha-read-so', 'bulk_get'),
+ actions.savedObject.get('read-alpha-read-so', 'get'),
+ actions.savedObject.get('read-alpha-read-so', 'find'),
+ actions.savedObject.get('read-alpha-read-so', 'open_point_in_time'),
+ actions.savedObject.get('read-alpha-read-so', 'close_point_in_time'),
+ actions.ui.get('alpha', 'read-alpha-ui'),
+ // To maintain compatibility with the new UI capabilities that are feature specific:
+ // read.replacedBy: {
+ // default: [{ feature: 'beta', privileges: ['read', 'sub_beta'] }]
+ // },
+ actions.ui.get('navLinks', 'read-beta-app'),
+ actions.ui.get('beta', 'read-beta-ui'),
+ actions.ui.get('navLinks', 'sub-beta-app'),
+ actions.ui.get('beta', 'sub-beta-ui'),
+ ];
+
+ const expectedMinimalReadPrivileges = [
+ actions.login,
+ actions.api.get('read-alpha-api'),
+ actions.app.get('read-alpha-app'),
+ actions.ui.get('navLinks', 'read-alpha-app'),
+ actions.savedObject.get('read-alpha-all-so', 'bulk_get'),
+ actions.savedObject.get('read-alpha-all-so', 'get'),
+ actions.savedObject.get('read-alpha-all-so', 'find'),
+ actions.savedObject.get('read-alpha-all-so', 'open_point_in_time'),
+ actions.savedObject.get('read-alpha-all-so', 'close_point_in_time'),
+ actions.savedObject.get('read-alpha-all-so', 'create'),
+ actions.savedObject.get('read-alpha-all-so', 'bulk_create'),
+ actions.savedObject.get('read-alpha-all-so', 'update'),
+ actions.savedObject.get('read-alpha-all-so', 'bulk_update'),
+ actions.savedObject.get('read-alpha-all-so', 'delete'),
+ actions.savedObject.get('read-alpha-all-so', 'bulk_delete'),
+ actions.savedObject.get('read-alpha-all-so', 'share_to_space'),
+ actions.savedObject.get('read-alpha-read-so', 'bulk_get'),
+ actions.savedObject.get('read-alpha-read-so', 'get'),
+ actions.savedObject.get('read-alpha-read-so', 'find'),
+ actions.savedObject.get('read-alpha-read-so', 'open_point_in_time'),
+ actions.savedObject.get('read-alpha-read-so', 'close_point_in_time'),
+ actions.ui.get('alpha', 'read-alpha-ui'),
+ // To maintain compatibility with the new UI capabilities that are feature specific:
+ // read.replacedBy: {
+ // minimal: [{ feature: 'beta', privileges: ['minimal_read'] }],
+ // },
+ actions.ui.get('navLinks', 'read-beta-app'),
+ actions.ui.get('beta', 'read-beta-ui'),
+ ];
+
+ const expectedSubFeaturePrivileges = [
+ actions.login,
+ actions.api.get('sub-alpha-api'),
+ actions.app.get('sub-alpha-app'),
+ actions.ui.get('navLinks', 'sub-alpha-app'),
+ actions.savedObject.get('sub-alpha-all-so', 'bulk_get'),
+ actions.savedObject.get('sub-alpha-all-so', 'get'),
+ actions.savedObject.get('sub-alpha-all-so', 'find'),
+ actions.savedObject.get('sub-alpha-all-so', 'open_point_in_time'),
+ actions.savedObject.get('sub-alpha-all-so', 'close_point_in_time'),
+ actions.savedObject.get('sub-alpha-all-so', 'create'),
+ actions.savedObject.get('sub-alpha-all-so', 'bulk_create'),
+ actions.savedObject.get('sub-alpha-all-so', 'update'),
+ actions.savedObject.get('sub-alpha-all-so', 'bulk_update'),
+ actions.savedObject.get('sub-alpha-all-so', 'delete'),
+ actions.savedObject.get('sub-alpha-all-so', 'bulk_delete'),
+ actions.savedObject.get('sub-alpha-all-so', 'share_to_space'),
+ actions.savedObject.get('sub-alpha-read-so', 'bulk_get'),
+ actions.savedObject.get('sub-alpha-read-so', 'get'),
+ actions.savedObject.get('sub-alpha-read-so', 'find'),
+ actions.savedObject.get('sub-alpha-read-so', 'open_point_in_time'),
+ actions.savedObject.get('sub-alpha-read-so', 'close_point_in_time'),
+ actions.ui.get('alpha', 'sub-alpha-ui'),
+ // To maintain compatibility with the new UI capabilities that are feature specific:
+ // sub_alpha.replacedBy: [
+ // { feature: 'beta', privileges: ['minimal_read'] },
+ // { feature: 'beta', privileges: ['sub_beta'] },
+ // ],
+ actions.ui.get('navLinks', 'read-beta-app'),
+ actions.ui.get('beta', 'read-beta-ui'),
+ actions.ui.get('navLinks', 'sub-beta-app'),
+ actions.ui.get('beta', 'sub-beta-ui'),
+ ];
+
+ const actual = privileges.get();
+ expect(actual).toHaveProperty('features.alpha', {
+ all: expectedAllPrivileges,
+ read: expectedReadPrivileges,
+ minimal_all: expectedMinimalAllPrivileges,
+ minimal_read: expectedMinimalReadPrivileges,
+ sub_alpha: expectedSubFeaturePrivileges,
+ });
+ });
+});
+
+describe('#getReplacedByForPrivilege', () => {
+ test('correctly gets `replacedBy` with simple format', () => {
+ const basePrivilege = { savedObject: { all: [], read: [] }, ui: [] };
+ expect(getReplacedByForPrivilege('all', basePrivilege)).toBeUndefined();
+ expect(getReplacedByForPrivilege('minimal_all', basePrivilege)).toBeUndefined();
+
+ const privilegeWithReplacedBy = {
+ ...basePrivilege,
+ replacedBy: [{ feature: 'alpha', privileges: ['all', 'read'] }],
+ };
+ expect(getReplacedByForPrivilege('all', privilegeWithReplacedBy)).toEqual([
+ { feature: 'alpha', privileges: ['all', 'read'] },
+ ]);
+ expect(getReplacedByForPrivilege('minimal_all', privilegeWithReplacedBy)).toEqual([
+ { feature: 'alpha', privileges: ['all', 'read'] },
+ ]);
+ expect(getReplacedByForPrivilege('custom', privilegeWithReplacedBy)).toEqual([
+ { feature: 'alpha', privileges: ['all', 'read'] },
+ ]);
+ });
+
+ test('correctly gets `replacedBy` with extended format', () => {
+ const basePrivilege = { savedObject: { all: [], read: [] }, ui: [] };
+ expect(getReplacedByForPrivilege('all', basePrivilege)).toBeUndefined();
+ expect(getReplacedByForPrivilege('minimal_all', basePrivilege)).toBeUndefined();
+
+ const privilegeWithReplacedBy = {
+ ...basePrivilege,
+ replacedBy: {
+ default: [{ feature: 'alpha', privileges: ['all', 'read', 'custom'] }],
+ minimal: [{ feature: 'alpha', privileges: ['minimal_all'] }],
+ },
+ };
+ expect(getReplacedByForPrivilege('all', privilegeWithReplacedBy)).toEqual([
+ { feature: 'alpha', privileges: ['all', 'read', 'custom'] },
+ ]);
+ expect(getReplacedByForPrivilege('custom', privilegeWithReplacedBy)).toEqual([
+ { feature: 'alpha', privileges: ['all', 'read', 'custom'] },
+ ]);
+ expect(getReplacedByForPrivilege('minimal_all', privilegeWithReplacedBy)).toEqual([
+ { feature: 'alpha', privileges: ['minimal_all'] },
+ ]);
+ });
});
diff --git a/x-pack/packages/security/authorization_core/src/privileges/privileges.ts b/x-pack/packages/security/authorization_core/src/privileges/privileges.ts
index 6b8acc4e4013a..7f388e80defd2 100644
--- a/x-pack/packages/security/authorization_core/src/privileges/privileges.ts
+++ b/x-pack/packages/security/authorization_core/src/privileges/privileges.ts
@@ -12,10 +12,13 @@ import type {
FeatureKibanaPrivilegesReference,
} from '@kbn/features-plugin/common';
import type { FeaturesPluginSetup, KibanaFeature } from '@kbn/features-plugin/server';
-import type { SecurityLicense } from '@kbn/security-plugin-types-common';
+import {
+ getMinimalPrivilegeId,
+ isMinimalPrivilegeId,
+} from '@kbn/security-authorization-core-common';
+import type { RawKibanaPrivileges, SecurityLicense } from '@kbn/security-plugin-types-common';
import { featurePrivilegeBuilderFactory } from './feature_privilege_builder';
-import type { RawKibanaPrivileges } from './raw_kibana_privileges';
import type { Actions } from '../actions';
export interface PrivilegesService {
@@ -63,26 +66,46 @@ export function privilegesFactory(
// Remember privilege as composable to update it later, once actions for all referenced privileges are also
// calculated and registered.
- const composableFeaturePrivileges: Array<{
+ const composablePrivileges: Array<{
featureId: string;
privilegeId: string;
+ references: readonly FeatureKibanaPrivilegesReference[];
excludeFromBasePrivileges?: boolean;
- composedOf: readonly FeatureKibanaPrivilegesReference[];
+ actionsFilter?: (action: string) => boolean;
}> = [];
- const tryStoreComposableFeature = (
+ const tryStoreComposablePrivilege = (
feature: KibanaFeature,
privilegeId: string,
privilege: FeatureKibanaPrivileges
) => {
+ // If privilege is configured with `composedOf` it should be complemented with **all**
+ // actions from referenced privileges.
if (privilege.composedOf) {
- composableFeaturePrivileges.push({
+ composablePrivileges.push({
featureId: feature.id,
privilegeId,
- composedOf: privilege.composedOf,
+ references: privilege.composedOf,
excludeFromBasePrivileges:
feature.excludeFromBasePrivileges || privilege.excludeFromBasePrivileges,
});
}
+
+ // If a privilege is configured with `replacedBy`, it's part of the deprecated feature and
+ // should be complemented with the subset of actions from the referenced privileges to
+ // maintain backward compatibility. Namely, deprecated privileges should grant the same UI
+ // capabilities and alerting actions as the privileges that replace them, so that the
+ // client-side code can safely use only non-deprecated UI capabilities and users can still
+ // access previously created alerting rules and alerts.
+ const replacedBy = getReplacedByForPrivilege(privilegeId, privilege);
+ if (replacedBy) {
+ composablePrivileges.push({
+ featureId: feature.id,
+ privilegeId,
+ references: replacedBy,
+ actionsFilter: (action) =>
+ actions.ui.isValid(action) || actions.alerting.isValid(action),
+ });
+ }
};
const hiddenFeatures = new Set();
@@ -99,20 +122,20 @@ export function privilegesFactory(
...uniq(featurePrivilegeBuilder.getActions(featurePrivilege.privilege, feature)),
];
- tryStoreComposableFeature(feature, fullPrivilegeId, featurePrivilege.privilege);
+ tryStoreComposablePrivilege(feature, fullPrivilegeId, featurePrivilege.privilege);
}
for (const featurePrivilege of featuresService.featurePrivilegeIterator(feature, {
augmentWithSubFeaturePrivileges: false,
licenseHasAtLeast,
})) {
- const minimalPrivilegeId = `minimal_${featurePrivilege.privilegeId}`;
+ const minimalPrivilegeId = getMinimalPrivilegeId(featurePrivilege.privilegeId);
featurePrivileges[feature.id][minimalPrivilegeId] = [
actions.login,
...uniq(featurePrivilegeBuilder.getActions(featurePrivilege.privilege, feature)),
];
- tryStoreComposableFeature(feature, minimalPrivilegeId, featurePrivilege.privilege);
+ tryStoreComposablePrivilege(feature, minimalPrivilegeId, featurePrivilege.privilege);
}
if (
@@ -127,6 +150,8 @@ export function privilegesFactory(
actions.login,
...uniq(featurePrivilegeBuilder.getActions(subFeaturePrivilege, feature)),
];
+
+ tryStoreComposablePrivilege(feature, subFeaturePrivilege.id, subFeaturePrivilege);
}
}
@@ -141,11 +166,14 @@ export function privilegesFactory(
// another feature. This could potentially enable functionality in a license lower than originally intended. It
// might or might not be desired, but we're accepting this for now, as every attempt to compose a feature
// undergoes a stringent review process.
- for (const composableFeature of composableFeaturePrivileges) {
- const composedActions = composableFeature.composedOf.flatMap((privilegeReference) =>
- privilegeReference.privileges.flatMap(
- (privilege) => featurePrivileges[privilegeReference.feature][privilege]
- )
+ for (const composableFeature of composablePrivileges) {
+ const composedActions = composableFeature.references.flatMap((privilegeReference) =>
+ privilegeReference.privileges.flatMap((privilege) => {
+ const privilegeActions = featurePrivileges[privilegeReference.feature][privilege] ?? [];
+ return composableFeature.actionsFilter
+ ? privilegeActions.filter(composableFeature.actionsFilter)
+ : privilegeActions;
+ })
);
featurePrivileges[composableFeature.featureId][composableFeature.privilegeId] = [
...new Set(
@@ -220,3 +248,27 @@ export function privilegesFactory(
},
};
}
+
+/**
+ * Returns a list of privileges that replace the given privilege, if any. Works for both top-level
+ * and sub-feature privileges.
+ * @param privilegeId The ID of the privilege to get replacements for.
+ * @param privilege The privilege definition to get replacements for.
+ */
+export function getReplacedByForPrivilege(
+ privilegeId: string,
+ privilege: FeatureKibanaPrivileges
+): readonly FeatureKibanaPrivilegesReference[] | undefined {
+ const replacedBy = privilege.replacedBy;
+ if (!replacedBy) {
+ return;
+ }
+
+ // If a privilege of the deprecated feature explicitly defines a replacement for minimal privileges, use it.
+ // Otherwise, use the default replacement for all cases.
+ return 'minimal' in replacedBy
+ ? isMinimalPrivilegeId(privilegeId)
+ ? replacedBy.minimal
+ : replacedBy.default
+ : replacedBy;
+}
diff --git a/x-pack/packages/security/authorization_core/tsconfig.json b/x-pack/packages/security/authorization_core/tsconfig.json
index 03870180c12c5..08437d11e23b9 100644
--- a/x-pack/packages/security/authorization_core/tsconfig.json
+++ b/x-pack/packages/security/authorization_core/tsconfig.json
@@ -9,6 +9,7 @@
"kbn_references": [
"@kbn/core",
"@kbn/features-plugin",
+ "@kbn/security-authorization-core-common",
"@kbn/security-plugin-types-common",
"@kbn/security-plugin-types-server",
"@kbn/licensing-plugin",
diff --git a/x-pack/packages/security/authorization_core_common/README.md b/x-pack/packages/security/authorization_core_common/README.md
new file mode 100644
index 0000000000000..1b4259d658730
--- /dev/null
+++ b/x-pack/packages/security/authorization_core_common/README.md
@@ -0,0 +1,3 @@
+# @kbn/security-authorization-core-common
+
+Contains core authorization logic (shared between server and browser)
diff --git a/x-pack/plugins/data_usage/common/types.ts b/x-pack/packages/security/authorization_core_common/index.ts
similarity index 74%
rename from x-pack/plugins/data_usage/common/types.ts
rename to x-pack/packages/security/authorization_core_common/index.ts
index d80bae2458d09..63fa40559fae2 100644
--- a/x-pack/plugins/data_usage/common/types.ts
+++ b/x-pack/packages/security/authorization_core_common/index.ts
@@ -5,5 +5,4 @@
* 2.0.
*/
-// temporary type until agreed on
-export type MetricKey = 'ingestedMax' | 'retainedMax';
+export { isMinimalPrivilegeId, getMinimalPrivilegeId } from './src/privileges';
diff --git a/x-pack/packages/security/authorization_core_common/jest.config.js b/x-pack/packages/security/authorization_core_common/jest.config.js
new file mode 100644
index 0000000000000..1034836296ba1
--- /dev/null
+++ b/x-pack/packages/security/authorization_core_common/jest.config.js
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+
+module.exports = {
+ coverageDirectory: '/x-pack/packages/security/authorization_core_common',
+ coverageReporters: ['text', 'html'],
+ collectCoverageFrom: [
+ '/x-pack/packages/security/authorization_core_common/**/*.{ts,tsx}',
+ ],
+ preset: '@kbn/test',
+ rootDir: '../../../..',
+ roots: ['/x-pack/packages/security/authorization_core_common'],
+};
diff --git a/x-pack/packages/security/authorization_core_common/kibana.jsonc b/x-pack/packages/security/authorization_core_common/kibana.jsonc
new file mode 100644
index 0000000000000..1ddb58d875826
--- /dev/null
+++ b/x-pack/packages/security/authorization_core_common/kibana.jsonc
@@ -0,0 +1,5 @@
+{
+ "type": "shared-common",
+ "id": "@kbn/security-authorization-core-common",
+ "owner": "@elastic/kibana-security"
+}
diff --git a/x-pack/packages/security/authorization_core_common/package.json b/x-pack/packages/security/authorization_core_common/package.json
new file mode 100644
index 0000000000000..74811f44978d7
--- /dev/null
+++ b/x-pack/packages/security/authorization_core_common/package.json
@@ -0,0 +1,6 @@
+{
+ "name": "@kbn/security-authorization-core-common",
+ "private": true,
+ "version": "1.0.0",
+ "license": "Elastic License 2.0"
+}
diff --git a/x-pack/packages/security/authorization_core_common/src/privileges/index.ts b/x-pack/packages/security/authorization_core_common/src/privileges/index.ts
new file mode 100644
index 0000000000000..01e05bfabde5c
--- /dev/null
+++ b/x-pack/packages/security/authorization_core_common/src/privileges/index.ts
@@ -0,0 +1,8 @@
+/*
+ * 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.
+ */
+
+export { isMinimalPrivilegeId, getMinimalPrivilegeId } from './minimal_privileges';
diff --git a/x-pack/packages/security/authorization_core_common/src/privileges/minimal_privileges.test.ts b/x-pack/packages/security/authorization_core_common/src/privileges/minimal_privileges.test.ts
new file mode 100644
index 0000000000000..cbec8e7f96796
--- /dev/null
+++ b/x-pack/packages/security/authorization_core_common/src/privileges/minimal_privileges.test.ts
@@ -0,0 +1,40 @@
+/*
+ * 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 { getMinimalPrivilegeId, isMinimalPrivilegeId } from '../..';
+
+describe('Minimal privileges', () => {
+ it('#isMinimalPrivilegeId correctly detects minimal privileges', () => {
+ expect(isMinimalPrivilegeId('minimal_all')).toBe(true);
+ expect(isMinimalPrivilegeId('minimal_read')).toBe(true);
+
+ for (const privilege of ['all', 'read', 'none', 'custom', 'minimal_custom', 'minimal_none']) {
+ expect(isMinimalPrivilegeId(privilege)).toBe(false);
+ }
+ });
+
+ it('#getMinimalPrivilegeId correctly constructs minimal privilege ID', () => {
+ expect(getMinimalPrivilegeId('all')).toBe('minimal_all');
+ expect(getMinimalPrivilegeId('minimal_all')).toBe('minimal_all');
+
+ expect(getMinimalPrivilegeId('read')).toBe('minimal_read');
+ expect(getMinimalPrivilegeId('minimal_read')).toBe('minimal_read');
+
+ expect(() => getMinimalPrivilegeId('none')).toThrowErrorMatchingInlineSnapshot(
+ `"Minimal privileges are only available for \\"read\\" and \\"all\\" privileges, but \\"none\\" was provided."`
+ );
+ expect(() => getMinimalPrivilegeId('custom')).toThrowErrorMatchingInlineSnapshot(
+ `"Minimal privileges are only available for \\"read\\" and \\"all\\" privileges, but \\"custom\\" was provided."`
+ );
+ expect(() => getMinimalPrivilegeId('minimal_none')).toThrowErrorMatchingInlineSnapshot(
+ `"Minimal privileges are only available for \\"read\\" and \\"all\\" privileges, but \\"minimal_none\\" was provided."`
+ );
+ expect(() => getMinimalPrivilegeId('minimal_custom')).toThrowErrorMatchingInlineSnapshot(
+ `"Minimal privileges are only available for \\"read\\" and \\"all\\" privileges, but \\"minimal_custom\\" was provided."`
+ );
+ });
+});
diff --git a/x-pack/packages/security/authorization_core_common/src/privileges/minimal_privileges.ts b/x-pack/packages/security/authorization_core_common/src/privileges/minimal_privileges.ts
new file mode 100644
index 0000000000000..e3484bc8a3890
--- /dev/null
+++ b/x-pack/packages/security/authorization_core_common/src/privileges/minimal_privileges.ts
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+/**
+ * Minimal privileges only exist for top-level privileges, as "minimal" means a privilege without
+ * any associated sub-feature privileges. Currently, sub-feature privileges cannot include or be
+ * associated with other sub-feature privileges. We use "minimal" privileges under the hood when
+ * admins customize sub-feature privileges for a given top-level privilege. We have only
+ * `minimal_all` and `minimal_read` minimal privileges.
+ *
+ * For example, let’s assume we have a feature Alpha with `All` and `Read` top-level privileges, and
+ * `Sub-alpha-1` and `Sub-alpha-2` sub-feature privileges, which are **by default included** in the
+ * `All` top-level privilege. When an admin toggles the `All` privilege for feature Alpha and
+ * doesn’t change anything else, the resulting role will only have the `feature-alpha.all`
+ * privilege, which assumes/includes both `sub-alpha-1` and `sub-alpha-2`. However, if the admin
+ * decides to customize sub-feature privileges and toggles off `Sub-alpha-2`, the resulting role
+ * will include `feature-alpha.minimal_all` and `feature-alpha.sub-alpha-1` thus excluding
+ * `feature-alpha.sub-alpha-2` that's included in `feature-alpha.all`, but not in
+ * `feature-alpha.minimal_all`.
+ */
+
+/**
+ * Returns true if the given privilege ID is a minimal feature privilege.
+ * @param privilegeId The privilege ID to check.
+ */
+export function isMinimalPrivilegeId(privilegeId: string) {
+ return privilegeId === 'minimal_all' || privilegeId === 'minimal_read';
+}
+
+/**
+ * Returns the minimal privilege ID for the given privilege ID.
+ * @param privilegeId The privilege ID to get the minimal privilege ID for. Only `all` and `read`
+ * privileges have "minimal" equivalents.
+ */
+export function getMinimalPrivilegeId(privilegeId: string) {
+ if (isMinimalPrivilegeId(privilegeId)) {
+ return privilegeId;
+ }
+
+ if (privilegeId !== 'read' && privilegeId !== 'all') {
+ throw new Error(
+ `Minimal privileges are only available for "read" and "all" privileges, but "${privilegeId}" was provided.`
+ );
+ }
+
+ return `minimal_${privilegeId}`;
+}
diff --git a/x-pack/packages/security/authorization_core_common/tsconfig.json b/x-pack/packages/security/authorization_core_common/tsconfig.json
new file mode 100644
index 0000000000000..e8a4b1a87df85
--- /dev/null
+++ b/x-pack/packages/security/authorization_core_common/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "../../../../tsconfig.base.json",
+ "compilerOptions": {
+ "outDir": "target/types",
+ "types": ["jest", "node", "react"]
+ },
+ "include": ["**/*.ts", "**/*.tsx"],
+ "exclude": ["target/**/*"],
+ "kbn_references": []
+}
diff --git a/x-pack/packages/security/plugin_types_common/index.ts b/x-pack/packages/security/plugin_types_common/index.ts
index 8dd0ff726103a..840e32a77b9c1 100644
--- a/x-pack/packages/security/plugin_types_common/index.ts
+++ b/x-pack/packages/security/plugin_types_common/index.ts
@@ -18,6 +18,8 @@ export type {
RoleRemoteIndexPrivilege,
RoleRemoteClusterPrivilege,
FeaturesPrivileges,
+ RawKibanaFeaturePrivileges,
+ RawKibanaPrivileges,
} from './src/authorization';
export type { SecurityLicense, SecurityLicenseFeatures, LoginLayout } from './src/licensing';
export type {
diff --git a/x-pack/packages/security/plugin_types_common/src/authorization/index.ts b/x-pack/packages/security/plugin_types_common/src/authorization/index.ts
index 89857a18865af..2a4462960b376 100644
--- a/x-pack/packages/security/plugin_types_common/src/authorization/index.ts
+++ b/x-pack/packages/security/plugin_types_common/src/authorization/index.ts
@@ -6,6 +6,7 @@
*/
export type { FeaturesPrivileges } from './features_privileges';
+export type { RawKibanaFeaturePrivileges, RawKibanaPrivileges } from './raw_kibana_privileges';
export type {
Role,
RoleKibanaPrivilege,
diff --git a/x-pack/packages/security/authorization_core/src/privileges/raw_kibana_privileges.ts b/x-pack/packages/security/plugin_types_common/src/authorization/raw_kibana_privileges.ts
similarity index 100%
rename from x-pack/packages/security/authorization_core/src/privileges/raw_kibana_privileges.ts
rename to x-pack/packages/security/plugin_types_common/src/authorization/raw_kibana_privileges.ts
diff --git a/x-pack/packages/security/plugin_types_public/src/privileges/privileges_api_client.ts b/x-pack/packages/security/plugin_types_public/src/privileges/privileges_api_client.ts
index 25d768cb7b1ac..4069fa574a2a0 100644
--- a/x-pack/packages/security/plugin_types_public/src/privileges/privileges_api_client.ts
+++ b/x-pack/packages/security/plugin_types_public/src/privileges/privileges_api_client.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import type { RawKibanaPrivileges } from '@kbn/security-authorization-core';
+import type { RawKibanaPrivileges } from '@kbn/security-plugin-types-common';
export interface PrivilegesAPIClientGetAllArgs {
includeActions: boolean;
diff --git a/x-pack/packages/security/plugin_types_public/tsconfig.json b/x-pack/packages/security/plugin_types_public/tsconfig.json
index 5c97e25656ecf..6779851e86367 100644
--- a/x-pack/packages/security/plugin_types_public/tsconfig.json
+++ b/x-pack/packages/security/plugin_types_public/tsconfig.json
@@ -14,6 +14,5 @@
"@kbn/core-user-profile-common",
"@kbn/security-plugin-types-common",
"@kbn/core-security-common",
- "@kbn/security-authorization-core"
]
}
diff --git a/x-pack/packages/security/role_management_model/src/kibana_privileges.ts b/x-pack/packages/security/role_management_model/src/kibana_privileges.ts
index a54ee72cf308a..ca4033047725a 100644
--- a/x-pack/packages/security/role_management_model/src/kibana_privileges.ts
+++ b/x-pack/packages/security/role_management_model/src/kibana_privileges.ts
@@ -6,8 +6,7 @@
*/
import type { KibanaFeature } from '@kbn/features-plugin/common';
-import type { RawKibanaPrivileges } from '@kbn/security-authorization-core';
-import type { RoleKibanaPrivilege } from '@kbn/security-plugin-types-common';
+import type { RawKibanaPrivileges, RoleKibanaPrivilege } from '@kbn/security-plugin-types-common';
import { KibanaPrivilege } from './kibana_privilege';
import { PrivilegeCollection } from './privilege_collection';
diff --git a/x-pack/packages/security/role_management_model/src/primary_feature_privilege.ts b/x-pack/packages/security/role_management_model/src/primary_feature_privilege.ts
index f9513c8ebb4d3..5fa03f40fbc8d 100644
--- a/x-pack/packages/security/role_management_model/src/primary_feature_privilege.ts
+++ b/x-pack/packages/security/role_management_model/src/primary_feature_privilege.ts
@@ -6,6 +6,7 @@
*/
import type { FeatureKibanaPrivileges } from '@kbn/features-plugin/public';
+import { getMinimalPrivilegeId } from '@kbn/security-authorization-core-common';
import { KibanaPrivilege } from './kibana_privilege';
@@ -18,15 +19,8 @@ export class PrimaryFeaturePrivilege extends KibanaPrivilege {
super(id, actions);
}
- public isMinimalFeaturePrivilege() {
- return this.id.startsWith('minimal_');
- }
-
public getMinimalPrivilegeId() {
- if (this.isMinimalFeaturePrivilege()) {
- return this.id;
- }
- return `minimal_${this.id}`;
+ return getMinimalPrivilegeId(this.id);
}
public get requireAllSpaces() {
diff --git a/x-pack/packages/security/role_management_model/src/secured_feature.ts b/x-pack/packages/security/role_management_model/src/secured_feature.ts
index d11b45129e6f9..f8ae1298679a2 100644
--- a/x-pack/packages/security/role_management_model/src/secured_feature.ts
+++ b/x-pack/packages/security/role_management_model/src/secured_feature.ts
@@ -7,6 +7,7 @@
import type { KibanaFeatureConfig } from '@kbn/features-plugin/common';
import { KibanaFeature } from '@kbn/features-plugin/common';
+import { getMinimalPrivilegeId } from '@kbn/security-authorization-core-common';
import { PrimaryFeaturePrivilege } from './primary_feature_privilege';
import { SecuredSubFeature } from './secured_sub_feature';
@@ -31,8 +32,14 @@ export class SecuredFeature extends KibanaFeature {
);
this.minimalPrimaryFeaturePrivileges = Object.entries(this.config.privileges || {}).map(
- ([id, privilege]) =>
- new PrimaryFeaturePrivilege(`minimal_${id}`, privilege, actionMapping[`minimal_${id}`])
+ ([id, privilege]) => {
+ const minimalPrivilegeId = getMinimalPrivilegeId(id);
+ return new PrimaryFeaturePrivilege(
+ minimalPrivilegeId,
+ privilege,
+ actionMapping[minimalPrivilegeId]
+ );
+ }
);
this.securedSubFeatures =
diff --git a/x-pack/packages/security/role_management_model/tsconfig.json b/x-pack/packages/security/role_management_model/tsconfig.json
index f18ed64fae713..026bde0ceaa11 100644
--- a/x-pack/packages/security/role_management_model/tsconfig.json
+++ b/x-pack/packages/security/role_management_model/tsconfig.json
@@ -10,6 +10,7 @@
"@kbn/features-plugin",
"@kbn/security-plugin-types-common",
"@kbn/security-authorization-core",
+ "@kbn/security-authorization-core-common",
"@kbn/licensing-plugin",
]
}
diff --git a/x-pack/plugins/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap b/x-pack/plugins/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap
index d778849347d18..94bc911557c21 100644
--- a/x-pack/plugins/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap
+++ b/x-pack/plugins/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap
@@ -5617,353 +5617,6 @@ Object {
}
`;
-exports[`Connector type config checks detect connector type changes for: .inference 1`] = `
-Object {
- "flags": Object {
- "default": Object {
- "special": "deep",
- },
- "error": [Function],
- "presence": "optional",
- },
- "keys": Object {
- "input": Object {
- "flags": Object {
- "error": [Function],
- },
- "rules": Array [
- Object {
- "args": Object {
- "method": [Function],
- },
- "name": "custom",
- },
- ],
- "type": "string",
- },
- },
- "type": "object",
-}
-`;
-
-exports[`Connector type config checks detect connector type changes for: .inference 2`] = `
-Object {
- "flags": Object {
- "default": Object {
- "special": "deep",
- },
- "error": [Function],
- "presence": "optional",
- },
- "keys": Object {
- "input": Object {
- "flags": Object {
- "default": Array [],
- "error": [Function],
- "presence": "optional",
- },
- "items": Array [
- Object {
- "flags": Object {
- "error": [Function],
- "presence": "optional",
- },
- "rules": Array [
- Object {
- "args": Object {
- "method": [Function],
- },
- "name": "custom",
- },
- ],
- "type": "string",
- },
- ],
- "type": "array",
- },
- "query": Object {
- "flags": Object {
- "error": [Function],
- },
- "rules": Array [
- Object {
- "args": Object {
- "method": [Function],
- },
- "name": "custom",
- },
- ],
- "type": "string",
- },
- },
- "type": "object",
-}
-`;
-
-exports[`Connector type config checks detect connector type changes for: .inference 3`] = `
-Object {
- "flags": Object {
- "default": Object {
- "special": "deep",
- },
- "error": [Function],
- "presence": "optional",
- },
- "keys": Object {
- "input": Object {
- "flags": Object {
- "error": [Function],
- },
- "rules": Array [
- Object {
- "args": Object {
- "method": [Function],
- },
- "name": "custom",
- },
- ],
- "type": "string",
- },
- },
- "type": "object",
-}
-`;
-
-exports[`Connector type config checks detect connector type changes for: .inference 4`] = `
-Object {
- "flags": Object {
- "default": Object {
- "special": "deep",
- },
- "error": [Function],
- "presence": "optional",
- },
- "keys": Object {
- "input": Object {
- "flags": Object {
- "error": [Function],
- },
- "rules": Array [
- Object {
- "args": Object {
- "method": [Function],
- },
- "name": "custom",
- },
- ],
- "type": "string",
- },
- "inputType": Object {
- "flags": Object {
- "error": [Function],
- },
- "rules": Array [
- Object {
- "args": Object {
- "method": [Function],
- },
- "name": "custom",
- },
- ],
- "type": "string",
- },
- },
- "type": "object",
-}
-`;
-
-exports[`Connector type config checks detect connector type changes for: .inference 5`] = `
-Object {
- "flags": Object {
- "default": Object {
- "special": "deep",
- },
- "error": [Function],
- "presence": "optional",
- },
- "keys": Object {
- "input": Object {
- "flags": Object {
- "error": [Function],
- },
- "rules": Array [
- Object {
- "args": Object {
- "method": [Function],
- },
- "name": "custom",
- },
- ],
- "type": "string",
- },
- },
- "type": "object",
-}
-`;
-
-exports[`Connector type config checks detect connector type changes for: .inference 6`] = `
-Object {
- "flags": Object {
- "default": Object {
- "special": "deep",
- },
- "error": [Function],
- "presence": "optional",
- },
- "keys": Object {
- "inferenceId": Object {
- "flags": Object {
- "error": [Function],
- },
- "rules": Array [
- Object {
- "args": Object {
- "method": [Function],
- },
- "name": "custom",
- },
- ],
- "type": "string",
- },
- "provider": Object {
- "flags": Object {
- "error": [Function],
- },
- "rules": Array [
- Object {
- "args": Object {
- "method": [Function],
- },
- "name": "custom",
- },
- ],
- "type": "string",
- },
- "providerConfig": Object {
- "flags": Object {
- "default": Object {},
- "error": [Function],
- "presence": "optional",
- "unknown": true,
- },
- "keys": Object {},
- "preferences": Object {
- "stripUnknown": Object {
- "objects": false,
- },
- },
- "type": "object",
- },
- "taskType": Object {
- "flags": Object {
- "error": [Function],
- },
- "rules": Array [
- Object {
- "args": Object {
- "method": [Function],
- },
- "name": "custom",
- },
- ],
- "type": "string",
- },
- "taskTypeConfig": Object {
- "flags": Object {
- "default": Object {},
- "error": [Function],
- "presence": "optional",
- "unknown": true,
- },
- "keys": Object {},
- "preferences": Object {
- "stripUnknown": Object {
- "objects": false,
- },
- },
- "type": "object",
- },
- },
- "type": "object",
-}
-`;
-
-exports[`Connector type config checks detect connector type changes for: .inference 7`] = `
-Object {
- "flags": Object {
- "default": Object {
- "special": "deep",
- },
- "error": [Function],
- "presence": "optional",
- },
- "keys": Object {
- "providerSecrets": Object {
- "flags": Object {
- "default": Object {},
- "error": [Function],
- "presence": "optional",
- "unknown": true,
- },
- "keys": Object {},
- "preferences": Object {
- "stripUnknown": Object {
- "objects": false,
- },
- },
- "type": "object",
- },
- },
- "type": "object",
-}
-`;
-
-exports[`Connector type config checks detect connector type changes for: .inference 8`] = `
-Object {
- "flags": Object {
- "default": Object {
- "special": "deep",
- },
- "error": [Function],
- "presence": "optional",
- },
- "keys": Object {
- "subAction": Object {
- "flags": Object {
- "error": [Function],
- },
- "rules": Array [
- Object {
- "args": Object {
- "method": [Function],
- },
- "name": "custom",
- },
- ],
- "type": "string",
- },
- "subActionParams": Object {
- "flags": Object {
- "default": Object {
- "special": "deep",
- },
- "error": [Function],
- "presence": "optional",
- "unknown": true,
- },
- "keys": Object {},
- "preferences": Object {
- "stripUnknown": Object {
- "objects": false,
- },
- },
- "type": "object",
- },
- },
- "type": "object",
-}
-`;
-
exports[`Connector type config checks detect connector type changes for: .jira 1`] = `
Object {
"flags": Object {
diff --git a/x-pack/plugins/actions/server/integration_tests/mocks/connector_types.ts b/x-pack/plugins/actions/server/integration_tests/mocks/connector_types.ts
index fff112de59f16..a26c775a74a5b 100644
--- a/x-pack/plugins/actions/server/integration_tests/mocks/connector_types.ts
+++ b/x-pack/plugins/actions/server/integration_tests/mocks/connector_types.ts
@@ -32,7 +32,6 @@ export const connectorTypes: string[] = [
'.thehive',
'.sentinelone',
'.crowdstrike',
- '.inference',
'.cases',
'.observability-ai-assistant',
];
diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/partitions_selector.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/partitions_selector.tsx
index 2034930913d6c..dddada9cd83db 100644
--- a/x-pack/plugins/aiops/public/components/change_point_detection/partitions_selector.tsx
+++ b/x-pack/plugins/aiops/public/components/change_point_detection/partitions_selector.tsx
@@ -16,7 +16,7 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { type SearchRequest } from '@elastic/elasticsearch/lib/api/types';
-import type { EuiComboBoxOptionOption } from '@elastic/eui/src/components/combo_box/types';
+import type { EuiComboBoxOptionOption } from '@elastic/eui';
import { debounce } from 'lodash';
import usePrevious from 'react-use/lib/usePrevious';
import { useAiopsAppContext } from '../../hooks/use_aiops_app_context';
diff --git a/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.test.ts b/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.test.ts
index e450cdd1e6f94..739e2d48bffd4 100644
--- a/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.test.ts
+++ b/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.test.ts
@@ -276,6 +276,9 @@ describe('mappingFromFieldMap', () => {
timestamp: {
type: 'date',
},
+ type: {
+ type: 'keyword',
+ },
uuid: {
type: 'keyword',
},
diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/list_types/index.ts b/x-pack/plugins/alerting/common/routes/rule/apis/list_types/index.ts
new file mode 100644
index 0000000000000..daac46c1381d6
--- /dev/null
+++ b/x-pack/plugins/alerting/common/routes/rule/apis/list_types/index.ts
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+export { typesRulesResponseSchema, typesRulesResponseBodySchema } from './schemas/latest';
+export type { TypesRulesResponse, TypesRulesResponseBody } from './types/latest';
+
+export {
+ typesRulesResponseSchema as typesRulesResponseSchemaV1,
+ typesRulesResponseBodySchema as typesRulesResponseBodySchemaV1,
+} from './schemas/v1';
+export type {
+ TypesRulesResponse as TypesRulesResponseV1,
+ TypesRulesResponseBody as TypesRulesResponseBodyV1,
+} from './types/v1';
diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/list_types/schemas/latest.ts b/x-pack/plugins/alerting/common/routes/rule/apis/list_types/schemas/latest.ts
new file mode 100644
index 0000000000000..25300c97a6d2e
--- /dev/null
+++ b/x-pack/plugins/alerting/common/routes/rule/apis/list_types/schemas/latest.ts
@@ -0,0 +1,8 @@
+/*
+ * 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.
+ */
+
+export * from './v1';
diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/list_types/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/list_types/schemas/v1.ts
new file mode 100644
index 0000000000000..bc38ef051ed90
--- /dev/null
+++ b/x-pack/plugins/alerting/common/routes/rule/apis/list_types/schemas/v1.ts
@@ -0,0 +1,75 @@
+/*
+ * 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 { schema } from '@kbn/config-schema';
+
+const actionVariableSchema = schema.object({
+ name: schema.string(),
+ description: schema.string(),
+ usesPublicBaseUrl: schema.maybe(schema.boolean()),
+});
+
+const actionGroupSchema = schema.object({
+ id: schema.string(),
+ name: schema.string(),
+});
+
+export const typesRulesResponseBodySchema = schema.arrayOf(
+ schema.object({
+ action_groups: schema.maybe(schema.arrayOf(actionGroupSchema)),
+ action_variables: schema.maybe(
+ schema.object({
+ context: schema.maybe(schema.arrayOf(actionVariableSchema)),
+ state: schema.maybe(schema.arrayOf(actionVariableSchema)),
+ params: schema.maybe(schema.arrayOf(actionVariableSchema)),
+ })
+ ),
+ alerts: schema.maybe(
+ schema.object({
+ context: schema.string(),
+ mappings: schema.maybe(
+ schema.object({
+ dynamic: schema.maybe(schema.oneOf([schema.literal(false), schema.literal('strict')])),
+ fieldMap: schema.recordOf(schema.string(), schema.any()),
+ shouldWrite: schema.maybe(schema.boolean()),
+ useEcs: schema.maybe(schema.boolean()),
+ })
+ ),
+ })
+ ),
+ authorized_consumers: schema.recordOf(
+ schema.string(),
+ schema.object({ read: schema.boolean(), all: schema.boolean() })
+ ),
+ category: schema.string(),
+ default_action_group_id: schema.string(),
+ default_schedule_interval: schema.maybe(schema.string()),
+ does_set_recovery_context: schema.maybe(schema.boolean()),
+ enabled_in_license: schema.boolean(),
+ fieldsForAAD: schema.maybe(schema.arrayOf(schema.string())),
+ has_alerts_mappings: schema.boolean(),
+ has_fields_for_a_a_d: schema.boolean(),
+ id: schema.string(),
+ is_exportable: schema.boolean(),
+ minimum_license_required: schema.oneOf([
+ schema.literal('basic'),
+ schema.literal('gold'),
+ schema.literal('platinum'),
+ schema.literal('standard'),
+ schema.literal('enterprise'),
+ schema.literal('trial'),
+ ]),
+ name: schema.string(),
+ producer: schema.string(),
+ recovery_action_group: actionGroupSchema,
+ rule_task_timeout: schema.maybe(schema.string()),
+ })
+);
+
+export const typesRulesResponseSchema = schema.object({
+ body: typesRulesResponseBodySchema,
+});
diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/list_types/types/latest.ts b/x-pack/plugins/alerting/common/routes/rule/apis/list_types/types/latest.ts
new file mode 100644
index 0000000000000..25300c97a6d2e
--- /dev/null
+++ b/x-pack/plugins/alerting/common/routes/rule/apis/list_types/types/latest.ts
@@ -0,0 +1,8 @@
+/*
+ * 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.
+ */
+
+export * from './v1';
diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/list_types/types/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/list_types/types/v1.ts
new file mode 100644
index 0000000000000..380b48c1c6615
--- /dev/null
+++ b/x-pack/plugins/alerting/common/routes/rule/apis/list_types/types/v1.ts
@@ -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 type { TypeOf } from '@kbn/config-schema';
+
+import { typesRulesResponseSchemaV1, typesRulesResponseBodySchemaV1 } from '..';
+
+export type TypesRulesResponse = TypeOf;
+export type TypesRulesResponseBody = TypeOf;
diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/index.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/index.ts
new file mode 100644
index 0000000000000..ba1dd568aeeb2
--- /dev/null
+++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/index.ts
@@ -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.
+ */
+
+export { muteAllRuleRequestParamsSchema } from './schemas/latest';
+export type { MuteAllRuleRequestParams } from './types/latest';
+
+export { muteAllRuleRequestParamsSchema as muteAllRuleRequestParamsSchemaV1 } from './schemas/v1';
+export type { MuteAllRuleRequestParams as MuteAllRuleRequestParamsV1 } from './types/v1';
diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/latest.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/latest.ts
new file mode 100644
index 0000000000000..25300c97a6d2e
--- /dev/null
+++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/latest.ts
@@ -0,0 +1,8 @@
+/*
+ * 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.
+ */
+
+export * from './v1';
diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/v1.ts
new file mode 100644
index 0000000000000..9305dac3d46eb
--- /dev/null
+++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/v1.ts
@@ -0,0 +1,16 @@
+/*
+ * 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 { schema } from '@kbn/config-schema';
+
+export const muteAllRuleRequestParamsSchema = schema.object({
+ id: schema.string({
+ meta: {
+ description: 'The identifier for the rule.',
+ },
+ }),
+});
diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/latest.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/latest.ts
new file mode 100644
index 0000000000000..25300c97a6d2e
--- /dev/null
+++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/latest.ts
@@ -0,0 +1,8 @@
+/*
+ * 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.
+ */
+
+export * from './v1';
diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/v1.ts
new file mode 100644
index 0000000000000..c18aa22dadd13
--- /dev/null
+++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/v1.ts
@@ -0,0 +1,11 @@
+/*
+ * 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 type { TypeOf } from '@kbn/config-schema';
+import { muteAllRuleRequestParamsSchemaV1 } from '..';
+
+export type MuteAllRuleRequestParams = TypeOf;
diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/unmute_all/index.ts b/x-pack/plugins/alerting/common/routes/rule/apis/unmute_all/index.ts
new file mode 100644
index 0000000000000..022a5eee7b942
--- /dev/null
+++ b/x-pack/plugins/alerting/common/routes/rule/apis/unmute_all/index.ts
@@ -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.
+ */
+
+export { unmuteAllRuleRequestParamsSchema } from './schemas/latest';
+export type { UnmuteAllRuleRequestParams } from './types/latest';
+
+export { unmuteAllRuleRequestParamsSchema as unmuteAllRuleRequestParamsSchemaV1 } from './schemas/v1';
+export type { UnmuteAllRuleRequestParams as UnmuteAllRuleRequestParamsV1 } from './types/v1';
diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/unmute_all/schemas/latest.ts b/x-pack/plugins/alerting/common/routes/rule/apis/unmute_all/schemas/latest.ts
new file mode 100644
index 0000000000000..25300c97a6d2e
--- /dev/null
+++ b/x-pack/plugins/alerting/common/routes/rule/apis/unmute_all/schemas/latest.ts
@@ -0,0 +1,8 @@
+/*
+ * 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.
+ */
+
+export * from './v1';
diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/unmute_all/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/unmute_all/schemas/v1.ts
new file mode 100644
index 0000000000000..890ba1925fbef
--- /dev/null
+++ b/x-pack/plugins/alerting/common/routes/rule/apis/unmute_all/schemas/v1.ts
@@ -0,0 +1,16 @@
+/*
+ * 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 { schema } from '@kbn/config-schema';
+
+export const unmuteAllRuleRequestParamsSchema = schema.object({
+ id: schema.string({
+ meta: {
+ description: 'The identifier for the rule.',
+ },
+ }),
+});
diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/unmute_all/types/latest.ts b/x-pack/plugins/alerting/common/routes/rule/apis/unmute_all/types/latest.ts
new file mode 100644
index 0000000000000..25300c97a6d2e
--- /dev/null
+++ b/x-pack/plugins/alerting/common/routes/rule/apis/unmute_all/types/latest.ts
@@ -0,0 +1,8 @@
+/*
+ * 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.
+ */
+
+export * from './v1';
diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/unmute_all/types/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/unmute_all/types/v1.ts
new file mode 100644
index 0000000000000..25638574f3972
--- /dev/null
+++ b/x-pack/plugins/alerting/common/routes/rule/apis/unmute_all/types/v1.ts
@@ -0,0 +1,11 @@
+/*
+ * 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 type { TypeOf } from '@kbn/config-schema';
+import { unmuteAllRuleRequestParamsSchemaV1 } from '..';
+
+export type UnmuteAllRuleRequestParams = TypeOf;
diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_all/index.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/index.ts
new file mode 100644
index 0000000000000..c8b85c149314e
--- /dev/null
+++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/index.ts
@@ -0,0 +1,9 @@
+/*
+ * 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.
+ */
+
+export type { MuteAllRuleParams } from './types';
+export { muteAll } from './mute_all';
diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.test.ts
new file mode 100644
index 0000000000000..eba9fc4cbf7d4
--- /dev/null
+++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.test.ts
@@ -0,0 +1,75 @@
+/*
+ * 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 { RulesClientContext } from '../../../../rules_client';
+import { muteAll } from './mute_all';
+import { savedObjectsRepositoryMock } from '@kbn/core-saved-objects-api-server-mocks';
+
+jest.mock('../../../../lib/retry_if_conflicts', () => ({
+ retryIfConflicts: (_: unknown, id: unknown, asyncFn: () => Promise) => {
+ return asyncFn();
+ },
+}));
+
+jest.mock('../../../../rules_client/lib', () => ({
+ updateMetaAttributes: () => {},
+}));
+
+jest.mock('../../../../saved_objects', () => ({
+ partiallyUpdateRule: async () => {},
+}));
+
+const loggerErrorMock = jest.fn();
+const getBulkMock = jest.fn();
+
+const savedObjectsMock = savedObjectsRepositoryMock.create();
+savedObjectsMock.get = jest.fn().mockReturnValue({
+ attributes: {
+ actions: [],
+ },
+ version: '9.0.0',
+});
+
+const context = {
+ logger: { error: loggerErrorMock },
+ getActionsClient: () => {
+ return {
+ getBulk: getBulkMock,
+ };
+ },
+ unsecuredSavedObjectsClient: savedObjectsMock,
+ authorization: { ensureAuthorized: async () => {} },
+ ruleTypeRegistry: {
+ ensureRuleTypeEnabled: () => {},
+ },
+ getUserName: async () => {},
+} as unknown as RulesClientContext;
+
+describe('validateMuteAllParams', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should not throw an error for valid params', () => {
+ const validParams = {
+ id: 'ble',
+ };
+
+ expect(() => muteAll(context, validParams)).not.toThrow();
+ expect(savedObjectsMock.get).toHaveBeenCalled();
+ });
+
+ it('should throw Boom.badRequest for invalid params', async () => {
+ const invalidParams = {
+ id: 22 as unknown as string, // type workaround to send wrong data validation
+ };
+
+ await expect(muteAll(context, invalidParams)).rejects.toThrowErrorMatchingInlineSnapshot(
+ `"Error validating mute all parameters - [id]: expected value of type [string] but got [number]"`
+ );
+ });
+});
diff --git a/x-pack/plugins/alerting/server/rules_client/methods/mute_all.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.ts
similarity index 65%
rename from x-pack/plugins/alerting/server/rules_client/methods/mute_all.ts
rename to x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.ts
index 4e647ee6e58ac..73cfe6e26fdce 100644
--- a/x-pack/plugins/alerting/server/rules_client/methods/mute_all.ts
+++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.ts
@@ -5,17 +5,23 @@
* 2.0.
*/
-import { RawRule } from '../../types';
-import { WriteOperations, AlertingAuthorizationEntity } from '../../authorization';
-import { retryIfConflicts } from '../../lib/retry_if_conflicts';
-import { partiallyUpdateRule, RULE_SAVED_OBJECT_TYPE } from '../../saved_objects';
-import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events';
-import { RulesClientContext } from '../types';
-import { updateMetaAttributes } from '../lib';
-import { clearUnscheduledSnoozeAttributes } from '../common';
-import { RuleAttributes } from '../../data/rule/types';
+import Boom from '@hapi/boom';
+import { RawRule } from '../../../../types';
+import { WriteOperations, AlertingAuthorizationEntity } from '../../../../authorization';
+import { retryIfConflicts } from '../../../../lib/retry_if_conflicts';
+import { partiallyUpdateRule, RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects';
+import { ruleAuditEvent, RuleAuditAction } from '../../../../rules_client/common/audit_events';
+import { RulesClientContext } from '../../../../rules_client/types';
+import { updateMetaAttributes } from '../../../../rules_client/lib';
+import { clearUnscheduledSnoozeAttributes } from '../../../../rules_client/common';
+import { RuleAttributes } from '../../../../data/rule/types';
+import { MuteAllRuleParams } from './types';
+import { muteAllRuleParamsSchema } from './schemas';
-export async function muteAll(context: RulesClientContext, { id }: { id: string }): Promise {
+export async function muteAll(
+ context: RulesClientContext,
+ { id }: MuteAllRuleParams
+): Promise {
return await retryIfConflicts(
context.logger,
`rulesClient.muteAll('${id}')`,
@@ -23,7 +29,14 @@ export async function muteAll(context: RulesClientContext, { id }: { id: string
);
}
-async function muteAllWithOCC(context: RulesClientContext, { id }: { id: string }) {
+async function muteAllWithOCC(context: RulesClientContext, params: MuteAllRuleParams) {
+ try {
+ muteAllRuleParamsSchema.validate(params);
+ } catch (error) {
+ throw Boom.badRequest(`Error validating mute all parameters - ${error.message}`);
+ }
+
+ const { id } = params;
const { attributes, version } = await context.unsecuredSavedObjectsClient.get(
RULE_SAVED_OBJECT_TYPE,
id
diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/index.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/index.ts
new file mode 100644
index 0000000000000..b6c6729ac5029
--- /dev/null
+++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/index.ts
@@ -0,0 +1,8 @@
+/*
+ * 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.
+ */
+
+export * from './mute_all_rule_schemas';
diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/mute_all_rule_schemas.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/mute_all_rule_schemas.ts
new file mode 100644
index 0000000000000..0d0ae33394e72
--- /dev/null
+++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/mute_all_rule_schemas.ts
@@ -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 { schema } from '@kbn/config-schema';
+
+export const muteAllRuleParamsSchema = schema.object({
+ id: schema.string(),
+});
diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/index.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/index.ts
new file mode 100644
index 0000000000000..c2d2f7401b350
--- /dev/null
+++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/index.ts
@@ -0,0 +1,8 @@
+/*
+ * 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.
+ */
+
+export * from './mute_all_rule_types';
diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/mute_all_rule_types.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/mute_all_rule_types.ts
new file mode 100644
index 0000000000000..4f4ad36dbc23a
--- /dev/null
+++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/mute_all_rule_types.ts
@@ -0,0 +1,11 @@
+/*
+ * 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 type { TypeOf } from '@kbn/config-schema';
+import { muteAllRuleParamsSchema } from '../schemas';
+
+export type MuteAllRuleParams = TypeOf;
diff --git a/x-pack/plugins/alerting/server/rules_client/methods/list_rule_types.ts b/x-pack/plugins/alerting/server/application/rule/methods/rule_types/rule_types.ts
similarity index 74%
rename from x-pack/plugins/alerting/server/rules_client/methods/list_rule_types.ts
rename to x-pack/plugins/alerting/server/application/rule/methods/rule_types/rule_types.ts
index 51f5b8f8af7ad..66256b4b7d7eb 100644
--- a/x-pack/plugins/alerting/server/rules_client/methods/list_rule_types.ts
+++ b/x-pack/plugins/alerting/server/application/rule/methods/rule_types/rule_types.ts
@@ -5,8 +5,12 @@
* 2.0.
*/
-import { WriteOperations, ReadOperations, AlertingAuthorizationEntity } from '../../authorization';
-import { RulesClientContext } from '../types';
+import {
+ WriteOperations,
+ ReadOperations,
+ AlertingAuthorizationEntity,
+} from '../../../../authorization';
+import { RulesClientContext } from '../../../../rules_client/types';
export async function listRuleTypes(context: RulesClientContext) {
return await context.authorization.filterByRuleTypeAuthorization(
diff --git a/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/index.ts b/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/index.ts
new file mode 100644
index 0000000000000..3dcd4557188af
--- /dev/null
+++ b/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/index.ts
@@ -0,0 +1,8 @@
+/*
+ * 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.
+ */
+
+export { unmuteAll } from './unmute_all';
diff --git a/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/schemas/index.ts b/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/schemas/index.ts
new file mode 100644
index 0000000000000..27354c6d51bef
--- /dev/null
+++ b/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/schemas/index.ts
@@ -0,0 +1,8 @@
+/*
+ * 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.
+ */
+
+export * from './unmute_all_rule_schemas';
diff --git a/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/schemas/unmute_all_rule_schemas.ts b/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/schemas/unmute_all_rule_schemas.ts
new file mode 100644
index 0000000000000..28be642e9ca49
--- /dev/null
+++ b/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/schemas/unmute_all_rule_schemas.ts
@@ -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 { schema } from '@kbn/config-schema';
+
+export const unmuteAllRuleParamsSchema = schema.object({
+ id: schema.string(),
+});
diff --git a/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/types/index.ts b/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/types/index.ts
new file mode 100644
index 0000000000000..36ee54afdda07
--- /dev/null
+++ b/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/types/index.ts
@@ -0,0 +1,8 @@
+/*
+ * 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.
+ */
+
+export * from './unmute_all_rule_types';
diff --git a/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/types/unmute_all_rule_types.ts b/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/types/unmute_all_rule_types.ts
new file mode 100644
index 0000000000000..3e82d8901dc01
--- /dev/null
+++ b/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/types/unmute_all_rule_types.ts
@@ -0,0 +1,11 @@
+/*
+ * 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 type { TypeOf } from '@kbn/config-schema';
+import { unmuteAllRuleParamsSchema } from '../schemas';
+
+export type UnmuteAllRuleParams = TypeOf;
diff --git a/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/unmute_all.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/unmute_all.test.ts
new file mode 100644
index 0000000000000..531b157a66899
--- /dev/null
+++ b/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/unmute_all.test.ts
@@ -0,0 +1,76 @@
+/*
+ * 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 { RulesClientContext } from '../../../../rules_client';
+import { unmuteAll } from './unmute_all';
+import { savedObjectsRepositoryMock } from '@kbn/core-saved-objects-api-server-mocks';
+
+jest.mock('../../../../lib/retry_if_conflicts', () => ({
+ retryIfConflicts: (_: unknown, id: unknown, asyncFn: () => Promise) => {
+ return asyncFn();
+ },
+}));
+
+jest.mock('../../../../rules_client/lib', () => ({
+ updateMetaAttributes: () => {},
+}));
+
+jest.mock('../../../../saved_objects', () => ({
+ partiallyUpdateRule: async () => {},
+}));
+
+const loggerErrorMock = jest.fn();
+const getBulkMock = jest.fn();
+
+const savedObjectsMock = savedObjectsRepositoryMock.create();
+savedObjectsMock.get = jest.fn().mockReturnValue({
+ attributes: {
+ actions: [],
+ },
+ version: '9.0.0',
+});
+
+const context = {
+ logger: { error: loggerErrorMock },
+ getActionsClient: () => {
+ return {
+ getBulk: getBulkMock,
+ };
+ },
+ unsecuredSavedObjectsClient: savedObjectsMock,
+ authorization: { ensureAuthorized: async () => {} },
+ ruleTypeRegistry: {
+ ensureRuleTypeEnabled: () => {},
+ },
+ getUserName: async () => {},
+} as unknown as RulesClientContext;
+
+describe('validate unmuteAll parameters', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should not throw an error for valid params', () => {
+ const validParams = {
+ id: 'ble',
+ };
+
+ expect(() => unmuteAll(context, validParams)).not.toThrow();
+ expect(savedObjectsMock.get).toHaveBeenCalled();
+ });
+
+ it('should throw Boom.badRequest for invalid params', async () => {
+ const invalidParams = {
+ id: 22,
+ };
+
+ // @ts-expect-error wrong type for testing purposes
+ await expect(unmuteAll(context, invalidParams)).rejects.toThrowErrorMatchingInlineSnapshot(
+ `"Error validating unmute all parameters - [id]: expected value of type [string] but got [number]"`
+ );
+ });
+});
diff --git a/x-pack/plugins/alerting/server/rules_client/methods/unmute_all.ts b/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/unmute_all.ts
similarity index 67%
rename from x-pack/plugins/alerting/server/rules_client/methods/unmute_all.ts
rename to x-pack/plugins/alerting/server/application/rule/methods/unmute_all/unmute_all.ts
index e007633e4012d..722cfed3700d0 100644
--- a/x-pack/plugins/alerting/server/rules_client/methods/unmute_all.ts
+++ b/x-pack/plugins/alerting/server/application/rule/methods/unmute_all/unmute_all.ts
@@ -5,19 +5,22 @@
* 2.0.
*/
-import { RawRule } from '../../types';
-import { WriteOperations, AlertingAuthorizationEntity } from '../../authorization';
-import { retryIfConflicts } from '../../lib/retry_if_conflicts';
-import { partiallyUpdateRule, RULE_SAVED_OBJECT_TYPE } from '../../saved_objects';
-import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events';
-import { RulesClientContext } from '../types';
-import { updateMetaAttributes } from '../lib';
-import { clearUnscheduledSnoozeAttributes } from '../common';
-import { RuleAttributes } from '../../data/rule/types';
+import Boom from '@hapi/boom';
+import { RawRule } from '../../../../types';
+import { WriteOperations, AlertingAuthorizationEntity } from '../../../../authorization';
+import { retryIfConflicts } from '../../../../lib/retry_if_conflicts';
+import { partiallyUpdateRule, RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects';
+import { ruleAuditEvent, RuleAuditAction } from '../../../../rules_client/common/audit_events';
+import { RulesClientContext } from '../../../../rules_client/types';
+import { updateMetaAttributes } from '../../../../rules_client/lib';
+import { clearUnscheduledSnoozeAttributes } from '../../../../rules_client/common';
+import { RuleAttributes } from '../../../../data/rule/types';
+import { UnmuteAllRuleParams } from './types';
+import { unmuteAllRuleParamsSchema } from './schemas';
export async function unmuteAll(
context: RulesClientContext,
- { id }: { id: string }
+ { id }: UnmuteAllRuleParams
): Promise {
return await retryIfConflicts(
context.logger,
@@ -26,7 +29,14 @@ export async function unmuteAll(
);
}
-async function unmuteAllWithOCC(context: RulesClientContext, { id }: { id: string }) {
+async function unmuteAllWithOCC(context: RulesClientContext, params: UnmuteAllRuleParams) {
+ try {
+ unmuteAllRuleParamsSchema.validate(params);
+ } catch (error) {
+ throw Boom.badRequest(`Error validating unmute all parameters - ${error.message}`);
+ }
+
+ const { id } = params;
const { attributes, version } = await context.unsecuredSavedObjectsClient.get(
RULE_SAVED_OBJECT_TYPE,
id
diff --git a/x-pack/plugins/alerting/server/integration_tests/__snapshots__/alert_as_data_fields.test.ts.snap b/x-pack/plugins/alerting/server/integration_tests/__snapshots__/alert_as_data_fields.test.ts.snap
index 072d8c59a53ff..8c65843f2d844 100644
--- a/x-pack/plugins/alerting/server/integration_tests/__snapshots__/alert_as_data_fields.test.ts.snap
+++ b/x-pack/plugins/alerting/server/integration_tests/__snapshots__/alert_as_data_fields.test.ts.snap
@@ -1075,6 +1075,11 @@ Object {
"required": false,
"type": "date",
},
+ "kibana.alert.rule.execution.type": Object {
+ "array": false,
+ "required": false,
+ "type": "keyword",
+ },
"kibana.alert.rule.execution.uuid": Object {
"array": false,
"required": false,
@@ -2173,6 +2178,11 @@ Object {
"required": false,
"type": "date",
},
+ "kibana.alert.rule.execution.type": Object {
+ "array": false,
+ "required": false,
+ "type": "keyword",
+ },
"kibana.alert.rule.execution.uuid": Object {
"array": false,
"required": false,
@@ -3271,6 +3281,11 @@ Object {
"required": false,
"type": "date",
},
+ "kibana.alert.rule.execution.type": Object {
+ "array": false,
+ "required": false,
+ "type": "keyword",
+ },
"kibana.alert.rule.execution.uuid": Object {
"array": false,
"required": false,
@@ -4369,6 +4384,11 @@ Object {
"required": false,
"type": "date",
},
+ "kibana.alert.rule.execution.type": Object {
+ "array": false,
+ "required": false,
+ "type": "keyword",
+ },
"kibana.alert.rule.execution.uuid": Object {
"array": false,
"required": false,
@@ -5467,6 +5487,11 @@ Object {
"required": false,
"type": "date",
},
+ "kibana.alert.rule.execution.type": Object {
+ "array": false,
+ "required": false,
+ "type": "keyword",
+ },
"kibana.alert.rule.execution.uuid": Object {
"array": false,
"required": false,
@@ -6571,6 +6596,11 @@ Object {
"required": false,
"type": "date",
},
+ "kibana.alert.rule.execution.type": Object {
+ "array": false,
+ "required": false,
+ "type": "keyword",
+ },
"kibana.alert.rule.execution.uuid": Object {
"array": false,
"required": false,
@@ -7669,6 +7699,11 @@ Object {
"required": false,
"type": "date",
},
+ "kibana.alert.rule.execution.type": Object {
+ "array": false,
+ "required": false,
+ "type": "keyword",
+ },
"kibana.alert.rule.execution.uuid": Object {
"array": false,
"required": false,
@@ -8767,6 +8802,11 @@ Object {
"required": false,
"type": "date",
},
+ "kibana.alert.rule.execution.type": Object {
+ "array": false,
+ "required": false,
+ "type": "keyword",
+ },
"kibana.alert.rule.execution.uuid": Object {
"array": false,
"required": false,
diff --git a/x-pack/plugins/alerting/server/routes/index.ts b/x-pack/plugins/alerting/server/routes/index.ts
index 93b1800208c7d..eee0382dd834c 100644
--- a/x-pack/plugins/alerting/server/routes/index.ts
+++ b/x-pack/plugins/alerting/server/routes/index.ts
@@ -30,10 +30,10 @@ import { getRuleExecutionKPIRoute } from './get_rule_execution_kpi';
import { getRuleStateRoute } from './get_rule_state';
import { healthRoute } from './health';
import { resolveRuleRoute } from './rule/apis/resolve';
-import { ruleTypesRoute } from './rule_types';
-import { muteAllRuleRoute } from './mute_all_rule';
+import { ruleTypesRoute } from './rule/apis/list_types/rule_types';
+import { muteAllRuleRoute } from './rule/apis/mute_all/mute_all_rule';
import { muteAlertRoute } from './rule/apis/mute_alert/mute_alert';
-import { unmuteAllRuleRoute } from './unmute_all_rule';
+import { unmuteAllRuleRoute } from './rule/apis/unmute_all';
import { unmuteAlertRoute } from './rule/apis/unmute_alert/unmute_alert_route';
import { updateRuleApiKeyRoute } from './rule/apis/update_api_key/update_rule_api_key_route';
import { bulkEditInternalRulesRoute } from './rule/apis/bulk_edit/bulk_edit_rules_route';
diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/list_types/index.ts b/x-pack/plugins/alerting/server/routes/rule/apis/list_types/index.ts
new file mode 100644
index 0000000000000..01f4d106a62ba
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/rule/apis/list_types/index.ts
@@ -0,0 +1,8 @@
+/*
+ * 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.
+ */
+
+export { ruleTypesRoute } from './rule_types';
diff --git a/x-pack/plugins/alerting/server/routes/rule_types.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/list_types/rule_types.test.ts
similarity index 93%
rename from x-pack/plugins/alerting/server/routes/rule_types.test.ts
rename to x-pack/plugins/alerting/server/routes/rule/apis/list_types/rule_types.test.ts
index a6483f15f9f1c..e6293a589743b 100644
--- a/x-pack/plugins/alerting/server/routes/rule_types.test.ts
+++ b/x-pack/plugins/alerting/server/routes/rule/apis/list_types/rule_types.test.ts
@@ -7,17 +7,17 @@
import { ruleTypesRoute } from './rule_types';
import { httpServiceMock } from '@kbn/core/server/mocks';
-import { licenseStateMock } from '../lib/license_state.mock';
-import { verifyApiAccess } from '../lib/license_api_access';
-import { mockHandlerArguments } from './_mock_handler_arguments';
-import { rulesClientMock } from '../rules_client.mock';
-import { RecoveredActionGroup } from '../../common';
-import { RegistryAlertTypeWithAuth } from '../authorization';
-import { AsApiContract } from './lib';
+import { licenseStateMock } from '../../../../lib/license_state.mock';
+import { verifyApiAccess } from '../../../../lib/license_api_access';
+import { mockHandlerArguments } from '../../../_mock_handler_arguments';
+import { rulesClientMock } from '../../../../rules_client.mock';
+import { RecoveredActionGroup } from '../../../../../common';
+import { RegistryAlertTypeWithAuth } from '../../../../authorization';
+import { AsApiContract } from '../../../lib';
const rulesClient = rulesClientMock.create();
-jest.mock('../lib/license_api_access', () => ({
+jest.mock('../../../../lib/license_api_access', () => ({
verifyApiAccess: jest.fn(),
}));
diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/list_types/rule_types.ts b/x-pack/plugins/alerting/server/routes/rule/apis/list_types/rule_types.ts
new file mode 100644
index 0000000000000..d6f2ffbe9af0c
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/rule/apis/list_types/rule_types.ts
@@ -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 { IRouter } from '@kbn/core/server';
+import { TypesRulesResponseBodyV1 } from '../../../../../common/routes/rule/apis/list_types';
+import { ILicenseState } from '../../../../lib';
+import { verifyAccessAndContext } from '../../../lib';
+import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../../../../types';
+import { transformRuleTypesResponseV1 } from './transforms';
+
+export const ruleTypesRoute = (
+ router: IRouter,
+ licenseState: ILicenseState
+) => {
+ router.get(
+ {
+ path: `${BASE_ALERTING_API_PATH}/rule_types`,
+ options: {
+ access: 'public',
+ summary: `Get the rule types`,
+ tags: ['oas-tag:alerting'],
+ },
+ validate: {},
+ },
+ router.handleLegacyErrors(
+ verifyAccessAndContext(licenseState, async function (context, req, res) {
+ const rulesClient = (await context.alerting).getRulesClient();
+ const ruleTypes = await rulesClient.listRuleTypes();
+
+ const responseBody: TypesRulesResponseBodyV1 = transformRuleTypesResponseV1(ruleTypes);
+
+ return res.ok({
+ body: responseBody,
+ });
+ })
+ )
+ );
+};
diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/list_types/transforms/index.ts b/x-pack/plugins/alerting/server/routes/rule/apis/list_types/transforms/index.ts
new file mode 100644
index 0000000000000..ac825ed771c48
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/rule/apis/list_types/transforms/index.ts
@@ -0,0 +1,9 @@
+/*
+ * 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.
+ */
+
+export { transformRuleTypesResponse } from './transform_rule_types_response/latest';
+export { transformRuleTypesResponse as transformRuleTypesResponseV1 } from './transform_rule_types_response/v1';
diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/list_types/transforms/transform_rule_types_response/latest.ts b/x-pack/plugins/alerting/server/routes/rule/apis/list_types/transforms/transform_rule_types_response/latest.ts
new file mode 100644
index 0000000000000..25300c97a6d2e
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/rule/apis/list_types/transforms/transform_rule_types_response/latest.ts
@@ -0,0 +1,8 @@
+/*
+ * 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.
+ */
+
+export * from './v1';
diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/list_types/transforms/transform_rule_types_response/v1.ts b/x-pack/plugins/alerting/server/routes/rule/apis/list_types/transforms/transform_rule_types_response/v1.ts
new file mode 100644
index 0000000000000..54a5874331c86
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/rule/apis/list_types/transforms/transform_rule_types_response/v1.ts
@@ -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 { isBoolean } from 'lodash/fp';
+import { RegistryAlertTypeWithAuth } from '../../../../../../authorization';
+import type { TypesRulesResponseBodyV1 } from '../../../../../../../common/routes/rule/apis/list_types';
+
+export const transformRuleTypesResponse = (
+ ruleTypes: Set
+): TypesRulesResponseBodyV1 => {
+ return Array.from(ruleTypes).map((ruleType: RegistryAlertTypeWithAuth) => {
+ return {
+ ...(ruleType.actionGroups ? { action_groups: ruleType.actionGroups } : {}),
+ ...(ruleType.actionVariables ? { action_variables: ruleType.actionVariables } : {}),
+ ...(ruleType.alerts ? { alerts: ruleType.alerts } : {}),
+ authorized_consumers: ruleType.authorizedConsumers,
+ category: ruleType.category,
+ default_action_group_id: ruleType.defaultActionGroupId,
+ ...(ruleType.defaultScheduleInterval
+ ? { default_schedule_interval: ruleType.defaultScheduleInterval }
+ : {}),
+ ...(isBoolean(ruleType.doesSetRecoveryContext)
+ ? { does_set_recovery_context: ruleType.doesSetRecoveryContext }
+ : {}),
+ enabled_in_license: ruleType.enabledInLicense,
+ ...(ruleType.fieldsForAAD ? { fieldsForAAD: ruleType.fieldsForAAD } : {}),
+ has_alerts_mappings: ruleType.hasAlertsMappings,
+ has_fields_for_a_a_d: ruleType.hasFieldsForAAD,
+ id: ruleType.id,
+ is_exportable: ruleType.isExportable,
+ minimum_license_required: ruleType.minimumLicenseRequired,
+ name: ruleType.name,
+ producer: ruleType.producer,
+ recovery_action_group: ruleType.recoveryActionGroup,
+ ...(ruleType.ruleTaskTimeout ? { rule_task_timeout: ruleType.ruleTaskTimeout } : {}),
+ };
+ });
+};
diff --git a/x-pack/plugins/alerting/server/routes/mute_all_rule.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/mute_all/mute_all_rule.test.ts
similarity index 85%
rename from x-pack/plugins/alerting/server/routes/mute_all_rule.test.ts
rename to x-pack/plugins/alerting/server/routes/rule/apis/mute_all/mute_all_rule.test.ts
index 131c20eb84a72..98cabee56904b 100644
--- a/x-pack/plugins/alerting/server/routes/mute_all_rule.test.ts
+++ b/x-pack/plugins/alerting/server/routes/rule/apis/mute_all/mute_all_rule.test.ts
@@ -7,18 +7,18 @@
import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock';
import { muteAllRuleRoute } from './mute_all_rule';
import { httpServiceMock } from '@kbn/core/server/mocks';
-import { licenseStateMock } from '../lib/license_state.mock';
-import { mockHandlerArguments } from './_mock_handler_arguments';
-import { rulesClientMock } from '../rules_client.mock';
-import { RuleTypeDisabledError } from '../lib/errors/rule_type_disabled';
-import { trackDeprecatedRouteUsage } from '../lib/track_deprecated_route_usage';
+import { licenseStateMock } from '../../../../lib/license_state.mock';
+import { mockHandlerArguments } from '../../../_mock_handler_arguments';
+import { rulesClientMock } from '../../../../rules_client.mock';
+import { RuleTypeDisabledError } from '../../../../lib/errors/rule_type_disabled';
+import { trackDeprecatedRouteUsage } from '../../../../lib/track_deprecated_route_usage';
const rulesClient = rulesClientMock.create();
-jest.mock('../lib/license_api_access', () => ({
+jest.mock('../../../../lib/license_api_access', () => ({
verifyApiAccess: jest.fn(),
}));
-jest.mock('../lib/track_deprecated_route_usage', () => ({
+jest.mock('../../../../lib/track_deprecated_route_usage', () => ({
trackDeprecatedRouteUsage: jest.fn(),
}));
diff --git a/x-pack/plugins/alerting/server/routes/mute_all_rule.ts b/x-pack/plugins/alerting/server/routes/rule/apis/mute_all/mute_all_rule.ts
similarity index 72%
rename from x-pack/plugins/alerting/server/routes/mute_all_rule.ts
rename to x-pack/plugins/alerting/server/routes/rule/apis/mute_all/mute_all_rule.ts
index ab220f7444590..8ac77973575bb 100644
--- a/x-pack/plugins/alerting/server/routes/mute_all_rule.ts
+++ b/x-pack/plugins/alerting/server/routes/rule/apis/mute_all/mute_all_rule.ts
@@ -6,20 +6,15 @@
*/
import { IRouter } from '@kbn/core/server';
-import { schema } from '@kbn/config-schema';
import { UsageCounter } from '@kbn/usage-collection-plugin/server';
-import { ILicenseState, RuleTypeDisabledError } from '../lib';
-import { verifyAccessAndContext } from './lib';
-import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types';
-import { trackDeprecatedRouteUsage } from '../lib/track_deprecated_route_usage';
-
-const paramSchema = schema.object({
- id: schema.string({
- meta: {
- description: 'The identifier for the rule.',
- },
- }),
-});
+import { ILicenseState, RuleTypeDisabledError } from '../../../../lib';
+import { verifyAccessAndContext } from '../../../lib';
+import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../../../../types';
+import { trackDeprecatedRouteUsage } from '../../../../lib/track_deprecated_route_usage';
+import {
+ muteAllRuleRequestParamsSchemaV1,
+ MuteAllRuleRequestParamsV1,
+} from '../../../../../common/routes/rule/apis/mute_all';
export const muteAllRuleRoute = (
router: IRouter,
@@ -36,7 +31,7 @@ export const muteAllRuleRoute = (
},
validate: {
request: {
- params: paramSchema,
+ params: muteAllRuleRequestParamsSchemaV1,
},
response: {
204: {
@@ -48,10 +43,10 @@ export const muteAllRuleRoute = (
router.handleLegacyErrors(
verifyAccessAndContext(licenseState, async function (context, req, res) {
const rulesClient = (await context.alerting).getRulesClient();
- const { id } = req.params;
+ const params: MuteAllRuleRequestParamsV1 = req.params;
trackDeprecatedRouteUsage('muteAll', usageCounter);
try {
- await rulesClient.muteAll({ id });
+ await rulesClient.muteAll(params);
return res.noContent();
} catch (e) {
if (e instanceof RuleTypeDisabledError) {
diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/unmute_all/index.ts b/x-pack/plugins/alerting/server/routes/rule/apis/unmute_all/index.ts
new file mode 100644
index 0000000000000..cd6f357ef9130
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/rule/apis/unmute_all/index.ts
@@ -0,0 +1,8 @@
+/*
+ * 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.
+ */
+
+export { unmuteAllRuleRoute } from './unmute_all_rule';
diff --git a/x-pack/plugins/alerting/server/routes/unmute_all_rule.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/unmute_all/unmute_all_rule.test.ts
similarity index 85%
rename from x-pack/plugins/alerting/server/routes/unmute_all_rule.test.ts
rename to x-pack/plugins/alerting/server/routes/rule/apis/unmute_all/unmute_all_rule.test.ts
index 8671247987bd4..e597e1b0fdef8 100644
--- a/x-pack/plugins/alerting/server/routes/unmute_all_rule.test.ts
+++ b/x-pack/plugins/alerting/server/routes/rule/apis/unmute_all/unmute_all_rule.test.ts
@@ -7,13 +7,13 @@
import { unmuteAllRuleRoute } from './unmute_all_rule';
import { httpServiceMock } from '@kbn/core/server/mocks';
-import { licenseStateMock } from '../lib/license_state.mock';
-import { mockHandlerArguments } from './_mock_handler_arguments';
-import { rulesClientMock } from '../rules_client.mock';
-import { RuleTypeDisabledError } from '../lib/errors/rule_type_disabled';
+import { licenseStateMock } from '../../../../lib/license_state.mock';
+import { mockHandlerArguments } from '../../../_mock_handler_arguments';
+import { rulesClientMock } from '../../../../rules_client.mock';
+import { RuleTypeDisabledError } from '../../../../lib/errors/rule_type_disabled';
const rulesClient = rulesClientMock.create();
-jest.mock('../lib/license_api_access', () => ({
+jest.mock('../../../../lib/license_api_access', () => ({
verifyApiAccess: jest.fn(),
}));
diff --git a/x-pack/plugins/alerting/server/routes/unmute_all_rule.ts b/x-pack/plugins/alerting/server/routes/rule/apis/unmute_all/unmute_all_rule.ts
similarity index 73%
rename from x-pack/plugins/alerting/server/routes/unmute_all_rule.ts
rename to x-pack/plugins/alerting/server/routes/rule/apis/unmute_all/unmute_all_rule.ts
index a6a706a34eafd..f9ab7d8d8d284 100644
--- a/x-pack/plugins/alerting/server/routes/unmute_all_rule.ts
+++ b/x-pack/plugins/alerting/server/routes/rule/apis/unmute_all/unmute_all_rule.ts
@@ -6,18 +6,13 @@
*/
import { IRouter } from '@kbn/core/server';
-import { schema } from '@kbn/config-schema';
-import { ILicenseState, RuleTypeDisabledError } from '../lib';
-import { verifyAccessAndContext } from './lib';
-import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types';
-
-const paramSchema = schema.object({
- id: schema.string({
- meta: {
- description: 'The identifier for the rule.',
- },
- }),
-});
+import { ILicenseState, RuleTypeDisabledError } from '../../../../lib';
+import { verifyAccessAndContext } from '../../../lib';
+import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../../../../types';
+import {
+ unmuteAllRuleRequestParamsSchemaV1,
+ UnmuteAllRuleRequestParamsV1,
+} from '../../../../../common/routes/rule/apis/unmute_all';
export const unmuteAllRuleRoute = (
router: IRouter,
@@ -33,7 +28,7 @@ export const unmuteAllRuleRoute = (
},
validate: {
request: {
- params: paramSchema,
+ params: unmuteAllRuleRequestParamsSchemaV1,
},
response: {
204: {
@@ -45,9 +40,9 @@ export const unmuteAllRuleRoute = (
router.handleLegacyErrors(
verifyAccessAndContext(licenseState, async function (context, req, res) {
const rulesClient = (await context.alerting).getRulesClient();
- const { id } = req.params;
+ const params: UnmuteAllRuleRequestParamsV1 = req.params;
try {
- await rulesClient.unmuteAll({ id });
+ await rulesClient.unmuteAll(params);
return res.noContent();
} catch (e) {
if (e instanceof RuleTypeDisabledError) {
diff --git a/x-pack/plugins/alerting/server/routes/rule_types.ts b/x-pack/plugins/alerting/server/routes/rule_types.ts
deleted file mode 100644
index afbe0c42696bd..0000000000000
--- a/x-pack/plugins/alerting/server/routes/rule_types.ts
+++ /dev/null
@@ -1,75 +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 { IRouter } from '@kbn/core/server';
-import { ILicenseState } from '../lib';
-import { RegistryAlertTypeWithAuth } from '../authorization';
-import { verifyAccessAndContext } from './lib';
-import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types';
-
-const rewriteBodyRes = (results: RegistryAlertTypeWithAuth[]) => {
- return results.map(
- ({
- enabledInLicense,
- recoveryActionGroup,
- actionGroups,
- defaultActionGroupId,
- minimumLicenseRequired,
- isExportable,
- ruleTaskTimeout,
- actionVariables,
- authorizedConsumers,
- defaultScheduleInterval,
- doesSetRecoveryContext,
- hasAlertsMappings,
- hasFieldsForAAD,
- validLegacyConsumers,
- ...rest
- }: RegistryAlertTypeWithAuth) => ({
- ...rest,
- enabled_in_license: enabledInLicense,
- recovery_action_group: recoveryActionGroup,
- action_groups: actionGroups,
- default_action_group_id: defaultActionGroupId,
- minimum_license_required: minimumLicenseRequired,
- is_exportable: isExportable,
- rule_task_timeout: ruleTaskTimeout,
- action_variables: actionVariables,
- authorized_consumers: authorizedConsumers,
- default_schedule_interval: defaultScheduleInterval,
- does_set_recovery_context: doesSetRecoveryContext,
- has_alerts_mappings: !!hasAlertsMappings,
- has_fields_for_a_a_d: !!hasFieldsForAAD,
- })
- );
-};
-
-export const ruleTypesRoute = (
- router: IRouter,
- licenseState: ILicenseState
-) => {
- router.get(
- {
- path: `${BASE_ALERTING_API_PATH}/rule_types`,
- options: {
- access: 'public',
- summary: `Get the rule types`,
- tags: ['oas-tag:alerting'],
- },
- validate: {},
- },
- router.handleLegacyErrors(
- verifyAccessAndContext(licenseState, async function (context, req, res) {
- const rulesClient = (await context.alerting).getRulesClient();
- const ruleTypes = Array.from(await rulesClient.listRuleTypes());
- return res.ok({
- body: rewriteBodyRes(ruleTypes),
- });
- })
- )
- );
-};
diff --git a/x-pack/plugins/alerting/server/rule_type_registry.ts b/x-pack/plugins/alerting/server/rule_type_registry.ts
index bc7a10d767ff0..7562942f0262d 100644
--- a/x-pack/plugins/alerting/server/rule_type_registry.ts
+++ b/x-pack/plugins/alerting/server/rule_type_registry.ts
@@ -72,6 +72,7 @@ export interface RegistryRuleType
| 'defaultScheduleInterval'
| 'doesSetRecoveryContext'
| 'fieldsForAAD'
+ | 'alerts'
> {
id: string;
enabledInLicense: boolean;
diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts
index 80f9b82733a9d..4c86469f11a29 100644
--- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts
+++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts
@@ -58,11 +58,11 @@ import { enableRule } from '../application/rule/methods/enable_rule/enable_rule'
import { updateRuleApiKey } from '../application/rule/methods/update_api_key/update_rule_api_key';
import { disableRule } from '../application/rule/methods/disable/disable_rule';
import { muteInstance } from '../application/rule/methods/mute_alert/mute_instance';
-import { muteAll } from './methods/mute_all';
-import { unmuteAll } from './methods/unmute_all';
+import { unmuteAll } from '../application/rule/methods/unmute_all';
+import { muteAll } from '../application/rule/methods/mute_all';
import { unmuteInstance } from '../application/rule/methods/unmute_alert/unmute_instance';
import { runSoon } from './methods/run_soon';
-import { listRuleTypes } from './methods/list_rule_types';
+import { listRuleTypes } from '../application/rule/methods/rule_types/rule_types';
import { getScheduleFrequency } from '../application/rule/methods/get_schedule_frequency/get_schedule_frequency';
import {
bulkUntrackAlerts,
diff --git a/x-pack/plugins/canvas/shareable_runtime/components/__snapshots__/app.test.tsx.snap b/x-pack/plugins/canvas/shareable_runtime/components/__snapshots__/app.test.tsx.snap
index 4aa379aa194bc..447771435a1dc 100644
--- a/x-pack/plugins/canvas/shareable_runtime/components/__snapshots__/app.test.tsx.snap
+++ b/x-pack/plugins/canvas/shareable_runtime/components/__snapshots__/app.test.tsx.snap
@@ -1,3 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[` App renders properly 1`] = `"
markdown mock
markdown mock
Page level controls
My Canvas Workpad
There is a new region landmark with page level controls at the end of the document.
"`;
+exports[` App renders properly 1`] = `"
markdown mock
markdown mock
Page level controls
My Canvas Workpad
There is a new region landmark with page level controls at the end of the document.
"`;
diff --git a/x-pack/plugins/cases/public/common/test_utils.tsx b/x-pack/plugins/cases/public/common/test_utils.tsx
index 0028d79019f2a..1cbf5e2a5d454 100644
--- a/x-pack/plugins/cases/public/common/test_utils.tsx
+++ b/x-pack/plugins/cases/public/common/test_utils.tsx
@@ -18,7 +18,7 @@ import { EuiButton } from '@elastic/eui';
* Convenience utility to remove text appended to links by EUI
*/
export const removeExternalLinkText = (str: string | null) =>
- str?.replace(/\(opens in a new tab or window\)/g, '');
+ str?.replace(/\(external[^)]*\)/g, '');
export async function waitForComponentToPaint