From b2371c9093061760620923e3aa2ef82a68ee700b Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Wed, 30 Aug 2023 14:45:00 +0200 Subject: [PATCH 01/53] [Security Solutions] Fix The 'Show top N' action inside the timeline (#165109) issue: https://github.com/elastic/kibana/issues/165075 ## Summary The "Show top N" action inside the timeline doesn't take the timeline filter and query into consideration ### Solutions Pass the `combinedQuery` created by the `top_n` component as a filter to Lens and disable `applyGlobalQueriesAndFilters` property (only for `top_n` component). --- .../components/matrix_histogram/index.tsx | 19 +++++++++++++++++-- .../public/common/components/top_n/top_n.tsx | 1 + .../lens_attributes/common/events.ts | 5 +++-- .../components/events_by_dataset/index.tsx | 2 ++ 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx index c2d1e6dc59ff8..e14a0c7f4c224 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx @@ -12,6 +12,7 @@ import styled from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, EuiProgress, EuiSelect, EuiSpacer } from '@elastic/eui'; import { useDispatch } from 'react-redux'; import type { AggregationsTermsAggregateBase } from '@elastic/elasticsearch/lib/api/types'; +import { isString } from 'lodash/fp'; import * as i18n from './translations'; import { HeaderSection } from '../header_section'; import { Panel } from '../panel'; @@ -66,6 +67,7 @@ export type MatrixHistogramComponentProps = MatrixHistogramProps & scopeId?: string; title: string | GetTitle; hideQueryToggle?: boolean; + applyGlobalQueriesAndFilters?: boolean; }; const DEFAULT_PANEL_HEIGHT = 300; @@ -117,6 +119,7 @@ export const MatrixHistogramComponent: React.FC = yTickFormatter, skip, hideQueryToggle = false, + applyGlobalQueriesAndFilters = true, }) => { const visualizationId = `${id}-embeddable`; const dispatch = useDispatch(); @@ -258,9 +261,20 @@ export const MatrixHistogramComponent: React.FC = const timerange = useMemo(() => ({ from: startDate, to: endDate }), [startDate, endDate]); const extraVisualizationOptions = useMemo( - () => ({ dnsIsPtrIncluded: isPtrIncluded ?? false }), - [isPtrIncluded] + () => ({ + dnsIsPtrIncluded: isPtrIncluded ?? false, + filters: filterQuery + ? [ + { + query: isString(filterQuery) ? JSON.parse(filterQuery) : filterQuery, + meta: {}, + }, + ] + : undefined, + }), + [isPtrIncluded, filterQuery] ); + if (hideHistogram) { return null; } @@ -329,6 +343,7 @@ export const MatrixHistogramComponent: React.FC = {toggleStatus ? ( isChartEmbeddablesEnabled ? ( = ({ {view === 'raw' || view === 'all' ? ( { return { title: 'Events', @@ -55,7 +56,7 @@ export const getEventsHistogramLensAttributes: GetLensAttributes = ( query: '', language: 'kuery', }, - filters: [], + filters: extraOptions.filters ?? [], datasourceStates: { formBased: { layers: { diff --git a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx index 360cfbbce9825..360c95ec94019 100644 --- a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx @@ -60,6 +60,7 @@ interface Props extends Pick void; hideQueryToggle?: boolean; + applyGlobalQueriesAndFilters?: boolean; } const getHistogramOption = (fieldName: string): MatrixHistogramOption => ({ @@ -95,6 +96,7 @@ const EventsByDatasetComponent: React.FC = ({ to, toggleTopN, hideQueryToggle = false, + applyGlobalQueriesAndFilters = true, }) => { const uniqueQueryId = useMemo(() => `${ID}-${queryType}`, [queryType]); From 4509cbeaa3b0540cd937549f20d72b5f574dd28a Mon Sep 17 00:00:00 2001 From: Shahzad Date: Wed, 30 Aug 2023 14:48:40 +0200 Subject: [PATCH 02/53] [Synthetics] Parse response from agent policies API in route (#165206) --- .../common/types/synthetics_monitor.ts | 8 +++++++ .../getting_started_page.test.tsx | 14 +++---------- .../private_locations/location_form.tsx | 4 ++-- .../private_locations/manage_empty_state.tsx | 2 +- .../manage_private_locations.test.tsx | 21 +++---------------- .../private_locations/policy_hosts.tsx | 2 +- .../private_locations/policy_name.tsx | 2 +- .../state/private_locations/actions.ts | 4 ++-- .../synthetics/state/private_locations/api.ts | 4 ++-- .../state/private_locations/index.ts | 11 ++-------- .../private_locations/get_agent_policies.ts | 13 ++++++++++-- .../get_private_locations.ts | 4 ++-- .../settings/private_locations/helpers.ts | 4 ++-- .../synthetics_private_location.ts | 4 +--- 14 files changed, 41 insertions(+), 56 deletions(-) diff --git a/x-pack/plugins/synthetics/common/types/synthetics_monitor.ts b/x-pack/plugins/synthetics/common/types/synthetics_monitor.ts index 65d466a5ab2f8..47b0eaf9d143c 100644 --- a/x-pack/plugins/synthetics/common/types/synthetics_monitor.ts +++ b/x-pack/plugins/synthetics/common/types/synthetics_monitor.ts @@ -20,3 +20,11 @@ export interface TestNowResponse { configId: string; monitor: SyntheticsMonitor; } + +export interface AgentPolicyInfo { + id: string; + name: string; + agents: number; + status: string; + description?: string; +} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/getting_started/getting_started_page.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/getting_started/getting_started_page.test.tsx index d66a079d8a2ea..caa3a2d66fcfe 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/getting_started/getting_started_page.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/getting_started/getting_started_page.test.tsx @@ -82,9 +82,7 @@ describe('GettingStartedPage', () => { loading: false, }, agentPolicies: { - data: { - total: 0, - }, + data: [], isAddingNewPrivateLocation: true, }, }, @@ -109,10 +107,7 @@ describe('GettingStartedPage', () => { loading: false, }, agentPolicies: { - data: { - total: 1, - items: [{}], - }, + data: [{}], isAddingNewPrivateLocation: true, }, }, @@ -141,10 +136,7 @@ describe('GettingStartedPage', () => { loading: false, }, agentPolicies: { - data: { - total: 1, - items: [{}], - }, + data: [{}], isAddingNewPrivateLocation: true, }, }, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/location_form.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/location_form.tsx index cc2b835fbfdd8..7a8fdd67c6efd 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/location_form.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/location_form.tsx @@ -29,7 +29,7 @@ export const LocationForm = ({ privateLocations }: { privateLocations: PrivateLo const { control, register, watch } = useFormContext(); const { errors } = useFormState(); const selectedPolicyId = watch('agentPolicyId'); - const selectedPolicy = data?.items.find((item) => item.id === selectedPolicyId); + const selectedPolicy = data?.find((item) => item.id === selectedPolicyId); const tagsList = privateLocations.reduce((acc, item) => { const tags = item.tags || []; @@ -38,7 +38,7 @@ export const LocationForm = ({ privateLocations }: { privateLocations: PrivateLo return ( <> - {data?.items.length === 0 && } + {data?.length === 0 && } { const { data: agentPolicies } = useSelector(selectAgentPolicies); - if (agentPolicies?.total === 0 && showNeedAgentPolicy) { + if (agentPolicies?.length === 0 && showNeedAgentPolicy) { return ; } diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/manage_private_locations.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/manage_private_locations.test.tsx index 818b398315610..41086bacde0be 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/manage_private_locations.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/manage_private_locations.test.tsx @@ -46,12 +46,7 @@ describe('', () => { const { getByText, getByRole, findByText } = render(, { state: { agentPolicies: { - data: { - items: [], - total: 0, - page: 1, - perPage: 20, - }, + data: [], loading: false, error: null, isManageFlyoutOpen: false, @@ -85,12 +80,7 @@ describe('', () => { const { getByText, getByRole, findByText } = render(, { state: { agentPolicies: { - data: { - items: [{}], - total: 1, - page: 1, - perPage: 20, - }, + data: [{}], loading: false, error: null, isManageFlyoutOpen: false, @@ -140,12 +130,7 @@ describe('', () => { const { getByText, getByRole, findByText } = render(, { state: { agentPolicies: { - data: { - items: [{}], - total: 1, - page: 1, - perPage: 20, - }, + data: [{}], loading: false, error: null, isManageFlyoutOpen: false, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/policy_hosts.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/policy_hosts.tsx index e91a8b529131c..483e80fae65b7 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/policy_hosts.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/policy_hosts.tsx @@ -34,7 +34,7 @@ export const PolicyHostsField = ({ }) => { const { data } = useSelector(selectAgentPolicies); - const policyHostsOptions = data?.items.map((item) => { + const policyHostsOptions = data?.map((item) => { const hasLocation = privateLocations.find((location) => location.agentPolicyId === item.id); return { disabled: Boolean(hasLocation), diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/policy_name.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/policy_name.tsx index f336d83a98ee7..ea4ac5b75688b 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/policy_name.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/policy_name.tsx @@ -20,7 +20,7 @@ export const PolicyName = ({ agentPolicyId }: { agentPolicyId: string }) => { const { data: policies, loading } = useSelector(selectAgentPolicies); - const policy = policies?.items.find((policyT) => policyT.id === agentPolicyId); + const policy = policies?.find((policyT) => policyT.id === agentPolicyId); if (loading) { return ; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/actions.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/actions.ts index 60586a99c380e..05fc1e950c112 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/actions.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/actions.ts @@ -6,10 +6,10 @@ */ import { createAction } from '@reduxjs/toolkit'; +import { AgentPolicyInfo } from '../../../../../common/types'; import { createAsyncAction } from '../utils/actions'; -import { AgentPoliciesList } from '.'; -export const getAgentPoliciesAction = createAsyncAction( +export const getAgentPoliciesAction = createAsyncAction( '[AGENT POLICIES] GET' ); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/api.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/api.ts index c3baa6f21ddc5..805d5672ae9bd 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/api.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/api.ts @@ -5,12 +5,12 @@ * 2.0. */ +import { AgentPolicyInfo } from '../../../../../common/types'; import { SYNTHETICS_API_URLS } from '../../../../../common/constants'; import { PrivateLocation, SyntheticsPrivateLocations } from '../../../../../common/runtime_types'; import { apiService } from '../../../../utils/api_service/api_service'; -import { AgentPoliciesList } from '.'; -export const fetchAgentPolicies = async (): Promise => { +export const fetchAgentPolicies = async (): Promise => { return await apiService.get(SYNTHETICS_API_URLS.AGENT_POLICIES); }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/index.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/index.ts index 5b023a8bd0b55..d38c6b6702660 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/index.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/index.ts @@ -6,19 +6,12 @@ */ import { createReducer } from '@reduxjs/toolkit'; -import { AgentPolicy } from '@kbn/fleet-plugin/common'; +import { AgentPolicyInfo } from '../../../../../common/types'; import { IHttpSerializedFetchError } from '..'; import { getAgentPoliciesAction, setAddingNewPrivateLocation } from './actions'; -export interface AgentPoliciesList { - items: AgentPolicy[]; - total: number; - page: number; - perPage: number; -} - export interface AgentPoliciesState { - data: AgentPoliciesList | null; + data: AgentPolicyInfo[] | null; loading: boolean; error: IHttpSerializedFetchError | null; isManageFlyoutOpen?: boolean; diff --git a/x-pack/plugins/synthetics/server/routes/settings/private_locations/get_agent_policies.ts b/x-pack/plugins/synthetics/server/routes/settings/private_locations/get_agent_policies.ts index 204731474ca71..668beba0a8f95 100644 --- a/x-pack/plugins/synthetics/server/routes/settings/private_locations/get_agent_policies.ts +++ b/x-pack/plugins/synthetics/server/routes/settings/private_locations/get_agent_policies.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { AgentPolicyInfo } from '../../../../common/types'; import { SyntheticsServerSetup } from '../../../types'; import { SyntheticsRestApiRouteFactory } from '../../types'; import { SYNTHETICS_API_URLS } from '../../../../common/constants'; @@ -13,7 +14,7 @@ export const getAgentPoliciesRoute: SyntheticsRestApiRouteFactory = () => ({ method: 'GET', path: SYNTHETICS_API_URLS.AGENT_POLICIES, validate: {}, - handler: async ({ server, context, uptimeEsClient }): Promise => { + handler: async ({ server }): Promise => { return getAgentPoliciesAsInternalUser(server); }, }); @@ -22,7 +23,7 @@ export const getAgentPoliciesAsInternalUser = async (server: SyntheticsServerSet const soClient = server.coreStart.savedObjects.createInternalRepository(); const esClient = server.coreStart.elasticsearch.client.asInternalUser; - return server.fleet?.agentPolicyService.list(soClient, { + const agentPolicies = await server.fleet?.agentPolicyService.list(soClient, { page: 1, perPage: 10000, sortField: 'name', @@ -31,4 +32,12 @@ export const getAgentPoliciesAsInternalUser = async (server: SyntheticsServerSet esClient, withAgentCount: true, }); + + return agentPolicies.items.map((agentPolicy) => ({ + id: agentPolicy.id, + name: agentPolicy.name, + agents: agentPolicy.agents ?? 0, + status: agentPolicy.status, + description: agentPolicy.description, + })); }; diff --git a/x-pack/plugins/synthetics/server/routes/settings/private_locations/get_private_locations.ts b/x-pack/plugins/synthetics/server/routes/settings/private_locations/get_private_locations.ts index 3a8e8a043d8c8..44a31bad08cff 100644 --- a/x-pack/plugins/synthetics/server/routes/settings/private_locations/get_private_locations.ts +++ b/x-pack/plugins/synthetics/server/routes/settings/private_locations/get_private_locations.ts @@ -4,9 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { AgentPolicy } from '@kbn/fleet-plugin/common'; import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; +import { AgentPolicyInfo } from '../../../../common/types'; import { SyntheticsRestApiRouteFactory } from '../../types'; import { SyntheticsPrivateLocations } from '../../../../common/runtime_types'; import { SYNTHETICS_API_URLS } from '../../../../common/constants'; @@ -33,7 +33,7 @@ export const getPrivateLocationsRoute: SyntheticsRestApiRouteFactory< export const getPrivateLocationsAndAgentPolicies = async ( savedObjectsClient: SavedObjectsClientContract, syntheticsMonitorClient: SyntheticsMonitorClient -): Promise => { +): Promise => { try { const [privateLocations, agentPolicies] = await Promise.all([ getPrivateLocations(savedObjectsClient), diff --git a/x-pack/plugins/synthetics/server/routes/settings/private_locations/helpers.ts b/x-pack/plugins/synthetics/server/routes/settings/private_locations/helpers.ts index 7f64b1bed7425..b41c8f5e7538d 100644 --- a/x-pack/plugins/synthetics/server/routes/settings/private_locations/helpers.ts +++ b/x-pack/plugins/synthetics/server/routes/settings/private_locations/helpers.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { AgentPolicy } from '@kbn/fleet-plugin/common'; +import { AgentPolicyInfo } from '../../../../common/types'; import type { SyntheticsPrivateLocations } from '../../../../common/runtime_types'; import type { SyntheticsPrivateLocationsAttributes, @@ -14,7 +14,7 @@ import { PrivateLocation } from '../../../../common/runtime_types'; export const toClientContract = ( attributes: SyntheticsPrivateLocationsAttributes, - agentPolicies?: AgentPolicy[] + agentPolicies?: AgentPolicyInfo[] ): SyntheticsPrivateLocations => { return { locations: attributes.locations.map((location) => ({ diff --git a/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.ts b/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.ts index a1d22b3404f03..69788a6f10ae0 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.ts @@ -435,9 +435,7 @@ export class SyntheticsPrivateLocation { } async getAgentPolicies() { - const agentPolicies = await getAgentPoliciesAsInternalUser(this.server); - - return agentPolicies.items; + return await getAgentPoliciesAsInternalUser(this.server); } } From f35ecc6f962c50ecff65c76b52fdad12eccdd930 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Wed, 30 Aug 2023 08:50:00 -0400 Subject: [PATCH 03/53] [Response Ops][Actions] Prepare connector `listTypes` HTTP API for versioning (#162830) Towards https://github.com/elastic/response-ops-team/issues/125 ## Summary Preparing the `GET ${BASE_ACTION_API_PATH}/connector_types` HTTP API for versioning --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../connector/apis/connector_types/index.ts | 12 + .../apis/connector_types/schemas/latest.ts | 8 + .../apis/connector_types/schemas/v1.ts | 12 + .../apis/connector_types/types/latest.ts | 8 + .../apis/connector_types/types/v1.ts | 11 + .../common/routes/connector/response/index.ts | 3 + .../connector/response/schemas/latest.ts | 1 + .../routes/connector/response/schemas/v1.ts | 18 ++ .../routes/connector/response/types/v1.ts | 14 +- .../actions_client.mock.ts | 0 .../actions_client/actions_client.test.ts | 166 ------------ .../server/actions_client/actions_client.ts | 26 +- .../connector/methods/list_types/index.ts | 8 + .../methods/list_types/list_types.test.ts | 236 ++++++++++++++++++ .../methods/list_types/list_types.ts | 33 +++ .../methods/list_types/schemas/index.ts | 8 + .../schemas/list_types_params_schema.ts | 13 + .../methods/list_types/types/index.ts | 8 + .../list_types/types/list_types_params.ts | 16 ++ .../schemas/connector_type_schema.ts | 26 ++ .../application/connector/schemas/index.ts | 1 + .../connector/types/connector_type.ts | 22 ++ .../application/connector/types/index.ts | 1 + x-pack/plugins/actions/server/mocks.ts | 2 +- x-pack/plugins/actions/server/plugin.ts | 2 +- .../routes/connector/get_all/get_all.test.ts | 2 +- .../routes/connector/list_types/index.ts | 8 + .../list_types/list_types.test.ts} | 22 +- .../routes/connector/list_types/list_types.ts | 47 ++++ .../connector/list_types/transforms/index.ts | 9 + .../transform_list_types_response/latest.ts | 8 + .../transform_list_types_response/v1.ts | 35 +++ .../actions/server/routes/connector_types.ts | 59 ----- .../actions/server/routes/create.test.ts | 2 +- .../actions/server/routes/execute.test.ts | 2 +- .../plugins/actions/server/routes/get.test.ts | 2 +- .../routes/get_global_execution_kpi.test.ts | 2 +- .../routes/get_global_execution_logs.test.ts | 2 +- .../routes/get_oauth_access_token.test.ts | 2 +- x-pack/plugins/actions/server/routes/index.ts | 4 +- .../routes/legacy/_mock_handler_arguments.ts | 7 +- .../server/routes/legacy/create.test.ts | 2 +- .../server/routes/legacy/execute.test.ts | 2 +- .../actions/server/routes/legacy/get.test.ts | 2 +- .../server/routes/legacy/get_all.test.ts | 2 +- .../server/routes/legacy/update.test.ts | 2 +- .../actions/server/routes/update.test.ts | 2 +- .../routes/verify_access_and_context.test.ts | 2 +- x-pack/plugins/cases/server/client/mocks.ts | 2 +- .../routes/__mocks__/request_context.ts | 2 +- .../logic/export/get_export_all.test.ts | 2 +- .../export/get_export_by_object_ids.test.ts | 2 +- .../import_rule_action_connectors.test.ts | 2 +- 53 files changed, 609 insertions(+), 283 deletions(-) create mode 100644 x-pack/plugins/actions/common/routes/connector/apis/connector_types/index.ts create mode 100644 x-pack/plugins/actions/common/routes/connector/apis/connector_types/schemas/latest.ts create mode 100644 x-pack/plugins/actions/common/routes/connector/apis/connector_types/schemas/v1.ts create mode 100644 x-pack/plugins/actions/common/routes/connector/apis/connector_types/types/latest.ts create mode 100644 x-pack/plugins/actions/common/routes/connector/apis/connector_types/types/v1.ts rename x-pack/plugins/actions/server/{ => actions_client}/actions_client.mock.ts (100%) create mode 100644 x-pack/plugins/actions/server/application/connector/methods/list_types/index.ts create mode 100644 x-pack/plugins/actions/server/application/connector/methods/list_types/list_types.test.ts create mode 100644 x-pack/plugins/actions/server/application/connector/methods/list_types/list_types.ts create mode 100644 x-pack/plugins/actions/server/application/connector/methods/list_types/schemas/index.ts create mode 100644 x-pack/plugins/actions/server/application/connector/methods/list_types/schemas/list_types_params_schema.ts create mode 100644 x-pack/plugins/actions/server/application/connector/methods/list_types/types/index.ts create mode 100644 x-pack/plugins/actions/server/application/connector/methods/list_types/types/list_types_params.ts create mode 100644 x-pack/plugins/actions/server/application/connector/schemas/connector_type_schema.ts create mode 100644 x-pack/plugins/actions/server/application/connector/types/connector_type.ts create mode 100644 x-pack/plugins/actions/server/routes/connector/list_types/index.ts rename x-pack/plugins/actions/server/routes/{connector_types.test.ts => connector/list_types/list_types.test.ts} (91%) create mode 100644 x-pack/plugins/actions/server/routes/connector/list_types/list_types.ts create mode 100644 x-pack/plugins/actions/server/routes/connector/list_types/transforms/index.ts create mode 100644 x-pack/plugins/actions/server/routes/connector/list_types/transforms/transform_list_types_response/latest.ts create mode 100644 x-pack/plugins/actions/server/routes/connector/list_types/transforms/transform_list_types_response/v1.ts delete mode 100644 x-pack/plugins/actions/server/routes/connector_types.ts diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connector_types/index.ts b/x-pack/plugins/actions/common/routes/connector/apis/connector_types/index.ts new file mode 100644 index 0000000000000..b4d93fc1be5e6 --- /dev/null +++ b/x-pack/plugins/actions/common/routes/connector/apis/connector_types/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 { connectorTypesQuerySchema } from './schemas/latest'; +export type { ConnectorTypesRequestQuery } from './types/latest'; + +export { connectorTypesQuerySchema as connectorTypesQuerySchemaV1 } from './schemas/v1'; +export type { ConnectorTypesRequestQuery as ConnectorTypesRequestQueryV1 } from './types/v1'; diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connector_types/schemas/latest.ts b/x-pack/plugins/actions/common/routes/connector/apis/connector_types/schemas/latest.ts new file mode 100644 index 0000000000000..25300c97a6d2e --- /dev/null +++ b/x-pack/plugins/actions/common/routes/connector/apis/connector_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/actions/common/routes/connector/apis/connector_types/schemas/v1.ts b/x-pack/plugins/actions/common/routes/connector/apis/connector_types/schemas/v1.ts new file mode 100644 index 0000000000000..bdbc01efc4b7a --- /dev/null +++ b/x-pack/plugins/actions/common/routes/connector/apis/connector_types/schemas/v1.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 connectorTypesQuerySchema = schema.object({ + feature_id: schema.maybe(schema.string()), +}); diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connector_types/types/latest.ts b/x-pack/plugins/actions/common/routes/connector/apis/connector_types/types/latest.ts new file mode 100644 index 0000000000000..25300c97a6d2e --- /dev/null +++ b/x-pack/plugins/actions/common/routes/connector/apis/connector_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/actions/common/routes/connector/apis/connector_types/types/v1.ts b/x-pack/plugins/actions/common/routes/connector/apis/connector_types/types/v1.ts new file mode 100644 index 0000000000000..cef43f8b41b18 --- /dev/null +++ b/x-pack/plugins/actions/common/routes/connector/apis/connector_types/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 { connectorTypesQuerySchemaV1 } from '..'; + +export type ConnectorTypesRequestQuery = TypeOf; diff --git a/x-pack/plugins/actions/common/routes/connector/response/index.ts b/x-pack/plugins/actions/common/routes/connector/response/index.ts index cfc4d4a4a0098..99d08664c96a4 100644 --- a/x-pack/plugins/actions/common/routes/connector/response/index.ts +++ b/x-pack/plugins/actions/common/routes/connector/response/index.ts @@ -8,6 +8,7 @@ // Latest export type { ConnectorResponse, ActionTypeConfig } from './types/latest'; export { connectorResponseSchema } from './schemas/latest'; +export { connectorTypesResponseSchema } from './schemas/latest'; // v1 export type { @@ -15,3 +16,5 @@ export type { ActionTypeConfig as ActionTypeConfigV1, } from './types/v1'; export { connectorResponseSchema as connectorResponseSchemaV1 } from './schemas/v1'; +export type { ConnectorTypesResponse as ConnectorTypesResponseV1 } from './types/v1'; +export { connectorTypesResponseSchema as connectorTypesResponseSchemaV1 } from './schemas/v1'; diff --git a/x-pack/plugins/actions/common/routes/connector/response/schemas/latest.ts b/x-pack/plugins/actions/common/routes/connector/response/schemas/latest.ts index 96d490935d339..dbb5659baf8c4 100644 --- a/x-pack/plugins/actions/common/routes/connector/response/schemas/latest.ts +++ b/x-pack/plugins/actions/common/routes/connector/response/schemas/latest.ts @@ -6,3 +6,4 @@ */ export { connectorResponseSchema } from './v1'; +export { connectorTypesResponseSchema } from './v1'; diff --git a/x-pack/plugins/actions/common/routes/connector/response/schemas/v1.ts b/x-pack/plugins/actions/common/routes/connector/response/schemas/v1.ts index 6726aa740cf87..4f8be5b0f344f 100644 --- a/x-pack/plugins/actions/common/routes/connector/response/schemas/v1.ts +++ b/x-pack/plugins/actions/common/routes/connector/response/schemas/v1.ts @@ -18,3 +18,21 @@ export const connectorResponseSchema = schema.object({ is_system_action: schema.boolean(), referenced_by_count: schema.number(), }); + +export const connectorTypesResponseSchema = schema.object({ + id: schema.string(), + name: schema.string(), + enabled: schema.boolean(), + enabled_in_config: schema.boolean(), + enabled_in_license: schema.boolean(), + minimum_license_required: schema.oneOf([ + schema.literal('basic'), + schema.literal('standard'), + schema.literal('gold'), + schema.literal('platinum'), + schema.literal('enterprise'), + schema.literal('trial'), + ]), + supported_feature_ids: schema.arrayOf(schema.string()), + is_system_action_type: schema.boolean(), +}); diff --git a/x-pack/plugins/actions/common/routes/connector/response/types/v1.ts b/x-pack/plugins/actions/common/routes/connector/response/types/v1.ts index 9f49c048f92bf..5aed107c6f4f6 100644 --- a/x-pack/plugins/actions/common/routes/connector/response/types/v1.ts +++ b/x-pack/plugins/actions/common/routes/connector/response/types/v1.ts @@ -6,7 +6,7 @@ */ import { TypeOf } from '@kbn/config-schema'; -import { connectorResponseSchemaV1 } from '..'; +import { connectorResponseSchemaV1, connectorTypesResponseSchemaV1 } from '..'; export type ActionTypeConfig = Record; type ConnectorResponseSchemaType = TypeOf; @@ -22,3 +22,15 @@ export interface ConnectorResponse; +export interface ConnectorTypesResponse { + id: ConnectorTypesResponseSchemaType['id']; + name: ConnectorTypesResponseSchemaType['name']; + enabled: ConnectorTypesResponseSchemaType['enabled']; + enabled_in_config: ConnectorTypesResponseSchemaType['enabled_in_config']; + enabled_in_license: ConnectorTypesResponseSchemaType['enabled_in_license']; + minimum_license_required: ConnectorTypesResponseSchemaType['minimum_license_required']; + supported_feature_ids: ConnectorTypesResponseSchemaType['supported_feature_ids']; + is_system_action_type: ConnectorTypesResponseSchemaType['is_system_action_type']; +} diff --git a/x-pack/plugins/actions/server/actions_client.mock.ts b/x-pack/plugins/actions/server/actions_client/actions_client.mock.ts similarity index 100% rename from x-pack/plugins/actions/server/actions_client.mock.ts rename to x-pack/plugins/actions/server/actions_client/actions_client.mock.ts diff --git a/x-pack/plugins/actions/server/actions_client/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client/actions_client.test.ts index 94adb4a6205c2..96e9168a68d51 100644 --- a/x-pack/plugins/actions/server/actions_client/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client/actions_client.test.ts @@ -3233,172 +3233,6 @@ describe('bulkEnqueueExecution()', () => { }); }); -describe('listType()', () => { - it('filters action types by feature ID', async () => { - mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); - - actionTypeRegistry.register({ - id: 'my-action-type', - name: 'My action type', - minimumLicenseRequired: 'basic', - supportedFeatureIds: ['alerting'], - validate: { - config: { schema: schema.object({}) }, - secrets: { schema: schema.object({}) }, - params: { schema: schema.object({}) }, - }, - executor, - }); - - actionTypeRegistry.register({ - id: 'my-action-type-2', - name: 'My action type 2', - minimumLicenseRequired: 'basic', - supportedFeatureIds: ['cases'], - validate: { - config: { schema: schema.object({}) }, - secrets: { schema: schema.object({}) }, - params: { schema: schema.object({}) }, - }, - executor, - }); - - expect(await actionsClient.listTypes({ featureId: 'alerting' })).toEqual([ - { - id: 'my-action-type', - name: 'My action type', - minimumLicenseRequired: 'basic', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - supportedFeatureIds: ['alerting'], - isSystemActionType: false, - }, - ]); - }); - - it('filters out system action types when not defining options', async () => { - mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); - - actionTypeRegistry.register({ - id: 'my-action-type', - name: 'My action type', - minimumLicenseRequired: 'basic', - supportedFeatureIds: ['alerting'], - validate: { - config: { schema: schema.object({}) }, - secrets: { schema: schema.object({}) }, - params: { schema: schema.object({}) }, - }, - executor, - }); - - actionTypeRegistry.register({ - id: 'my-action-type-2', - name: 'My action type 2', - minimumLicenseRequired: 'basic', - supportedFeatureIds: ['cases'], - validate: { - config: { schema: schema.object({}) }, - secrets: { schema: schema.object({}) }, - params: { schema: schema.object({}) }, - }, - executor, - }); - - actionTypeRegistry.register({ - id: '.cases', - name: 'Cases', - minimumLicenseRequired: 'platinum', - supportedFeatureIds: ['alerting'], - validate: { - config: { schema: schema.object({}) }, - secrets: { schema: schema.object({}) }, - params: { schema: schema.object({}) }, - }, - isSystemActionType: true, - executor, - }); - - expect(await actionsClient.listTypes()).toEqual([ - { - id: 'my-action-type', - name: 'My action type', - minimumLicenseRequired: 'basic', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - supportedFeatureIds: ['alerting'], - isSystemActionType: false, - }, - { - id: 'my-action-type-2', - name: 'My action type 2', - isSystemActionType: false, - minimumLicenseRequired: 'basic', - supportedFeatureIds: ['cases'], - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - }, - ]); - }); - - it('return system action types when defining options', async () => { - mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); - - actionTypeRegistry.register({ - id: 'my-action-type', - name: 'My action type', - minimumLicenseRequired: 'basic', - supportedFeatureIds: ['alerting'], - validate: { - config: { schema: schema.object({}) }, - secrets: { schema: schema.object({}) }, - params: { schema: schema.object({}) }, - }, - executor, - }); - - actionTypeRegistry.register({ - id: '.cases', - name: 'Cases', - minimumLicenseRequired: 'platinum', - supportedFeatureIds: ['alerting'], - validate: { - config: { schema: schema.object({}) }, - secrets: { schema: schema.object({}) }, - params: { schema: schema.object({}) }, - }, - isSystemActionType: true, - executor, - }); - - expect(await actionsClient.listTypes({ includeSystemActionTypes: true })).toEqual([ - { - id: 'my-action-type', - name: 'My action type', - minimumLicenseRequired: 'basic', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - supportedFeatureIds: ['alerting'], - isSystemActionType: false, - }, - { - id: '.cases', - name: 'Cases', - isSystemActionType: true, - minimumLicenseRequired: 'platinum', - supportedFeatureIds: ['alerting'], - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - }, - ]); - }); -}); - describe('isActionTypeEnabled()', () => { const fooActionType: ActionType = { id: 'foo', diff --git a/x-pack/plugins/actions/server/actions_client/actions_client.ts b/x-pack/plugins/actions/server/actions_client/actions_client.ts index 9fbf7c988c8f0..6b4ca1ef9fabc 100644 --- a/x-pack/plugins/actions/server/actions_client/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client/actions_client.ts @@ -25,9 +25,10 @@ import { RunNowResult } from '@kbn/task-manager-plugin/server'; import { IEventLogClient } from '@kbn/event-log-plugin/server'; import { KueryNode } from '@kbn/es-query'; import { FindConnectorResult } from '../application/connector/types'; +import { ConnectorType } from '../application/connector/types'; import { getAll } from '../application/connector/methods/get_all'; +import { listTypes } from '../application/connector/methods/list_types'; import { - ActionType, GetGlobalExecutionKPIParams, GetGlobalExecutionLogParams, IExecutionLogResult, @@ -48,7 +49,6 @@ import { ActionTypeExecutorResult, ConnectorTokenClientContract, } from '../types'; - import { PreconfiguredActionDisabledModificationError } from '../lib/errors/preconfigured_action_disabled_modification'; import { ExecuteOptions } from '../lib/action_executor'; import { @@ -88,6 +88,7 @@ import { getExecutionLogAggregation, } from '../lib/get_execution_log_aggregation'; import { connectorFromSavedObject, isConnectorDeprecated } from '../application/connector/lib'; +import { ListTypesParams } from '../application/connector/methods/list_types/types'; interface ActionUpdate { name: string; @@ -104,7 +105,7 @@ export interface CreateOptions { options?: { id?: string }; } -interface ConstructorOptions { +export interface ConstructorOptions { logger: Logger; kibanaIndices: string[]; scopedClusterClient: IScopedClusterClient; @@ -128,11 +129,6 @@ export interface UpdateOptions { action: ActionUpdate; } -interface ListTypesOptions { - featureId?: string; - includeSystemActionTypes?: boolean; -} - export interface ActionsClientContext { logger: Logger; kibanaIndices: string[]; @@ -839,21 +835,11 @@ export class ActionsClient { ); } - /** - * Return all available action types - * expect system action types - */ public async listTypes({ featureId, includeSystemActionTypes = false, - }: ListTypesOptions = {}): Promise { - const actionTypes = this.context.actionTypeRegistry.list(featureId); - - const filteredActionTypes = includeSystemActionTypes - ? actionTypes - : actionTypes.filter((actionType) => !Boolean(actionType.isSystemActionType)); - - return filteredActionTypes; + }: ListTypesParams = {}): Promise { + return listTypes(this.context, { featureId, includeSystemActionTypes }); } public isActionTypeEnabled( diff --git a/x-pack/plugins/actions/server/application/connector/methods/list_types/index.ts b/x-pack/plugins/actions/server/application/connector/methods/list_types/index.ts new file mode 100644 index 0000000000000..e46209cd26fba --- /dev/null +++ b/x-pack/plugins/actions/server/application/connector/methods/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 { listTypes } from './list_types'; diff --git a/x-pack/plugins/actions/server/application/connector/methods/list_types/list_types.test.ts b/x-pack/plugins/actions/server/application/connector/methods/list_types/list_types.test.ts new file mode 100644 index 0000000000000..d8e273f232258 --- /dev/null +++ b/x-pack/plugins/actions/server/application/connector/methods/list_types/list_types.test.ts @@ -0,0 +1,236 @@ +/* + * 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 { actionsConfigMock } from '../../../../actions_config.mock'; +import { ActionTypeRegistry, ActionTypeRegistryOpts } from '../../../../action_type_registry'; +import { ActionsAuthorization } from '../../../../authorization/actions_authorization'; +import { ActionExecutor, ILicenseState, TaskRunnerFactory } from '../../../../lib'; +import { actionExecutorMock } from '../../../../lib/action_executor.mock'; +import { connectorTokenClientMock } from '../../../../lib/connector_token_client.mock'; +import { licenseStateMock } from '../../../../lib/license_state.mock'; +import { actionsAuthorizationMock } from '../../../../mocks'; +import { inMemoryMetricsMock } from '../../../../monitoring/in_memory_metrics.mock'; +import { schema } from '@kbn/config-schema'; +import { + httpServerMock, + loggingSystemMock, + elasticsearchServiceMock, + savedObjectsClientMock, +} from '@kbn/core/server/mocks'; +import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; +import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; +import { ActionsClient } from '../../../../actions_client/actions_client'; +import { ExecutorType } from '../../../../types'; + +let mockedLicenseState: jest.Mocked; +let actionTypeRegistryParams: ActionTypeRegistryOpts; +let actionTypeRegistry: ActionTypeRegistry; + +const executor: ExecutorType<{}, {}, {}, void> = async (options) => { + return { status: 'ok', actionId: options.actionId }; +}; + +describe('listTypes()', () => { + let actionsClient: ActionsClient; + + beforeEach(async () => { + jest.resetAllMocks(); + mockedLicenseState = licenseStateMock.create(); + actionTypeRegistryParams = { + licensing: licensingMock.createSetup(), + taskManager: taskManagerMock.createSetup(), + taskRunnerFactory: new TaskRunnerFactory( + new ActionExecutor({ isESOCanEncrypt: true }), + inMemoryMetricsMock.create() + ), + actionsConfigUtils: actionsConfigMock.create(), + licenseState: mockedLicenseState, + inMemoryConnectors: [], + }; + actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); + actionsClient = new ActionsClient({ + logger: loggingSystemMock.create().get(), + kibanaIndices: ['.kibana'], + scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), + actionTypeRegistry, + unsecuredSavedObjectsClient: savedObjectsClientMock.create(), + inMemoryConnectors: [], + actionExecutor: actionExecutorMock.create(), + executionEnqueuer: jest.fn(), + ephemeralExecutionEnqueuer: jest.fn(), + bulkExecutionEnqueuer: jest.fn(), + request: httpServerMock.createKibanaRequest(), + authorization: actionsAuthorizationMock.create() as unknown as ActionsAuthorization, + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient: jest.fn(), + }); + }); + + it('filters action types by feature ID', async () => { + mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); + + actionTypeRegistry.register({ + id: 'my-action-type', + name: 'My action type', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + executor, + }); + + actionTypeRegistry.register({ + id: 'my-action-type-2', + name: 'My action type 2', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['cases'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + executor, + }); + + expect(await actionsClient.listTypes({ featureId: 'alerting' })).toEqual([ + { + id: 'my-action-type', + name: 'My action type', + minimumLicenseRequired: 'basic', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + supportedFeatureIds: ['alerting'], + isSystemActionType: false, + }, + ]); + }); + + it('filters out system action types when not defining options', async () => { + mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); + + actionTypeRegistry.register({ + id: 'my-action-type', + name: 'My action type', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + executor, + }); + + actionTypeRegistry.register({ + id: 'my-action-type-2', + name: 'My action type 2', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['cases'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + executor, + }); + + actionTypeRegistry.register({ + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + isSystemActionType: true, + executor, + }); + + expect(await actionsClient.listTypes({})).toEqual([ + { + id: 'my-action-type', + name: 'My action type', + minimumLicenseRequired: 'basic', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + supportedFeatureIds: ['alerting'], + isSystemActionType: false, + }, + { + id: 'my-action-type-2', + name: 'My action type 2', + isSystemActionType: false, + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['cases'], + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + }, + ]); + }); + + it('return system action types when defining options', async () => { + mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); + + actionTypeRegistry.register({ + id: 'my-action-type', + name: 'My action type', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + executor, + }); + + actionTypeRegistry.register({ + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + isSystemActionType: true, + executor, + }); + + expect(await actionsClient.listTypes({ includeSystemActionTypes: true })).toEqual([ + { + id: 'my-action-type', + name: 'My action type', + minimumLicenseRequired: 'basic', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + supportedFeatureIds: ['alerting'], + isSystemActionType: false, + }, + { + id: '.cases', + name: 'Cases', + isSystemActionType: true, + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + }, + ]); + }); +}); diff --git a/x-pack/plugins/actions/server/application/connector/methods/list_types/list_types.ts b/x-pack/plugins/actions/server/application/connector/methods/list_types/list_types.ts new file mode 100644 index 0000000000000..cf218eba89355 --- /dev/null +++ b/x-pack/plugins/actions/server/application/connector/methods/list_types/list_types.ts @@ -0,0 +1,33 @@ +/* + * 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 Boom from '@hapi/boom'; +import { ActionsClientContext } from '../../../../actions_client'; +import { ConnectorType } from '../../types'; +import { listTypesParamsSchema } from './schemas'; +import { ListTypesParams } from './types'; + +export async function listTypes( + context: ActionsClientContext, + options: ListTypesParams +): Promise { + try { + listTypesParamsSchema.validate(options); + } catch (error) { + throw Boom.badRequest(`Error validating params - ${error.message}`); + } + + const { featureId, includeSystemActionTypes } = options; + + const connectorTypes = context.actionTypeRegistry.list(featureId); + + const filteredConnectorTypes = includeSystemActionTypes + ? connectorTypes + : connectorTypes.filter((type) => !Boolean(type.isSystemActionType)); + + return filteredConnectorTypes; +} diff --git a/x-pack/plugins/actions/server/application/connector/methods/list_types/schemas/index.ts b/x-pack/plugins/actions/server/application/connector/methods/list_types/schemas/index.ts new file mode 100644 index 0000000000000..afc2a2e545ac1 --- /dev/null +++ b/x-pack/plugins/actions/server/application/connector/methods/list_types/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 { listTypesParamsSchema } from './list_types_params_schema'; diff --git a/x-pack/plugins/actions/server/application/connector/methods/list_types/schemas/list_types_params_schema.ts b/x-pack/plugins/actions/server/application/connector/methods/list_types/schemas/list_types_params_schema.ts new file mode 100644 index 0000000000000..848345c79ff31 --- /dev/null +++ b/x-pack/plugins/actions/server/application/connector/methods/list_types/schemas/list_types_params_schema.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 { schema } from '@kbn/config-schema'; + +export const listTypesParamsSchema = schema.object({ + featureId: schema.maybe(schema.string()), + includeSystemActionTypes: schema.maybe(schema.boolean()), +}); diff --git a/x-pack/plugins/actions/server/application/connector/methods/list_types/types/index.ts b/x-pack/plugins/actions/server/application/connector/methods/list_types/types/index.ts new file mode 100644 index 0000000000000..763aba62f135d --- /dev/null +++ b/x-pack/plugins/actions/server/application/connector/methods/list_types/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 type { ListTypesParams } from './list_types_params'; diff --git a/x-pack/plugins/actions/server/application/connector/methods/list_types/types/list_types_params.ts b/x-pack/plugins/actions/server/application/connector/methods/list_types/types/list_types_params.ts new file mode 100644 index 0000000000000..ac6d8b292964c --- /dev/null +++ b/x-pack/plugins/actions/server/application/connector/methods/list_types/types/list_types_params.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 { TypeOf } from '@kbn/config-schema'; +import { listTypesParamsSchema } from '../schemas'; + +type ListTypesParamsType = TypeOf; + +export interface ListTypesParams { + featureId?: ListTypesParamsType['featureId']; + includeSystemActionTypes?: ListTypesParamsType['includeSystemActionTypes']; +} diff --git a/x-pack/plugins/actions/server/application/connector/schemas/connector_type_schema.ts b/x-pack/plugins/actions/server/application/connector/schemas/connector_type_schema.ts new file mode 100644 index 0000000000000..e5556ab5c4a33 --- /dev/null +++ b/x-pack/plugins/actions/server/application/connector/schemas/connector_type_schema.ts @@ -0,0 +1,26 @@ +/* + * 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 connectorTypeSchema = schema.object({ + id: schema.string(), + name: schema.string(), + enabled: schema.boolean(), + enabledInConfig: schema.boolean(), + enabledInLicense: schema.boolean(), + minimumLicenseRequired: schema.oneOf([ + schema.literal('basic'), + schema.literal('standard'), + schema.literal('gold'), + schema.literal('platinum'), + schema.literal('enterprise'), + schema.literal('trial'), + ]), + supportedFeatureIds: schema.arrayOf(schema.string()), + isSystemActionType: schema.boolean(), +}); diff --git a/x-pack/plugins/actions/server/application/connector/schemas/index.ts b/x-pack/plugins/actions/server/application/connector/schemas/index.ts index f2a1bc4c6096a..b3cfc462c4208 100644 --- a/x-pack/plugins/actions/server/application/connector/schemas/index.ts +++ b/x-pack/plugins/actions/server/application/connector/schemas/index.ts @@ -6,3 +6,4 @@ */ export * from './connector_schema'; +export * from './connector_type_schema'; diff --git a/x-pack/plugins/actions/server/application/connector/types/connector_type.ts b/x-pack/plugins/actions/server/application/connector/types/connector_type.ts new file mode 100644 index 0000000000000..64be01365a8ba --- /dev/null +++ b/x-pack/plugins/actions/server/application/connector/types/connector_type.ts @@ -0,0 +1,22 @@ +/* + * 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 { TypeOf } from '@kbn/config-schema'; +import { connectorTypeSchema } from '../schemas'; + +type ConnectorTypeSchemaType = TypeOf; + +export interface ConnectorType { + id: ConnectorTypeSchemaType['id']; + name: ConnectorTypeSchemaType['name']; + enabled: ConnectorTypeSchemaType['enabled']; + enabledInConfig: ConnectorTypeSchemaType['enabledInConfig']; + enabledInLicense: ConnectorTypeSchemaType['enabledInLicense']; + minimumLicenseRequired: ConnectorTypeSchemaType['minimumLicenseRequired']; + supportedFeatureIds: ConnectorTypeSchemaType['supportedFeatureIds']; + isSystemActionType: ConnectorTypeSchemaType['isSystemActionType']; +} diff --git a/x-pack/plugins/actions/server/application/connector/types/index.ts b/x-pack/plugins/actions/server/application/connector/types/index.ts index ab87e9a5baaad..973513ec7b5cb 100644 --- a/x-pack/plugins/actions/server/application/connector/types/index.ts +++ b/x-pack/plugins/actions/server/application/connector/types/index.ts @@ -6,3 +6,4 @@ */ export type { Connector, FindConnectorResult } from './connector'; +export type { ConnectorType } from './connector_type'; diff --git a/x-pack/plugins/actions/server/mocks.ts b/x-pack/plugins/actions/server/mocks.ts index 14ae64391177f..ad26114cf7d07 100644 --- a/x-pack/plugins/actions/server/mocks.ts +++ b/x-pack/plugins/actions/server/mocks.ts @@ -12,7 +12,7 @@ import { } from '@kbn/core/server/mocks'; import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; import { Logger } from '@kbn/core/server'; -import { actionsClientMock } from './actions_client.mock'; +import { actionsClientMock } from './actions_client/actions_client.mock'; import { PluginSetupContract, PluginStartContract, renderActionParameterTemplates } from './plugin'; import { Services } from './types'; import { actionsAuthorizationMock } from './authorization/actions_authorization.mock'; diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 122e3075ac0ac..f754a0235cf0e 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -42,7 +42,7 @@ import { MonitoringCollectionSetup } from '@kbn/monitoring-collection-plugin/ser import { ActionsConfig, getValidatedConfig } from './config'; import { resolveCustomHosts } from './lib/custom_host_settings'; -import { ActionsClient } from './actions_client'; +import { ActionsClient } from './actions_client/actions_client'; import { ActionTypeRegistry } from './action_type_registry'; import { createExecutionEnqueuerFunction, diff --git a/x-pack/plugins/actions/server/routes/connector/get_all/get_all.test.ts b/x-pack/plugins/actions/server/routes/connector/get_all/get_all.test.ts index 06491cfeb8ef2..223a2d56c0843 100644 --- a/x-pack/plugins/actions/server/routes/connector/get_all/get_all.test.ts +++ b/x-pack/plugins/actions/server/routes/connector/get_all/get_all.test.ts @@ -10,7 +10,7 @@ import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../../../lib/license_state.mock'; import { mockHandlerArguments } from '../../legacy/_mock_handler_arguments'; import { verifyAccessAndContext } from '../../verify_access_and_context'; -import { actionsClientMock } from '../../../actions_client.mock'; +import { actionsClientMock } from '../../../actions_client/actions_client.mock'; jest.mock('../../verify_access_and_context', () => ({ verifyAccessAndContext: jest.fn(), diff --git a/x-pack/plugins/actions/server/routes/connector/list_types/index.ts b/x-pack/plugins/actions/server/routes/connector/list_types/index.ts new file mode 100644 index 0000000000000..668942c40ae90 --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/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 { listTypesRoute } from './list_types'; diff --git a/x-pack/plugins/actions/server/routes/connector_types.test.ts b/x-pack/plugins/actions/server/routes/connector/list_types/list_types.test.ts similarity index 91% rename from x-pack/plugins/actions/server/routes/connector_types.test.ts rename to x-pack/plugins/actions/server/routes/connector/list_types/list_types.test.ts index 4c5a7089b75c4..91419ac562324 100644 --- a/x-pack/plugins/actions/server/routes/connector_types.test.ts +++ b/x-pack/plugins/actions/server/routes/connector/list_types/list_types.test.ts @@ -5,15 +5,15 @@ * 2.0. */ -import { connectorTypesRoute } from './connector_types'; import { httpServiceMock } from '@kbn/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { mockHandlerArguments } from './legacy/_mock_handler_arguments'; import { LicenseType } from '@kbn/licensing-plugin/server'; -import { actionsClientMock } from '../mocks'; -import { verifyAccessAndContext } from './verify_access_and_context'; +import { licenseStateMock } from '../../../lib/license_state.mock'; +import { mockHandlerArguments } from '../../legacy/_mock_handler_arguments'; +import { listTypesRoute } from './list_types'; +import { verifyAccessAndContext } from '../../verify_access_and_context'; +import { actionsClientMock } from '../../../mocks'; -jest.mock('./verify_access_and_context', () => ({ +jest.mock('../../verify_access_and_context', () => ({ verifyAccessAndContext: jest.fn(), })); @@ -22,12 +22,12 @@ beforeEach(() => { (verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler); }); -describe('connectorTypesRoute', () => { +describe('listTypesRoute', () => { it('lists action types with proper parameters', async () => { const licenseState = licenseStateMock.create(); const router = httpServiceMock.createRouter(); - connectorTypesRoute(router, licenseState); + listTypesRoute(router, licenseState); const [config, handler] = router.get.mock.calls[0]; @@ -89,7 +89,7 @@ describe('connectorTypesRoute', () => { const licenseState = licenseStateMock.create(); const router = httpServiceMock.createRouter(); - connectorTypesRoute(router, licenseState); + listTypesRoute(router, licenseState); const [config, handler] = router.get.mock.calls[0]; @@ -168,7 +168,7 @@ describe('connectorTypesRoute', () => { const licenseState = licenseStateMock.create(); const router = httpServiceMock.createRouter(); - connectorTypesRoute(router, licenseState); + listTypesRoute(router, licenseState); const [config, handler] = router.get.mock.calls[0]; @@ -211,7 +211,7 @@ describe('connectorTypesRoute', () => { throw new Error('OMG'); }); - connectorTypesRoute(router, licenseState); + listTypesRoute(router, licenseState); const [config, handler] = router.get.mock.calls[0]; diff --git a/x-pack/plugins/actions/server/routes/connector/list_types/list_types.ts b/x-pack/plugins/actions/server/routes/connector/list_types/list_types.ts new file mode 100644 index 0000000000000..078c51743c4d9 --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/list_types/list_types.ts @@ -0,0 +1,47 @@ +/* + * 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 { ConnectorTypesResponseV1 } from '../../../../common/routes/connector/response'; +import { + connectorTypesQuerySchemaV1, + ConnectorTypesRequestQueryV1, +} from '../../../../common/routes/connector/apis/connector_types'; +import { transformListTypesResponseV1 } from './transforms'; +import { ActionsRequestHandlerContext } from '../../../types'; +import { BASE_ACTION_API_PATH } from '../../../../common'; +import { ILicenseState } from '../../../lib'; +import { verifyAccessAndContext } from '../../verify_access_and_context'; + +export const listTypesRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.get( + { + path: `${BASE_ACTION_API_PATH}/connector_types`, + validate: { + query: connectorTypesQuerySchemaV1, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const actionsClient = (await context.actions).getActionsClient(); + + // Assert versioned inputs + const query: ConnectorTypesRequestQueryV1 = req.query; + + const connectorTypes = await actionsClient.listTypes({ featureId: query?.feature_id }); + + const responseBody: ConnectorTypesResponseV1[] = + transformListTypesResponseV1(connectorTypes); + + return res.ok({ body: responseBody }); + }) + ) + ); +}; diff --git a/x-pack/plugins/actions/server/routes/connector/list_types/transforms/index.ts b/x-pack/plugins/actions/server/routes/connector/list_types/transforms/index.ts new file mode 100644 index 0000000000000..35e5f1db443c2 --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/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 { transformListTypesResponse } from './transform_list_types_response/latest'; +export { transformListTypesResponse as transformListTypesResponseV1 } from './transform_list_types_response/v1'; diff --git a/x-pack/plugins/actions/server/routes/connector/list_types/transforms/transform_list_types_response/latest.ts b/x-pack/plugins/actions/server/routes/connector/list_types/transforms/transform_list_types_response/latest.ts new file mode 100644 index 0000000000000..5fd887263233c --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/list_types/transforms/transform_list_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 { transformListTypesResponse } from './v1'; diff --git a/x-pack/plugins/actions/server/routes/connector/list_types/transforms/transform_list_types_response/v1.ts b/x-pack/plugins/actions/server/routes/connector/list_types/transforms/transform_list_types_response/v1.ts new file mode 100644 index 0000000000000..e32bec2f9e1a1 --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/list_types/transforms/transform_list_types_response/v1.ts @@ -0,0 +1,35 @@ +/* + * 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 { ConnectorType } from '../../../../../application/connector/types'; +import { ConnectorTypesResponseV1 } from '../../../../../../common/routes/connector/response'; + +export const transformListTypesResponse = ( + results: ConnectorType[] +): ConnectorTypesResponseV1[] => { + return results.map( + ({ + id, + name, + enabled, + enabledInConfig, + enabledInLicense, + minimumLicenseRequired, + supportedFeatureIds, + isSystemActionType, + }) => ({ + id, + name, + enabled, + enabled_in_config: enabledInConfig, + enabled_in_license: enabledInLicense, + minimum_license_required: minimumLicenseRequired, + supported_feature_ids: supportedFeatureIds, + is_system_action_type: isSystemActionType, + }) + ); +}; diff --git a/x-pack/plugins/actions/server/routes/connector_types.ts b/x-pack/plugins/actions/server/routes/connector_types.ts deleted file mode 100644 index d54b35a7a99df..0000000000000 --- a/x-pack/plugins/actions/server/routes/connector_types.ts +++ /dev/null @@ -1,59 +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 { schema } from '@kbn/config-schema'; -import { ILicenseState } from '../lib'; -import { ActionType, BASE_ACTION_API_PATH, RewriteResponseCase } from '../../common'; -import { ActionsRequestHandlerContext } from '../types'; -import { verifyAccessAndContext } from './verify_access_and_context'; - -const querySchema = schema.object({ - feature_id: schema.maybe(schema.string()), -}); - -const rewriteBodyRes: RewriteResponseCase = (results) => { - return results.map( - ({ - enabledInConfig, - enabledInLicense, - minimumLicenseRequired, - supportedFeatureIds, - isSystemActionType, - ...res - }) => ({ - ...res, - enabled_in_config: enabledInConfig, - enabled_in_license: enabledInLicense, - minimum_license_required: minimumLicenseRequired, - supported_feature_ids: supportedFeatureIds, - is_system_action_type: isSystemActionType, - }) - ); -}; - -export const connectorTypesRoute = ( - router: IRouter, - licenseState: ILicenseState -) => { - router.get( - { - path: `${BASE_ACTION_API_PATH}/connector_types`, - validate: { - query: querySchema, - }, - }, - router.handleLegacyErrors( - verifyAccessAndContext(licenseState, async function (context, req, res) { - const actionsClient = (await context.actions).getActionsClient(); - return res.ok({ - body: rewriteBodyRes(await actionsClient.listTypes({ featureId: req.query?.feature_id })), - }); - }) - ) - ); -}; diff --git a/x-pack/plugins/actions/server/routes/create.test.ts b/x-pack/plugins/actions/server/routes/create.test.ts index 2161e68e3706b..dd1317f57cd27 100644 --- a/x-pack/plugins/actions/server/routes/create.test.ts +++ b/x-pack/plugins/actions/server/routes/create.test.ts @@ -9,9 +9,9 @@ import { createActionRoute, bodySchema } from './create'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../lib/license_state.mock'; import { mockHandlerArguments } from './legacy/_mock_handler_arguments'; -import { actionsClientMock } from '../actions_client.mock'; import { verifyAccessAndContext } from './verify_access_and_context'; import { omit } from 'lodash'; +import { actionsClientMock } from '../actions_client/actions_client.mock'; jest.mock('./verify_access_and_context', () => ({ verifyAccessAndContext: jest.fn(), diff --git a/x-pack/plugins/actions/server/routes/execute.test.ts b/x-pack/plugins/actions/server/routes/execute.test.ts index 6bcab72fbc869..12960aeae47e6 100644 --- a/x-pack/plugins/actions/server/routes/execute.test.ts +++ b/x-pack/plugins/actions/server/routes/execute.test.ts @@ -10,7 +10,7 @@ import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../lib/license_state.mock'; import { mockHandlerArguments } from './legacy/_mock_handler_arguments'; import { asHttpRequestExecutionSource } from '../lib'; -import { actionsClientMock } from '../actions_client.mock'; +import { actionsClientMock } from '../actions_client/actions_client.mock'; import { ActionTypeExecutorResult } from '../types'; import { verifyAccessAndContext } from './verify_access_and_context'; diff --git a/x-pack/plugins/actions/server/routes/get.test.ts b/x-pack/plugins/actions/server/routes/get.test.ts index 76dda602945c9..a52cea1d49f6d 100644 --- a/x-pack/plugins/actions/server/routes/get.test.ts +++ b/x-pack/plugins/actions/server/routes/get.test.ts @@ -9,7 +9,7 @@ import { getActionRoute } from './get'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../lib/license_state.mock'; import { mockHandlerArguments } from './legacy/_mock_handler_arguments'; -import { actionsClientMock } from '../actions_client.mock'; +import { actionsClientMock } from '../actions_client/actions_client.mock'; import { verifyAccessAndContext } from './verify_access_and_context'; jest.mock('./verify_access_and_context', () => ({ diff --git a/x-pack/plugins/actions/server/routes/get_global_execution_kpi.test.ts b/x-pack/plugins/actions/server/routes/get_global_execution_kpi.test.ts index 838a1bf69789f..066d558bcfd59 100644 --- a/x-pack/plugins/actions/server/routes/get_global_execution_kpi.test.ts +++ b/x-pack/plugins/actions/server/routes/get_global_execution_kpi.test.ts @@ -8,7 +8,7 @@ import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../lib/license_state.mock'; import { mockHandlerArguments } from './legacy/_mock_handler_arguments'; -import { actionsClientMock } from '../actions_client.mock'; +import { actionsClientMock } from '../actions_client/actions_client.mock'; import { getGlobalExecutionKPIRoute } from './get_global_execution_kpi'; import { verifyAccessAndContext } from './verify_access_and_context'; diff --git a/x-pack/plugins/actions/server/routes/get_global_execution_logs.test.ts b/x-pack/plugins/actions/server/routes/get_global_execution_logs.test.ts index 1c309e14dae09..4654885a49bcb 100644 --- a/x-pack/plugins/actions/server/routes/get_global_execution_logs.test.ts +++ b/x-pack/plugins/actions/server/routes/get_global_execution_logs.test.ts @@ -9,7 +9,7 @@ import { getGlobalExecutionLogRoute } from './get_global_execution_logs'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../lib/license_state.mock'; import { mockHandlerArguments } from './legacy/_mock_handler_arguments'; -import { actionsClientMock } from '../actions_client.mock'; +import { actionsClientMock } from '../actions_client/actions_client.mock'; import { IExecutionLogResult } from '../../common'; import { verifyAccessAndContext } from './verify_access_and_context'; diff --git a/x-pack/plugins/actions/server/routes/get_oauth_access_token.test.ts b/x-pack/plugins/actions/server/routes/get_oauth_access_token.test.ts index ff13b021a209f..ae06068273ca3 100644 --- a/x-pack/plugins/actions/server/routes/get_oauth_access_token.test.ts +++ b/x-pack/plugins/actions/server/routes/get_oauth_access_token.test.ts @@ -11,7 +11,7 @@ import { licenseStateMock } from '../lib/license_state.mock'; import { mockHandlerArguments } from './legacy/_mock_handler_arguments'; import { verifyAccessAndContext } from './verify_access_and_context'; import { actionsConfigMock } from '../actions_config.mock'; -import { actionsClientMock } from '../actions_client.mock'; +import { actionsClientMock } from '../actions_client/actions_client.mock'; jest.mock('./verify_access_and_context', () => ({ verifyAccessAndContext: jest.fn(), diff --git a/x-pack/plugins/actions/server/routes/index.ts b/x-pack/plugins/actions/server/routes/index.ts index d46ce9dd5cc51..e5dce0ac3b6c7 100644 --- a/x-pack/plugins/actions/server/routes/index.ts +++ b/x-pack/plugins/actions/server/routes/index.ts @@ -8,13 +8,13 @@ import { IRouter } from '@kbn/core/server'; import { UsageCounter } from '@kbn/usage-collection-plugin/server'; import { getAllConnectorsRoute } from './connector/get_all'; +import { listTypesRoute } from './connector/list_types'; import { ILicenseState } from '../lib'; import { ActionsRequestHandlerContext } from '../types'; import { createActionRoute } from './create'; import { deleteActionRoute } from './delete'; import { executeActionRoute } from './execute'; import { getActionRoute } from './get'; -import { connectorTypesRoute } from './connector_types'; import { updateActionRoute } from './update'; import { getOAuthAccessToken } from './get_oauth_access_token'; import { defineLegacyRoutes } from './legacy'; @@ -39,7 +39,7 @@ export function defineRoutes(opts: RouteOptions) { getActionRoute(router, licenseState); getAllConnectorsRoute(router, licenseState); updateActionRoute(router, licenseState); - connectorTypesRoute(router, licenseState); + listTypesRoute(router, licenseState); executeActionRoute(router, licenseState); getGlobalExecutionLogRoute(router, licenseState); getGlobalExecutionKPIRoute(router, licenseState); diff --git a/x-pack/plugins/actions/server/routes/legacy/_mock_handler_arguments.ts b/x-pack/plugins/actions/server/routes/legacy/_mock_handler_arguments.ts index 07ac40b7e52e1..73906fa0a63e3 100644 --- a/x-pack/plugins/actions/server/routes/legacy/_mock_handler_arguments.ts +++ b/x-pack/plugins/actions/server/routes/legacy/_mock_handler_arguments.ts @@ -9,15 +9,16 @@ import { KibanaRequest, KibanaResponseFactory } from '@kbn/core/server'; import { identity } from 'lodash'; import type { MethodKeysOf } from '@kbn/utility-types'; import { httpServerMock } from '@kbn/core/server/mocks'; -import { ActionType } from '../../../common'; -import { ActionsClientMock, actionsClientMock } from '../../actions_client.mock'; import { ActionsRequestHandlerContext } from '../../types'; +import { actionsClientMock } from '../../mocks'; +import { ActionsClientMock } from '../../actions_client/actions_client.mock'; +import { ConnectorType } from '../../application/connector/types'; export function mockHandlerArguments( { actionsClient = actionsClientMock.create(), listTypes: listTypesRes = [], - }: { actionsClient?: ActionsClientMock; listTypes?: ActionType[] }, + }: { actionsClient?: ActionsClientMock; listTypes?: ConnectorType[] }, request: unknown, response?: Array> ): [ActionsRequestHandlerContext, KibanaRequest, KibanaResponseFactory] { diff --git a/x-pack/plugins/actions/server/routes/legacy/create.test.ts b/x-pack/plugins/actions/server/routes/legacy/create.test.ts index 0d285244342a6..e711f73265f53 100644 --- a/x-pack/plugins/actions/server/routes/legacy/create.test.ts +++ b/x-pack/plugins/actions/server/routes/legacy/create.test.ts @@ -9,7 +9,7 @@ import { createActionRoute } from './create'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../../lib/license_state.mock'; import { mockHandlerArguments } from './_mock_handler_arguments'; -import { actionsClientMock } from '../../actions_client.mock'; +import { actionsClientMock } from '../../actions_client/actions_client.mock'; import { verifyAccessAndContext } from '../verify_access_and_context'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; diff --git a/x-pack/plugins/actions/server/routes/legacy/execute.test.ts b/x-pack/plugins/actions/server/routes/legacy/execute.test.ts index 1f9e7bb7566da..53e7d038dfea5 100644 --- a/x-pack/plugins/actions/server/routes/legacy/execute.test.ts +++ b/x-pack/plugins/actions/server/routes/legacy/execute.test.ts @@ -10,7 +10,7 @@ import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../../lib/license_state.mock'; import { mockHandlerArguments } from './_mock_handler_arguments'; import { verifyApiAccess, ActionTypeDisabledError, asHttpRequestExecutionSource } from '../../lib'; -import { actionsClientMock } from '../../actions_client.mock'; +import { actionsClientMock } from '../../actions_client/actions_client.mock'; import { ActionTypeExecutorResult } from '../../types'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; diff --git a/x-pack/plugins/actions/server/routes/legacy/get.test.ts b/x-pack/plugins/actions/server/routes/legacy/get.test.ts index be5cc1c819e3a..6a19400da0fb5 100644 --- a/x-pack/plugins/actions/server/routes/legacy/get.test.ts +++ b/x-pack/plugins/actions/server/routes/legacy/get.test.ts @@ -10,7 +10,7 @@ import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../../lib/license_state.mock'; import { verifyApiAccess } from '../../lib'; import { mockHandlerArguments } from './_mock_handler_arguments'; -import { actionsClientMock } from '../../actions_client.mock'; +import { actionsClientMock } from '../../actions_client/actions_client.mock'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; diff --git a/x-pack/plugins/actions/server/routes/legacy/get_all.test.ts b/x-pack/plugins/actions/server/routes/legacy/get_all.test.ts index f7cdb56082a10..e999c769f3dbb 100644 --- a/x-pack/plugins/actions/server/routes/legacy/get_all.test.ts +++ b/x-pack/plugins/actions/server/routes/legacy/get_all.test.ts @@ -10,7 +10,7 @@ import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../../lib/license_state.mock'; import { verifyApiAccess } from '../../lib'; import { mockHandlerArguments } from './_mock_handler_arguments'; -import { actionsClientMock } from '../../actions_client.mock'; +import { actionsClientMock } from '../../actions_client/actions_client.mock'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; diff --git a/x-pack/plugins/actions/server/routes/legacy/update.test.ts b/x-pack/plugins/actions/server/routes/legacy/update.test.ts index 9158b4165a660..304b504636c91 100644 --- a/x-pack/plugins/actions/server/routes/legacy/update.test.ts +++ b/x-pack/plugins/actions/server/routes/legacy/update.test.ts @@ -10,7 +10,7 @@ import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../../lib/license_state.mock'; import { verifyApiAccess, ActionTypeDisabledError } from '../../lib'; import { mockHandlerArguments } from './_mock_handler_arguments'; -import { actionsClientMock } from '../../actions_client.mock'; +import { actionsClientMock } from '../../actions_client/actions_client.mock'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; diff --git a/x-pack/plugins/actions/server/routes/update.test.ts b/x-pack/plugins/actions/server/routes/update.test.ts index ac9f3dc45f660..aef179264ac48 100644 --- a/x-pack/plugins/actions/server/routes/update.test.ts +++ b/x-pack/plugins/actions/server/routes/update.test.ts @@ -9,7 +9,7 @@ import { bodySchema, updateActionRoute } from './update'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../lib/license_state.mock'; import { mockHandlerArguments } from './legacy/_mock_handler_arguments'; -import { actionsClientMock } from '../actions_client.mock'; +import { actionsClientMock } from '../actions_client/actions_client.mock'; import { verifyAccessAndContext } from './verify_access_and_context'; jest.mock('./verify_access_and_context', () => ({ diff --git a/x-pack/plugins/actions/server/routes/verify_access_and_context.test.ts b/x-pack/plugins/actions/server/routes/verify_access_and_context.test.ts index 4b07216172473..e079634fbfeff 100644 --- a/x-pack/plugins/actions/server/routes/verify_access_and_context.test.ts +++ b/x-pack/plugins/actions/server/routes/verify_access_and_context.test.ts @@ -8,7 +8,7 @@ import { licenseStateMock } from '../lib/license_state.mock'; import { verifyApiAccess, ActionTypeDisabledError } from '../lib'; import { mockHandlerArguments } from './legacy/_mock_handler_arguments'; -import { actionsClientMock } from '../actions_client.mock'; +import { actionsClientMock } from '../actions_client/actions_client.mock'; import { verifyAccessAndContext } from './verify_access_and_context'; jest.mock('../lib/verify_api_access', () => ({ diff --git a/x-pack/plugins/cases/server/client/mocks.ts b/x-pack/plugins/cases/server/client/mocks.ts index f912ce26f581e..8fd0a61c35f30 100644 --- a/x-pack/plugins/cases/server/client/mocks.ts +++ b/x-pack/plugins/cases/server/client/mocks.ts @@ -11,7 +11,7 @@ import type { ISavedObjectsSerializer } from '@kbn/core-saved-objects-server'; import { createFileServiceMock } from '@kbn/files-plugin/server/mocks'; import { securityMock } from '@kbn/security-plugin/server/mocks'; -import { actionsClientMock } from '@kbn/actions-plugin/server/actions_client.mock'; +import { actionsClientMock } from '@kbn/actions-plugin/server/actions_client/actions_client.mock'; import { makeLensEmbeddableFactory } from '@kbn/lens-plugin/server/embeddable/make_lens_embeddable_factory'; import { serializerMock } from '@kbn/core-saved-objects-base-server-mocks'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts index a71585308c397..e68e84acf6029 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts @@ -16,7 +16,7 @@ import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; // See: https://github.com/elastic/kibana/issues/117255, the moduleNameMapper creates mocks to avoid memory leaks from kibana core. // We cannot import from "../../../../../../actions/server" directly here or we have a really bad memory issue. We cannot add this to the existing mocks we created, this fix must be here. -import { actionsClientMock } from '@kbn/actions-plugin/server/actions_client.mock'; +import { actionsClientMock } from '@kbn/actions-plugin/server/actions_client/actions_client.mock'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import { listMock } from '@kbn/lists-plugin/server/mocks'; import { ruleRegistryMocks } from '@kbn/rule-registry-plugin/server/mocks'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts index 6897fdaf0b92c..16167acdf51ac 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts @@ -27,7 +27,7 @@ import { requestContextMock } from '../../../routes/__mocks__/request_context'; import { savedObjectsExporterMock } from '@kbn/core-saved-objects-import-export-server-mocks'; import { mockRouter } from '@kbn/core-http-router-server-mocks'; import { Readable } from 'stream'; -import { actionsClientMock } from '@kbn/actions-plugin/server/actions_client.mock'; +import { actionsClientMock } from '@kbn/actions-plugin/server/actions_client/actions_client.mock'; const exceptionsClient = getExceptionListClientMock(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts index c2fe91251d75d..c9d7f82588c52 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts @@ -28,7 +28,7 @@ import { mockRouter } from '@kbn/core-http-router-server-mocks'; const exceptionsClient = getExceptionListClientMock(); import type { loggingSystemMock } from '@kbn/core/server/mocks'; import { requestContextMock } from '../../../routes/__mocks__/request_context'; -import { actionsClientMock } from '@kbn/actions-plugin/server/actions_client.mock'; +import { actionsClientMock } from '@kbn/actions-plugin/server/actions_client/actions_client.mock'; const connectors = [ { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/import_rule_action_connectors.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/import_rule_action_connectors.test.ts index f6e0e34904c5a..7b161f1ab27f2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/import_rule_action_connectors.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/import_rule_action_connectors.test.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { actionsClientMock } from '@kbn/actions-plugin/server/actions_client.mock'; +import { actionsClientMock } from '@kbn/actions-plugin/server/actions_client/actions_client.mock'; import { getImportRulesSchemaMock, webHookConnector, From 870011739dff09183baa5809f702f727fa6e4a27 Mon Sep 17 00:00:00 2001 From: Sander Philipse <94373878+sphilipse@users.noreply.github.com> Date: Wed, 30 Aug 2023 14:57:36 +0200 Subject: [PATCH 04/53] [Search] Fix platinum license for cloud connectors (#165209) ## Summary This makes platinum license connectors accessible on cloud regardless of license. --- .../components/new_index/select_connector/select_connector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/select_connector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/select_connector.tsx index 83371d415ad84..ce8cb3c699478 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/select_connector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/select_connector.tsx @@ -228,7 +228,7 @@ export const SelectConnector: React.FC = () => { {filteredConnectors.map((connector) => ( Date: Wed, 30 Aug 2023 07:58:11 -0500 Subject: [PATCH 05/53] [Serverless Search] fix: gate ent-search results with config (#165171) ## Summary Gates returning Enterprise Search results when ent-search nodes are not going to be available. --- .../server/utils/search_result_provider.ts | 54 ++++++++++--------- .../components/api_key/api_key.tsx | 2 +- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/x-pack/plugins/enterprise_search/server/utils/search_result_provider.ts b/x-pack/plugins/enterprise_search/server/utils/search_result_provider.ts index 13ead48887acb..0869a16b79dc7 100644 --- a/x-pack/plugins/enterprise_search/server/utils/search_result_provider.ts +++ b/x-pack/plugins/enterprise_search/server/utils/search_result_provider.ts @@ -98,32 +98,34 @@ export function getSearchResultProvider( ] : []), ...(config.hasConnectors ? CONNECTOR_DEFINITIONS : []), - ...[ - { - keywords: ['app', 'search', 'engines'], - name: i18n.translate('xpack.enterpriseSearch.searchProvider.appSearch.name', { - defaultMessage: 'App Search', - }), - serviceType: 'app_search', - url: APP_SEARCH_PLUGIN.URL, - }, - { - keywords: ['workplace', 'search'], - name: i18n.translate('xpack.enterpriseSearch.searchProvider.workplaceSearch.name', { - defaultMessage: 'Workplace Search', - }), - serviceType: 'workplace_search', - url: WORKPLACE_SEARCH_PLUGIN.URL, - }, - { - keywords: ['esre', 'search'], - name: i18n.translate('xpack.enterpriseSearch.searchProvider.esre.name', { - defaultMessage: 'ESRE', - }), - serviceType: 'esre', - url: ESRE_PLUGIN.URL, - }, - ], + ...(config.canDeployEntSearch + ? [ + { + keywords: ['app', 'search', 'engines'], + name: i18n.translate('xpack.enterpriseSearch.searchProvider.appSearch.name', { + defaultMessage: 'App Search', + }), + serviceType: 'app_search', + url: APP_SEARCH_PLUGIN.URL, + }, + { + keywords: ['workplace', 'search'], + name: i18n.translate('xpack.enterpriseSearch.searchProvider.workplaceSearch.name', { + defaultMessage: 'Workplace Search', + }), + serviceType: 'workplace_search', + url: WORKPLACE_SEARCH_PLUGIN.URL, + }, + { + keywords: ['esre', 'search'], + name: i18n.translate('xpack.enterpriseSearch.searchProvider.esre.name', { + defaultMessage: 'ESRE', + }), + serviceType: 'esre', + url: ESRE_PLUGIN.URL, + }, + ] + : []), ]; const result = services .map((service) => { diff --git a/x-pack/plugins/serverless_search/public/application/components/api_key/api_key.tsx b/x-pack/plugins/serverless_search/public/application/components/api_key/api_key.tsx index 4351c0777fe80..07d93fb8bcfe2 100644 --- a/x-pack/plugins/serverless_search/public/application/components/api_key/api_key.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/api_key/api_key.tsx @@ -53,7 +53,7 @@ export const ApiKeyPanel = ({ setClientApiKey }: { setClientApiKey: (value: stri /> )} {apiKey ? ( - + Date: Wed, 30 Aug 2023 07:58:25 -0500 Subject: [PATCH 06/53] [Serverless Search] Hide Connectors page from side nav (#165174) ## Summary Removing the connectors from the side nav for now. It is currently a WIP and should not be included in Feature Freeze until its ready. --- .../serverless_search/public/layout/nav.tsx | 14 ++++++++------ x-pack/plugins/serverless_search/public/plugin.ts | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/serverless_search/public/layout/nav.tsx b/x-pack/plugins/serverless_search/public/layout/nav.tsx index 65eb1787a07a9..0879f86bc2b73 100644 --- a/x-pack/plugins/serverless_search/public/layout/nav.tsx +++ b/x-pack/plugins/serverless_search/public/layout/nav.tsx @@ -16,6 +16,14 @@ import { i18n } from '@kbn/i18n'; import type { ServerlessPluginStart } from '@kbn/serverless/public'; import type { CloudStart } from '@kbn/cloud-plugin/public'; +// Hiding this until page is in a better space +const _connectorItem = { + link: 'serverlessConnectors', + title: i18n.translate('xpack.serverlessSearch.nav.connectors', { + defaultMessage: 'Connectors', + }), +}; + const navigationTree: NavigationTreeDefinition = { body: [ { type: 'recentlyAccessed' }, @@ -72,12 +80,6 @@ const navigationTree: NavigationTreeDefinition = { defaultMessage: 'Alerts', }), }, - { - link: 'serverlessConnectors', - title: i18n.translate('xpack.serverlessSearch.nav.connectors', { - defaultMessage: 'Connectors', - }), - }, ], }, { diff --git a/x-pack/plugins/serverless_search/public/plugin.ts b/x-pack/plugins/serverless_search/public/plugin.ts index dfe9cbbc5165c..c3faeb15f4384 100644 --- a/x-pack/plugins/serverless_search/public/plugin.ts +++ b/x-pack/plugins/serverless_search/public/plugin.ts @@ -54,6 +54,7 @@ export class ServerlessSearchPlugin defaultMessage: 'Connectors', }), appRoute: '/app/connectors', + searchable: false, async mount({ element }: AppMountParameters) { const { renderApp } = await import('./application/connectors'); const [coreStart, services] = await core.getStartServices(); From b88547c323fb97dac9e08c4ccdf12ac4e5f0899d Mon Sep 17 00:00:00 2001 From: Jordan <51442161+JordanSh@users.noreply.github.com> Date: Wed, 30 Aug 2023 16:14:58 +0300 Subject: [PATCH 07/53] [Cloud Security] Fix first item flyout wont open in vuln table (#165214) --- .../public/pages/vulnerabilities/vulnerabilities.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx index d0b6412eaa393..a9b8fdaa2f190 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx @@ -279,9 +279,7 @@ const VulnerabilitiesDataGrid = ({ [pageSize, setUrlQuery] ); - const showVulnerabilityFlyout = flyoutVulnerabilityIndex - ? flyoutVulnerabilityIndex > invalidIndex - : undefined; + const showVulnerabilityFlyout = flyoutVulnerabilityIndex > invalidIndex; if (data?.page.length === 0) { return ; From 3128c46c70029c5a227dedf4214e73be02ca7009 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Wed, 30 Aug 2023 15:38:22 +0200 Subject: [PATCH 08/53] Add GitHub Action Workflow: create-deploy-tag (#165213) --- .github/workflows/create-deploy-tag.yml | 60 +++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .github/workflows/create-deploy-tag.yml diff --git a/.github/workflows/create-deploy-tag.yml b/.github/workflows/create-deploy-tag.yml new file mode 100644 index 0000000000000..e173784ecc3fb --- /dev/null +++ b/.github/workflows/create-deploy-tag.yml @@ -0,0 +1,60 @@ +--- +# - This workflow creates a tag with the format "deploy@" on the main branch. +# - It is triggered manually from the GitHub Actions UI. +# - It is only allowed to run on the main branch and ensures that the tag is created +# on the main branch only in a verification step. +# This is only to prevent accidental creation of the tag on other branches and cannot be used to prevent malicious creation of the tag. + +name: create-deploy-tag + +on: + workflow_dispatch: + inputs: + commit: + description: "The commit to tag (default: latest commit on main)" + +concurrency: + group: ${{ github.workflow }} + +jobs: + create-deploy-tag: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Select commit to be tagged + run: | + commit="${{ github.event.inputs.commit || github.sha }}" + echo "COMMIT=${commit}" >> "${GITHUB_ENV}" + - name: Verify selected commit isn't already tagged + run: | + git tag --contains ${COMMIT} | grep -P "^deploy@\d+$" && { + echo "Tag already exists on selected commit" + exit 1 + } || true + - name: Verify branch + run: | + if [[ "${GITHUB_REF}" != "refs/heads/main" ]]; then + echo "This workflow can only be run on the main branch" + exit 1 + fi + - name: Prepare tag + run: | + tag_name="deploy@$(date +%s)" + echo "TAG_NAME=${tag_name}" >> "${GITHUB_ENV}" + - name: Create tag + run: | + git tag ${TAG_NAME} ${COMMIT} + git push origin "refs/tags/${TAG_NAME}" + - if: always() + uses: elastic/apm-pipeline-library/.github/actions/notify-build-status@current + with: + message: ${{ job.status == 'success' && format('Created tag `{0}` for commit `{1}`', env.TAG_NAME, env.COMMIT) || 'Creating a deploy tag failed' }} + vaultUrl: ${{ secrets.VAULT_ADDR }} + vaultRoleId: ${{ secrets.VAULT_ROLE_ID }} + vaultSecretId: ${{ secrets.VAULT_SECRET_ID }} + slackChannel: "#kibana-mission-control" \ No newline at end of file From e56c0f61eddbb5dd377a7893cfb69d9044d3d86f Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Wed, 30 Aug 2023 09:57:10 -0400 Subject: [PATCH 09/53] feat(slo): add partition by field in APM indicators (#165106) --- .../apm_availability/apm_availability_indicator_type_form.tsx | 4 ++++ .../apm_latency/apm_latency_indicator_type_form.tsx | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx index 5c6f06051eb3d..377f5e0dc0710 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx @@ -12,10 +12,12 @@ import { useFormContext } from 'react-hook-form'; import { CreateSLOForm } from '../../types'; import { FieldSelector } from '../apm_common/field_selector'; import { DataPreviewChart } from '../common/data_preview_chart'; +import { GroupByFieldSelector } from '../common/group_by_field_selector'; import { QueryBuilder } from '../common/query_builder'; export function ApmAvailabilityIndicatorTypeForm() { const { watch } = useFormContext(); + const index = watch('indicator.params.index'); return ( @@ -119,6 +121,8 @@ export function ApmAvailabilityIndicatorTypeForm() { + + ); diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx index e91080e2910dd..316f17f5072e6 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx @@ -12,10 +12,12 @@ import { Controller, useFormContext } from 'react-hook-form'; import { CreateSLOForm } from '../../types'; import { FieldSelector } from '../apm_common/field_selector'; import { DataPreviewChart } from '../common/data_preview_chart'; +import { GroupByFieldSelector } from '../common/group_by_field_selector'; import { QueryBuilder } from '../common/query_builder'; export function ApmLatencyIndicatorTypeForm() { const { control, watch, getFieldState } = useFormContext(); + const index = watch('indicator.params.index'); return ( @@ -162,6 +164,8 @@ export function ApmLatencyIndicatorTypeForm() { + + ); From 7dd2663cba103ee40234c62fadb6e3639aa797a4 Mon Sep 17 00:00:00 2001 From: Katerina Date: Wed, 30 Aug 2023 16:21:53 +0200 Subject: [PATCH 10/53] [APM] Require `_meta` in APM Telemetry Schema (#165219) ## Summary 1. Update type to accept `RequireMeta` and make it required for apm telemetry schmea 2. Add missing descriptions 3. Cleaning up apm schema a bit ### Related - https://github.com/elastic/kibana/pull/157529 --- .../server/collector/types.ts | 14 +- .../__snapshots__/apm_telemetry.test.ts.snap | 215 ++++-- .../apm/server/lib/apm_telemetry/schema.ts | 611 +++++++++++------- .../apm/server/lib/apm_telemetry/types.ts | 2 +- .../schema/xpack_plugins.json | 215 ++++-- 5 files changed, 737 insertions(+), 320 deletions(-) diff --git a/src/plugins/usage_collection/server/collector/types.ts b/src/plugins/usage_collection/server/collector/types.ts index ff5b7135ba5c0..50a04560e384b 100644 --- a/src/plugins/usage_collection/server/collector/types.ts +++ b/src/plugins/usage_collection/server/collector/types.ts @@ -21,7 +21,7 @@ export type { /** * Helper to find out whether to keep recursively looking or if we are on an end value */ -export type RecursiveMakeSchemaFrom = U extends object +export type RecursiveMakeSchemaFrom = U extends object ? Record extends U ? | { @@ -31,19 +31,21 @@ export type RecursiveMakeSchemaFrom = U extends object description: string; // Intentionally enforcing the descriptions here } & SchemaMetaOptional; } - | MakeSchemaFrom // But still allow being explicit in the definition if they want to. - : MakeSchemaFrom + | MakeSchemaFrom // But still allow being explicit in the definition if they want to. + : MakeSchemaFrom + : RequireMeta extends true + ? { type: PossibleSchemaTypes; _meta: { description: string } } : { type: PossibleSchemaTypes; _meta?: { description: string } }; /** * The `schema` property in {@link CollectorOptions} must match the output of * the `fetch` method. This type helps ensure that is correct */ -export type MakeSchemaFrom = { +export type MakeSchemaFrom = { // Using Required to enforce all optional keys in the object [Key in keyof Required]: Required[Key] extends Array - ? { type: 'array'; items: RecursiveMakeSchemaFrom } - : RecursiveMakeSchemaFrom[Key]>; + ? { type: 'array'; items: RecursiveMakeSchemaFrom } + : RecursiveMakeSchemaFrom[Key], RequireMeta>; }; /** diff --git a/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap b/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap index 7e0d699fddfc0..2bed81c9c05c3 100644 --- a/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap +++ b/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap @@ -14,76 +14,148 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "services_per_agent": { "properties": { "android/java": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the android/java agent within the last day" + } }, "dotnet": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the dotnet (.Net) agent within the last day" + } }, "iOS/swift": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the iOS/swift agent within the last day" + } }, "go": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the go agent within the last day" + } }, "java": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the Java agent within the last day" + } }, "js-base": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the js-base agent within the last day" + } }, "nodejs": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the nodeJS agent within the last day" + } }, "php": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the PHH agent within the last day" + } }, "python": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the Python agent within the last day" + } }, "ruby": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the Ruby agent within the last day" + } }, "rum-js": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the rum-js agent within the last day" + } }, "otlp": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the otlp agent within the last day" + } }, "opentelemetry/cpp": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/cpp agent within the last day" + } }, "opentelemetry/dotnet": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/dotnet agent within the last day" + } }, "opentelemetry/erlang": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/erlang agent within the last day" + } }, "opentelemetry/go": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/go agent within the last day" + } }, "opentelemetry/java": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/java agent within the last day" + } }, "opentelemetry/nodejs": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/nodejs agent within the last day" + } }, "opentelemetry/php": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/php agent within the last day" + } }, "opentelemetry/python": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/python agent within the last day" + } }, "opentelemetry/ruby": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/ruby agent within the last day" + } }, "opentelemetry/rust": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/rust agent within the last day" + } }, "opentelemetry/swift": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/swift agent within the last day" + } }, "opentelemetry/webjs": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/webjs agent within the last day" + } } } }, @@ -1140,60 +1212,96 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "current_implementation": { "properties": { "expected_metric_document_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } }, "transaction_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } } } }, "no_observer_name": { "properties": { "expected_metric_document_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } }, "transaction_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } } } }, "no_rum": { "properties": { "expected_metric_document_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } }, "transaction_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } } } }, "no_rum_no_observer_name": { "properties": { "expected_metric_document_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } }, "transaction_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } } } }, "only_rum": { "properties": { "expected_metric_document_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } }, "transaction_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } } } }, "only_rum_no_observer_name": { "properties": { "expected_metric_document_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } }, "transaction_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } } } } @@ -1366,10 +1474,10 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "services": { "properties": { "1d": { - "type": "long" - }, - "all": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of unique services within the last day" + } } } }, @@ -1552,7 +1660,10 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "shards": { "properties": { "total": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of shards for metric indices" + } } } }, @@ -1563,14 +1674,20 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "docs": { "properties": { "count": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of metric documents overall" + } } } }, "store": { "properties": { "size_in_bytes": { - "type": "long" + "type": "long", + "_meta": { + "description": "Size of the metric indicess in byte units overall." + } } } } @@ -1587,7 +1704,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "total": { "type": "long", "_meta": { - "description": "Total number of shards overall" + "description": "Total number of shards for span and trasnaction indices" } } } @@ -1819,7 +1936,10 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "pod": { "properties": { "name": { - "type": "keyword" + "type": "keyword", + "_meta": { + "description": "Kuberneted pod name " + } } } } @@ -1828,7 +1948,10 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "container": { "properties": { "id": { - "type": "keyword" + "type": "keyword", + "_meta": { + "description": "Container id" + } } } } diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts index 3054c9f5e8bc7..b5d361777b602 100644 --- a/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts @@ -6,164 +6,153 @@ */ import { MakeSchemaFrom } from '@kbn/usage-collection-plugin/server'; -import { - AggregatedTransactionsCounts, - APMUsage, - TimeframeMap, - TimeframeMap1d, - TimeframeMapAll, - APMPerService, -} from './types'; +import { AggregatedTransactionsCounts, APMUsage, APMPerService } from './types'; import { ElasticAgentName } from '../../../typings/es_schemas/ui/fields/agent'; -const long: { type: 'long' } = { type: 'long' }; - -const keyword: { type: 'keyword' } = { type: 'keyword' }; - -const aggregatedTransactionCountSchema: MakeSchemaFrom = - { - expected_metric_document_count: long, - transaction_count: long, - }; - -const timeframeMap1dSchema: MakeSchemaFrom = { - '1d': long, -}; - -const timeframeMapAllSchema: MakeSchemaFrom = { - all: long, -}; - -const timeframeMapSchema: MakeSchemaFrom = { - ...timeframeMap1dSchema, - ...timeframeMapAllSchema, -}; - -const agentSchema: MakeSchemaFrom['agents'][ElasticAgentName] = { - agent: { - version: { - type: 'array', - items: { - type: 'keyword', - _meta: { - description: - 'An array of the top 3 agent versions within the last day', - }, - }, +const aggregatedTransactionCountSchema: MakeSchemaFrom< + AggregatedTransactionsCounts, + true +> = { + expected_metric_document_count: { + type: 'long', + _meta: { + description: '', }, - activation_method: { - type: 'array', - items: { - type: 'keyword', - _meta: { - description: - 'An array of the top 3 agent activation methods within the last day', - }, - }, + }, + transaction_count: { + type: 'long', + _meta: { + description: '', }, }, - service: { - framework: { - name: { - type: 'array', - items: { - type: 'keyword', - _meta: { - description: - 'An array of the top 3 service framework name within the last day', - }, - }, - }, +}; + +const agentSchema: MakeSchemaFrom['agents'][ElasticAgentName] = + { + agent: { version: { type: 'array', items: { type: 'keyword', _meta: { description: - 'An array of the top 3 service framework version within the last day', + 'An array of the top 3 agent versions within the last day', }, }, }, - composite: { + activation_method: { type: 'array', items: { type: 'keyword', _meta: { description: - 'Composite field containing service framework and version sorted by doc count', + 'An array of the top 3 agent activation methods within the last day', }, }, }, }, - language: { - name: { - type: 'array', - items: { - type: 'keyword', - _meta: { - description: - 'An array of the top 3 service language name within the last day', + service: { + framework: { + name: { + type: 'array', + items: { + type: 'keyword', + _meta: { + description: + 'An array of the top 3 service framework name within the last day', + }, }, }, - }, - version: { - type: 'array', - items: { - type: 'keyword', - _meta: { - description: - 'An array of the top 3 service language version within the last day', + version: { + type: 'array', + items: { + type: 'keyword', + _meta: { + description: + 'An array of the top 3 service framework version within the last day', + }, }, }, - }, - composite: { - type: 'array', - items: { - type: 'keyword', - _meta: { - description: - 'Composite field containing service language name and version sorted by doc count.', + composite: { + type: 'array', + items: { + type: 'keyword', + _meta: { + description: + 'Composite field containing service framework and version sorted by doc count', + }, }, }, }, - }, - runtime: { - name: { - type: 'array', - items: { - type: 'keyword', - _meta: { - description: - 'An array of the top 3 service runtime name within the last day', + language: { + name: { + type: 'array', + items: { + type: 'keyword', + _meta: { + description: + 'An array of the top 3 service language name within the last day', + }, }, }, - }, - version: { - type: 'array', - items: { - type: 'keyword', - _meta: { - description: - 'An array of the top 3 service runtime version within the last day', + version: { + type: 'array', + items: { + type: 'keyword', + _meta: { + description: + 'An array of the top 3 service language version within the last day', + }, + }, + }, + composite: { + type: 'array', + items: { + type: 'keyword', + _meta: { + description: + 'Composite field containing service language name and version sorted by doc count.', + }, }, }, }, - composite: { - type: 'array', - items: { - type: 'keyword', - _meta: { - description: - 'Composite field containing service runtime name and version sorted by doc count.', + runtime: { + name: { + type: 'array', + items: { + type: 'keyword', + _meta: { + description: + 'An array of the top 3 service runtime name within the last day', + }, + }, + }, + version: { + type: 'array', + items: { + type: 'keyword', + _meta: { + description: + 'An array of the top 3 service runtime version within the last day', + }, + }, + }, + composite: { + type: 'array', + items: { + type: 'keyword', + _meta: { + description: + 'Composite field containing service runtime name and version sorted by doc count.', + }, }, }, }, }, - }, -}; + }; const apmPerAgentSchema: Pick< - MakeSchemaFrom, + MakeSchemaFrom, 'services_per_agent' | 'agents' > = { // services_per_agent: AGENT_NAMES.reduce( @@ -177,30 +166,174 @@ const apmPerAgentSchema: Pick< // TODO: Find a way for `@kbn/telemetry-tools` to understand and evaluate expressions. // In the meanwhile, we'll have to maintain these lists up to date (TS will remind us to update) services_per_agent: { - 'android/java': long, - dotnet: long, - 'iOS/swift': long, - go: long, - java: long, - 'js-base': long, - nodejs: long, - php: long, - python: long, - ruby: long, - 'rum-js': long, - otlp: long, - 'opentelemetry/cpp': long, - 'opentelemetry/dotnet': long, - 'opentelemetry/erlang': long, - 'opentelemetry/go': long, - 'opentelemetry/java': long, - 'opentelemetry/nodejs': long, - 'opentelemetry/php': long, - 'opentelemetry/python': long, - 'opentelemetry/ruby': long, - 'opentelemetry/rust': long, - 'opentelemetry/swift': long, - 'opentelemetry/webjs': long, + 'android/java': { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the android/java agent within the last day', + }, + }, + dotnet: { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the dotnet (.Net) agent within the last day', + }, + }, + 'iOS/swift': { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the iOS/swift agent within the last day', + }, + }, + go: { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the go agent within the last day', + }, + }, + java: { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the Java agent within the last day', + }, + }, + 'js-base': { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the js-base agent within the last day', + }, + }, + nodejs: { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the nodeJS agent within the last day', + }, + }, + php: { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the PHH agent within the last day', + }, + }, + python: { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the Python agent within the last day', + }, + }, + ruby: { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the Ruby agent within the last day', + }, + }, + 'rum-js': { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the rum-js agent within the last day', + }, + }, + otlp: { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the otlp agent within the last day', + }, + }, + 'opentelemetry/cpp': { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the opentelemetry/cpp agent within the last day', + }, + }, + 'opentelemetry/dotnet': { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the opentelemetry/dotnet agent within the last day', + }, + }, + 'opentelemetry/erlang': { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the opentelemetry/erlang agent within the last day', + }, + }, + 'opentelemetry/go': { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the opentelemetry/go agent within the last day', + }, + }, + 'opentelemetry/java': { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the opentelemetry/java agent within the last day', + }, + }, + 'opentelemetry/nodejs': { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the opentelemetry/nodejs agent within the last day', + }, + }, + 'opentelemetry/php': { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the opentelemetry/php agent within the last day', + }, + }, + 'opentelemetry/python': { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the opentelemetry/python agent within the last day', + }, + }, + 'opentelemetry/ruby': { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the opentelemetry/ruby agent within the last day', + }, + }, + 'opentelemetry/rust': { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the opentelemetry/rust agent within the last day', + }, + }, + 'opentelemetry/swift': { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the opentelemetry/swift agent within the last day', + }, + }, + 'opentelemetry/webjs': { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the opentelemetry/webjs agent within the last day', + }, + }, }, agents: { 'android/java': agentSchema, @@ -217,23 +350,23 @@ const apmPerAgentSchema: Pick< }, }; -export const apmPerServiceSchema: MakeSchemaFrom = { +export const apmPerServiceSchema: MakeSchemaFrom = { service_id: { - ...keyword, + type: 'keyword', _meta: { description: 'Unique identifier that combines the SHA256 hashed representation of the service name and environment', }, }, num_service_nodes: { - ...long, + type: 'long', _meta: { description: 'Total number of the unique service instances that served the transaction within an hour', }, }, num_transaction_types: { - ...long, + type: 'long', _meta: { description: 'Total number of the unique transaction types within an hour', @@ -293,21 +426,21 @@ export const apmPerServiceSchema: MakeSchemaFrom = { }, agent: { name: { - ...keyword, + type: 'keyword', _meta: { description: 'The top value of agent name for the service from transaction documents within an hour. Sorted by _score', }, }, version: { - ...keyword, + type: 'keyword', _meta: { description: 'The top value of agent version for the service from transaction documents within an hour. Sorted by _score', }, }, activation_method: { - ...keyword, + type: 'keyword', _meta: { description: 'The top value of agent activation method for the service from transaction documents within an hour. Sorted by _score', @@ -317,14 +450,14 @@ export const apmPerServiceSchema: MakeSchemaFrom = { service: { language: { name: { - ...keyword, + type: 'keyword', _meta: { description: 'The top value of language name for the service from transaction documents within an hour. Sorted by _score', }, }, version: { - ...keyword, + type: 'keyword', _meta: { description: 'The top value of language version for the service from transaction documents within an hour. Sorted by _score', @@ -333,14 +466,14 @@ export const apmPerServiceSchema: MakeSchemaFrom = { }, framework: { name: { - ...keyword, + type: 'keyword', _meta: { description: 'The top value of service framework name from transaction documents within an hour. Sorted by _score. Example AWS Lambda', }, }, version: { - ...keyword, + type: 'keyword', _meta: { description: 'The top value of service framework version from transaction documents within an hour. Sorted by _score', @@ -349,14 +482,14 @@ export const apmPerServiceSchema: MakeSchemaFrom = { }, runtime: { name: { - ...keyword, + type: 'keyword', _meta: { description: 'The top value of service runtime name from transaction documents within an hour. Sorted by _score', }, }, version: { - ...keyword, + type: 'keyword', _meta: { description: 'The top value of service runtime version version from transaction documents within an hour. Sorted by _score', @@ -367,16 +500,26 @@ export const apmPerServiceSchema: MakeSchemaFrom = { // No data found kubernetes: { pod: { - name: keyword, + name: { + type: 'keyword', + _meta: { + description: 'Kuberneted pod name ', + }, + }, }, }, // No data found container: { - id: keyword, + id: { + type: 'keyword', + _meta: { + description: 'Container id', + }, + }, }, }; -export const apmSchema: MakeSchemaFrom = { +export const apmSchema: MakeSchemaFrom = { ...apmPerAgentSchema, has_any_services: { type: 'boolean', @@ -388,19 +531,19 @@ export const apmSchema: MakeSchemaFrom = { version: { apm_server: { major: { - ...long, + type: 'long', _meta: { description: 'The major version of the APM server. Example: 7', }, }, minor: { - ...long, + type: 'long', _meta: { description: 'The minor version of the APM server. Example: 17', }, }, patch: { - ...long, + type: 'long', _meta: { description: 'The patch version of the APM server. Example 3', }, @@ -409,14 +552,14 @@ export const apmSchema: MakeSchemaFrom = { }, environments: { services_without_environment: { - ...long, + type: 'long', _meta: { description: 'Number of services without an assigned environment within the last day. This is determined by checking the "service.environment" field and counting instances where it is null', }, }, services_with_multiple_environments: { - ...long, + type: 'long', _meta: { description: 'Number of services with more than one assigned environment within the last day', @@ -433,7 +576,6 @@ export const apmSchema: MakeSchemaFrom = { }, }, }, - // #NOTE No task identified for extracting the following information aggregated_transactions: { current_implementation: aggregatedTransactionCountSchema, no_observer_name: aggregatedTransactionCountSchema, @@ -491,14 +633,14 @@ export const apmSchema: MakeSchemaFrom = { counts: { transaction: { '1d': { - ...long, + type: 'long', _meta: { description: 'Total number of transaction documents within the last day', }, }, all: { - ...long, + type: 'long', _meta: { description: 'Total number of transaction documents overall', }, @@ -506,13 +648,13 @@ export const apmSchema: MakeSchemaFrom = { }, span: { '1d': { - ...long, + type: 'long', _meta: { description: 'Total number of span documents within the last day', }, }, all: { - ...long, + type: 'long', _meta: { description: 'Total number of span documents overall', }, @@ -520,13 +662,13 @@ export const apmSchema: MakeSchemaFrom = { }, error: { '1d': { - ...long, + type: 'long', _meta: { description: 'Total number of error documents within the last day', }, }, all: { - ...long, + type: 'long', _meta: { description: 'Total number of error documents overall', }, @@ -534,13 +676,13 @@ export const apmSchema: MakeSchemaFrom = { }, metric: { '1d': { - ...long, + type: 'long', _meta: { description: 'Total number of metric documents within the last day', }, }, all: { - ...long, + type: 'long', _meta: { description: 'Total number of metric documents overall', }, @@ -548,14 +690,14 @@ export const apmSchema: MakeSchemaFrom = { }, onboarding: { '1d': { - ...long, + type: 'long', _meta: { description: 'Total number of onboarding documents within the last day', }, }, all: { - ...long, + type: 'long', _meta: { description: 'Total number of onboarding documents overall', }, @@ -563,7 +705,7 @@ export const apmSchema: MakeSchemaFrom = { }, agent_configuration: { all: { - ...long, + type: 'long', _meta: { description: 'Total number of apm-agent-configuration documents overall', @@ -572,7 +714,7 @@ export const apmSchema: MakeSchemaFrom = { }, max_transaction_groups_per_service: { '1d': { - ...long, + type: 'long', _meta: { description: 'Total number of distinct transaction groups for the top service for the last 24 hours', @@ -581,7 +723,7 @@ export const apmSchema: MakeSchemaFrom = { }, max_error_groups_per_service: { '1d': { - ...long, + type: 'long', _meta: { description: 'Total number of distinct error groups for the top service for the last 24 hours', @@ -590,23 +732,29 @@ export const apmSchema: MakeSchemaFrom = { }, traces: { '1d': { - ...long, + type: 'long', _meta: { description: 'Total number of trace documents within the last day', }, }, all: { - ...long, + type: 'long', _meta: { description: 'Total number of trace documents overall', }, }, }, - // No tasks found - services: timeframeMapSchema, + services: { + '1d': { + type: 'long', + _meta: { + description: 'Total number of unique services within the last day', + }, + }, + }, environments: { '1d': { - ...long, + type: 'long', _meta: { description: 'Total number of unique environments within the last day', @@ -615,7 +763,7 @@ export const apmSchema: MakeSchemaFrom = { }, span_destination_service_resource: { '1d': { - ...long, + type: 'long', _meta: { description: 'Total number of unique values of span.destination.service.resource within the last day', @@ -630,7 +778,7 @@ export const apmSchema: MakeSchemaFrom = { country_iso_code: { rum: { '1d': { - ...long, + type: 'long', _meta: { description: 'Unique country iso code captured for the agents js-base, rum-js and opentelemetry/webjs within the last day', @@ -644,7 +792,7 @@ export const apmSchema: MakeSchemaFrom = { original: { all_agents: { '1d': { - ...long, + type: 'long', _meta: { description: 'Unique user agent for all agents within the last day', @@ -653,7 +801,7 @@ export const apmSchema: MakeSchemaFrom = { }, rum: { '1d': { - ...long, + type: 'long', _meta: { description: 'Unique user agent for rum agent within the last day', @@ -666,7 +814,7 @@ export const apmSchema: MakeSchemaFrom = { name: { all_agents: { '1d': { - ...long, + type: 'long', _meta: { description: 'Unique transaction names for all agents within the last day', @@ -675,7 +823,7 @@ export const apmSchema: MakeSchemaFrom = { }, rum: { '1d': { - ...long, + type: 'long', _meta: { description: 'Unique transaction names for rum agent within the last day', @@ -689,7 +837,7 @@ export const apmSchema: MakeSchemaFrom = { retainment: { span: { ms: { - ...long, + type: 'long', _meta: { description: 'Represent the time difference in milliseconds between the current date and the date when the span document was recorded', @@ -698,7 +846,7 @@ export const apmSchema: MakeSchemaFrom = { }, transaction: { ms: { - ...long, + type: 'long', _meta: { description: 'Represent the time difference in milliseconds between the current date and the date when the transaction document was recorded', @@ -707,7 +855,7 @@ export const apmSchema: MakeSchemaFrom = { }, error: { ms: { - ...long, + type: 'long', _meta: { description: 'Represent the time difference in milliseconds between the current date and the date when the error document was recorded', @@ -716,7 +864,7 @@ export const apmSchema: MakeSchemaFrom = { }, metric: { ms: { - ...long, + type: 'long', _meta: { description: 'Represent the time difference in milliseconds between the current date and the date when the metric document was recorded', @@ -725,7 +873,7 @@ export const apmSchema: MakeSchemaFrom = { }, onboarding: { ms: { - ...long, + type: 'long', _meta: { description: 'Represent the time difference in milliseconds between the current date and the date when the onboarding document was recorded', @@ -736,7 +884,7 @@ export const apmSchema: MakeSchemaFrom = { integrations: { ml: { all_jobs_count: { - ...long, + type: 'long', _meta: { description: 'Total number of anomaly detection jobs associated with the jobs apm-*, *-high_mean_response_time', @@ -746,23 +894,44 @@ export const apmSchema: MakeSchemaFrom = { }, indices: { - // cannot find related data metric: { - shards: { total: long }, + shards: { + total: { + type: 'long', + _meta: { + description: 'Total number of shards for metric indices', + }, + }, + }, all: { total: { - docs: { count: long }, - store: { size_in_bytes: long }, + docs: { + count: { + type: 'long', + _meta: { + description: 'Total number of metric documents overall', + }, + }, + }, + store: { + size_in_bytes: { + type: 'long', + _meta: { + description: + 'Size of the metric indicess in byte units overall.', + }, + }, + }, }, }, }, - // cannot find related data traces: { shards: { total: { - ...long, + type: 'long', _meta: { - description: 'Total number of shards overall', + description: + 'Total number of shards for span and trasnaction indices', }, }, }, @@ -770,7 +939,7 @@ export const apmSchema: MakeSchemaFrom = { total: { docs: { count: { - ...long, + type: 'long', _meta: { description: 'Total number of transaction and span documents overall', @@ -779,7 +948,7 @@ export const apmSchema: MakeSchemaFrom = { }, store: { size_in_bytes: { - ...long, + type: 'long', _meta: { description: 'Size of the index in byte units overall.', }, @@ -790,7 +959,7 @@ export const apmSchema: MakeSchemaFrom = { }, shards: { total: { - ...long, + type: 'long', _meta: { description: 'Total number of shards overall', }, @@ -800,7 +969,7 @@ export const apmSchema: MakeSchemaFrom = { total: { docs: { count: { - ...long, + type: 'long', _meta: { description: 'Total number of all documents overall', }, @@ -808,7 +977,7 @@ export const apmSchema: MakeSchemaFrom = { }, store: { size_in_bytes: { - ...long, + type: 'long', _meta: { description: 'Size of the index in byte units overall.', }, @@ -829,7 +998,7 @@ export const apmSchema: MakeSchemaFrom = { }, }, total: { - ...long, + type: 'long', _meta: { description: 'Total number of service groups retrived from the saved object across all spaces', @@ -841,7 +1010,7 @@ export const apmSchema: MakeSchemaFrom = { aggregated_transactions: { took: { ms: { - ...long, + type: 'long', _meta: { description: 'Execution time in milliseconds for the "aggregated_transactions" task', @@ -852,7 +1021,7 @@ export const apmSchema: MakeSchemaFrom = { cloud: { took: { ms: { - ...long, + type: 'long', _meta: { description: 'Execution time in milliseconds for the "cloud" task', }, @@ -862,7 +1031,7 @@ export const apmSchema: MakeSchemaFrom = { host: { took: { ms: { - ...long, + type: 'long', _meta: { description: 'Execution time in milliseconds for the "host" task', }, @@ -872,7 +1041,7 @@ export const apmSchema: MakeSchemaFrom = { processor_events: { took: { ms: { - ...long, + type: 'long', _meta: { description: 'Execution time in milliseconds for the "processor_events" task', @@ -883,7 +1052,7 @@ export const apmSchema: MakeSchemaFrom = { agent_configuration: { took: { ms: { - ...long, + type: 'long', _meta: { description: 'Execution time in milliseconds for the "agent_configuration" task', @@ -894,7 +1063,7 @@ export const apmSchema: MakeSchemaFrom = { services: { took: { ms: { - ...long, + type: 'long', _meta: { description: 'Execution time in milliseconds for the "services" task', @@ -905,7 +1074,7 @@ export const apmSchema: MakeSchemaFrom = { versions: { took: { ms: { - ...long, + type: 'long', _meta: { description: 'Execution time in milliseconds for the "versions" task', @@ -916,7 +1085,7 @@ export const apmSchema: MakeSchemaFrom = { groupings: { took: { ms: { - ...long, + type: 'long', _meta: { description: 'Execution time in milliseconds for the "groupings" task', @@ -927,7 +1096,7 @@ export const apmSchema: MakeSchemaFrom = { integrations: { took: { ms: { - ...long, + type: 'long', _meta: { description: 'Execution time in milliseconds for the "integrations" task', @@ -938,7 +1107,7 @@ export const apmSchema: MakeSchemaFrom = { agents: { took: { ms: { - ...long, + type: 'long', _meta: { description: 'Execution time in milliseconds for the "agents" task', }, @@ -948,7 +1117,7 @@ export const apmSchema: MakeSchemaFrom = { indices_stats: { took: { ms: { - ...long, + type: 'long', _meta: { description: 'Execution time in milliseconds for the "indices_stats" task', @@ -959,7 +1128,7 @@ export const apmSchema: MakeSchemaFrom = { cardinality: { took: { ms: { - ...long, + type: 'long', _meta: { description: 'Execution time in milliseconds for the "cardinality" task', @@ -970,7 +1139,7 @@ export const apmSchema: MakeSchemaFrom = { environments: { took: { ms: { - ...long, + type: 'long', _meta: { description: 'Execution time in milliseconds for the "environments" task', @@ -981,7 +1150,7 @@ export const apmSchema: MakeSchemaFrom = { service_groups: { took: { ms: { - ...long, + type: 'long', _meta: { description: 'Execution time in milliseconds for the "service_groups" task', @@ -992,7 +1161,7 @@ export const apmSchema: MakeSchemaFrom = { per_service: { took: { ms: { - ...long, + type: 'long', _meta: { description: 'Execution time in milliseconds for the "per_service" task', diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/types.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/types.ts index e88e021fbd0cb..9c6c312c284f4 100644 --- a/x-pack/plugins/apm/server/lib/apm_telemetry/types.ts +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/types.ts @@ -107,7 +107,7 @@ export interface APMUsage { max_transaction_groups_per_service: TimeframeMap1d; max_error_groups_per_service: TimeframeMap1d; traces: TimeframeMap; - services: TimeframeMap; + services: TimeframeMap1d; environments: TimeframeMap1d; span_destination_service_resource: TimeframeMap1d; }; diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json index 61ddfa8e00505..8e517ada4ddca 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -2728,76 +2728,148 @@ "services_per_agent": { "properties": { "android/java": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the android/java agent within the last day" + } }, "dotnet": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the dotnet (.Net) agent within the last day" + } }, "iOS/swift": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the iOS/swift agent within the last day" + } }, "go": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the go agent within the last day" + } }, "java": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the Java agent within the last day" + } }, "js-base": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the js-base agent within the last day" + } }, "nodejs": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the nodeJS agent within the last day" + } }, "php": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the PHH agent within the last day" + } }, "python": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the Python agent within the last day" + } }, "ruby": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the Ruby agent within the last day" + } }, "rum-js": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the rum-js agent within the last day" + } }, "otlp": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the otlp agent within the last day" + } }, "opentelemetry/cpp": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/cpp agent within the last day" + } }, "opentelemetry/dotnet": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/dotnet agent within the last day" + } }, "opentelemetry/erlang": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/erlang agent within the last day" + } }, "opentelemetry/go": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/go agent within the last day" + } }, "opentelemetry/java": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/java agent within the last day" + } }, "opentelemetry/nodejs": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/nodejs agent within the last day" + } }, "opentelemetry/php": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/php agent within the last day" + } }, "opentelemetry/python": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/python agent within the last day" + } }, "opentelemetry/ruby": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/ruby agent within the last day" + } }, "opentelemetry/rust": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/rust agent within the last day" + } }, "opentelemetry/swift": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/swift agent within the last day" + } }, "opentelemetry/webjs": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/webjs agent within the last day" + } } } }, @@ -4220,60 +4292,96 @@ "current_implementation": { "properties": { "expected_metric_document_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } }, "transaction_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } } } }, "no_observer_name": { "properties": { "expected_metric_document_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } }, "transaction_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } } } }, "no_rum": { "properties": { "expected_metric_document_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } }, "transaction_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } } } }, "no_rum_no_observer_name": { "properties": { "expected_metric_document_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } }, "transaction_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } } } }, "only_rum": { "properties": { "expected_metric_document_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } }, "transaction_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } } } }, "only_rum_no_observer_name": { "properties": { "expected_metric_document_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } }, "transaction_count": { - "type": "long" + "type": "long", + "_meta": { + "description": "" + } } } } @@ -4458,10 +4566,10 @@ "services": { "properties": { "1d": { - "type": "long" - }, - "all": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of unique services within the last day" + } } } }, @@ -4644,7 +4752,10 @@ "shards": { "properties": { "total": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of shards for metric indices" + } } } }, @@ -4655,14 +4766,20 @@ "docs": { "properties": { "count": { - "type": "long" + "type": "long", + "_meta": { + "description": "Total number of metric documents overall" + } } } }, "store": { "properties": { "size_in_bytes": { - "type": "long" + "type": "long", + "_meta": { + "description": "Size of the metric indicess in byte units overall." + } } } } @@ -4679,7 +4796,7 @@ "total": { "type": "long", "_meta": { - "description": "Total number of shards overall" + "description": "Total number of shards for span and trasnaction indices" } } } @@ -4928,7 +5045,10 @@ "pod": { "properties": { "name": { - "type": "keyword" + "type": "keyword", + "_meta": { + "description": "Kuberneted pod name " + } } } } @@ -4937,7 +5057,10 @@ "container": { "properties": { "id": { - "type": "keyword" + "type": "keyword", + "_meta": { + "description": "Container id" + } } } } From b343654fc40273e6fc4352b7f3631255eeb419c5 Mon Sep 17 00:00:00 2001 From: Rodney Norris Date: Wed, 30 Aug 2023 09:26:53 -0500 Subject: [PATCH 11/53] serverless_search(Overview): footer redesign (#165044) ## Summary Updated the getting started overview footer, we combined the whats next and icon buttons into a single panel with different icons and copy. ### Screenshots ![image](https://github.com/elastic/kibana/assets/1972968/cbea1f08-59b3-41df-8a38-3baa4d7f31b7) --- .../application/components/overview.scss | 35 +- .../application/components/overview.test.tsx | 2 +- .../application/components/overview.tsx | 340 ++++++++---------- .../public/assets/billing_icon.png | Bin 18049 -> 0 bytes .../public/assets/community_icon.png | Bin 25759 -> 0 bytes .../public/assets/discover_bg.png | Bin 12828 -> 0 bytes .../public/assets/discover_icon.png | Bin 18886 -> 0 bytes .../public/assets/feedback_icon.png | Bin 13881 -> 0 bytes .../public/assets/invite_users_icon.png | Bin 19222 -> 0 bytes .../public/assets/searchui_bg.png | Bin 14366 -> 0 bytes .../public/assets/searchui_icon.png | Bin 18295 -> 0 bytes .../public/assets/transform_bg.png | Bin 18279 -> 0 bytes .../public/assets/transform_icon.png | Bin 20727 -> 0 bytes .../translations/translations/fr-FR.json | 14 - .../translations/translations/ja-JP.json | 14 - .../translations/translations/zh-CN.json | 14 - 16 files changed, 144 insertions(+), 275 deletions(-) delete mode 100644 x-pack/plugins/serverless_search/public/assets/billing_icon.png delete mode 100644 x-pack/plugins/serverless_search/public/assets/community_icon.png delete mode 100644 x-pack/plugins/serverless_search/public/assets/discover_bg.png delete mode 100644 x-pack/plugins/serverless_search/public/assets/discover_icon.png delete mode 100644 x-pack/plugins/serverless_search/public/assets/feedback_icon.png delete mode 100644 x-pack/plugins/serverless_search/public/assets/invite_users_icon.png delete mode 100644 x-pack/plugins/serverless_search/public/assets/searchui_bg.png delete mode 100644 x-pack/plugins/serverless_search/public/assets/searchui_icon.png delete mode 100644 x-pack/plugins/serverless_search/public/assets/transform_bg.png delete mode 100644 x-pack/plugins/serverless_search/public/assets/transform_icon.png diff --git a/x-pack/plugins/serverless_search/public/application/components/overview.scss b/x-pack/plugins/serverless_search/public/application/components/overview.scss index bb7992b3ac51f..72948fb99c1c4 100644 --- a/x-pack/plugins/serverless_search/public/application/components/overview.scss +++ b/x-pack/plugins/serverless_search/public/application/components/overview.scss @@ -7,37 +7,6 @@ padding-top: 0; } -.serverlessSearchFooterCardsSection { - background-color: $euiColorPrimary; - padding-top: $euiSizeXL; - padding-bottom: $euiSizeXL; -} - -.serverlessSearchFooterCard { - &--wrapper { - position: relative; - } - - &--background { - z-index: 1; - } - - &--iconWrapper { - align-items: center; - display: inlineFlex; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - justify-content: center; - padding: 20px; - z-index: 2; - } -} - -.serverlessSearchFooter { - background-color: $euiColorSuccess; - padding-top: $euiSizeXL; - padding-bottom: $euiSizeXL; +.serverlessSearchOverviewFooterSection { + background-color: transparentize($euiColorPrimary, .9); } diff --git a/x-pack/plugins/serverless_search/public/application/components/overview.test.tsx b/x-pack/plugins/serverless_search/public/application/components/overview.test.tsx index 971025125ab94..0d1cf8efa67c8 100644 --- a/x-pack/plugins/serverless_search/public/application/components/overview.test.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/overview.test.tsx @@ -53,6 +53,6 @@ describe('', () => { }); test("what's next?", () => { const { getByRole } = render(); - expect(getByRole('heading', { name: "What's next?" })).toBeDefined(); + expect(getByRole('heading', { name: 'Do more with your data' })).toBeDefined(); }); }); diff --git a/x-pack/plugins/serverless_search/public/application/components/overview.tsx b/x-pack/plugins/serverless_search/public/application/components/overview.tsx index e8ef03a72a1f0..0fb599c770044 100644 --- a/x-pack/plugins/serverless_search/public/application/components/overview.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/overview.tsx @@ -6,17 +6,14 @@ */ import { - EuiButton, + EuiAvatar, + EuiButtonEmpty, EuiCard, EuiFlexGroup, EuiFlexItem, - EuiImage, - EuiLink, EuiPageTemplate, - EuiSpacer, + EuiPanel, EuiText, - EuiTextColor, - EuiTitle, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { @@ -52,7 +49,6 @@ export const ElasticsearchOverview = () => { useState(javascriptDefinition); const [clientApiKey, setClientApiKey] = useState(API_KEY_PLACEHOLDER); const { application, cloud, http, userProfile, share } = useKibanaServices(); - const { navigateToApp } = application; const elasticsearchURL = useMemo(() => { return cloud?.elasticsearchUrl ?? ELASTICSEARCH_URL_PLACEHOLDER; @@ -63,7 +59,7 @@ export const ElasticsearchOverview = () => { apiKey: clientApiKey, }; - const { data } = useQuery({ + const { data: _data } = useQuery({ queryKey: ['fetchConnectors'], queryFn: () => http.fetch<{ connectors: Connector[] }>('/internal/serverless_search/connectors'), @@ -241,203 +237,149 @@ export const ElasticsearchOverview = () => { })} /> - - - - -

- - {i18n.translate('xpack.serverlessSearch.footer.title', { - defaultMessage: "What's next?", - })} - -

-
-
-
- - - - - - navigateToApp('discover')}> - {i18n.translate('xpack.serverlessSearch.footer.discoverCard.buttonText', { - defaultMessage: 'Explore data in Discover', - })} - - - - } - image={ - - } - /> - - - - - - navigateToApp('management', { path: '/ingest/ingest_pipelines' }) - } - > - {i18n.translate('xpack.serverlessSearch.footer.pipelinesCard.buttonText', { - defaultMessage: 'Configure your ingest pipelines', - })} - - - - } - image={ - - } - /> - - - + } + links={[]} + overviewPanelProps={{ color: 'transparent', hasShadow: false }} + /> +
+ + ); +}; + +const OverviewFooter = () => { + const { + application: { navigateToApp }, + cloud, + } = useKibanaServices(); + + return ( + + + + + } + titleSize="xs" + title={i18n.translate('xpack.serverlessSearch.overview.footer.discover.title', { + defaultMessage: 'Discover', + })} + description={i18n.translate( + 'xpack.serverlessSearch.overview.footer.discover.description', + { defaultMessage: - 'Search UI is a free and open source JavaScript library maintained by Elastic for the fast development of modern, engaging search experiences.', - })} - footer={ - - - - {i18n.translate('xpack.serverlessSearch.footer.searchUI.buttonText', { - defaultMessage: 'Build with Search UI', - })} - - - + 'Search and filter your data, learn how your fields are structured, and create visualizations.', } - image={ - - } - /> - - - - - - {cloud.usersAndRolesUrl && ( - - navigateToApp('discover')} + /> + + + - - )} - {cloud.billingUrl && ( - - navigateToApp('management', { path: '/ingest/ingest_pipelines' })} + /> + + + - - )} - - - - - + + + + {cloud.usersAndRolesUrl && ( + + + {i18n.translate('xpack.serverlessSearch.overview.footer.links.inviteUsers', { + defaultMessage: 'Invite more users', })} - /> - - - - + + + )} + + + {i18n.translate('xpack.serverlessSearch.overview.footer.links.community', { + defaultMessage: 'Join our community', + })} + + + + + {i18n.translate('xpack.serverlessSearch.overview.footer.links.feedback', { + defaultMessage: 'Give feedback', + })} + + + + ); }; -const FooterCardImage = ({ - iconSrc, - backgroundSrc, -}: { - iconSrc: string; - backgroundSrc: string; -}) => ( -
- - -
-); - -const FooterIcon = ({ href, imgSrc, title }: { href: string; imgSrc: string; title: string }) => ( - - - - - - - -
{title}
-
-
-
-
+const FooterButtonContainer: React.FC = ({ children }) => ( + + + + {children} + + + ); diff --git a/x-pack/plugins/serverless_search/public/assets/billing_icon.png b/x-pack/plugins/serverless_search/public/assets/billing_icon.png deleted file mode 100644 index 0e315bfb06a3d917d9801839b399f68715a60db0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18049 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 diff --git a/x-pack/plugins/serverless_search/public/assets/community_icon.png b/x-pack/plugins/serverless_search/public/assets/community_icon.png deleted file mode 100644 index c55e85d1fb2089a1c8797736b1d25eb02d8c42ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25759 zcmV(}K+wO5P)>sbtZOR?psx5Rdw|SbfX%J2!I685HR9c=J2N5tTyK`Lf{vgl-|+vndXwT&`^Tv_ znzr8$hct>tLipX~MpLJ7SZn(^lbe|2t$#-%5eh{sw6Rs#ipQhp(h2c|=lFKP{CZB) zf<%mmM>_hAyz!fb*Hr5cs1b+Y~}I}@jn*8V4L zGJ&@X74g-I`I!WhSrdoE4&Nsgk5DoZb$y+?#R^qxO`6NZDH0B~{cI~=rba_Qt7}a7 zX7lAH#m*g`kDa3cV@E*rF#xW2oyw=#V&1Fk`WcP%(^gVTM43z=afAJ6SlxImOgeM6 zT5ELN7Z!Oo6&K_MK;V3-YD;o)Fl6@bYm$^;rSTl{_hZp;>v!aV0HpYS$@rc_i08=d zmOGz^^eujG=aTX0v+<~QE*uWS>aS3Mks=`ahyX(KmwWk!uV;$oI+ZFl*Wclon=+|A z3yWC0$TwZrU4KuK7k9%!ArA>4!iPaB5fhfbT&)SK4ALWpw>uEv+Dogi{v6-WBxB-d zZeD|g^d9iq%hkrYQmOJ}gJNd`i$59!L?0s7a-}n=RP=#VEPNjs!dM$%9YG+(%Cl+1 zx=Ob^5sQ#1jJ2UsZ3rvv1w1C)YmgY^!gJNxVq0Bq`5Jlw9vg8ypChh^k2S2w7pqh; zJrW3ky2kE)GxX%reDW*>7zzTS4;Ub~7NQ36sa#N<0gcbB_cAXE@>y#|-QDYS$})5F@ET7iu7@mZJ$-BaaS?d!={>rW;+Gq!eF0swjeJ{$D_!u!IxFekomcrSb)t>}95 zu`q?t&d(=TC_vW;h~6b~yQMR8>BK{PSt9P{c)4b)fJ=akDUP+(xEO?iMYjIlJmT0; zh`fN`EAsDWS>OUxYpnv`$MYbr2SD<$DXjScUi)mu+0%eW01-~Q;Zkt3LRITk*xetG zwYg)c_d)~UA4{!4crumh6Y)^!$(flTp6>=h!Hf?e9mY=feWZmR;EVfApN#t9;|)KT07i7Y(xHt^hT^t$%Mw^;t&Dpqx|^t5KC}6kFyRBR-5$T=5}eN*=#&^ zXg>1<1@J^*(Om$7H$&xzNN+xDjlFTvnU!IJJX`ra%cd&cDx55vgj+l>VVBCn^USi~ zr+N+2Jy)$}FA95tWBza`%vpf2?a!so04T9_8=Nu)cK7f2EKT=5WNHe?_p1=9ec;x6 zA|SdA+z2j#)K@~WqHs6m#fd_altYDCj_=oWNkuu41&~%67rYf9^StTGOITg{eiU}6 zutI%IyoNa&wcjbnj@O22yo6bJgZH&t>U@8AJiHCN_uYCdCVVUsda7R2zZej{69Lf` z_#wE2T7_0*p{i8g6PsZD(hio{%c|nyPqUyUU2$C8rD{!-ox0_!-Qwx0>9YGNwsC?} zd92;R6%+2ZL2^GH1m3f}zY}IshQ|U?(N5TtkCWj8m_4c|c6Y0f1%%&2Ky>Pk;8w`A zkP5fDbGtIpLBQ>Baq}YZ8u1f92LP({P^~91%=e=Lp-@)wR)l8*HNI)@*&tJZ-(fA~ zQK~1t7n{c(M-ce@2^`KjNF$_n9zCnGH@ zsK)Vwgj4<+u!a>9PFjFh?>Yzvr5!51S9KmfN2&Jr3O9kWo6qy06aK)Z*K)bia)SxZ zw(gAGC4vjoT4U~3j_L+nglJxoL`R(KMxtJT!^Do{i0zREbRuKe;Vr={a3iN-{hecE zctI$0MHnHo0J_=Q+W!1}-)KY}8i;W4I`P?9i5t=eV~fOru7w_7-!9x6ZiWtA{NCFY zJf+y!EWeo2$00NLMD*DawJc*dUGdSW< zN#|v8D>dhzMI!d^51E9;xuoO42e&@Mq8Z+Yn(EUmjw22}q#sj!Kq15tUIJTgDEmpv z^bDuZtkG!p6m{!+hvFaRo9YQc=?B?CR8$Xo)flL- zw(wfZgWdYZ%ramtYCRjEj_9H-1a z&wucTA@)G^wz`RkLH!aP?k~txx)c-p8%t3~yI;#np>FY^KJX5Hdpp zzd#MU8cQ2ZJMJEVmxs~1pYnOwy(YV?fv!lYm0G>yGlgfjV^fX`4+Jqk{ubMX^22-$ zoyWCA`w08Sb+;D2IJHayy%c&_tHLC_1T*XCCMTCPWR6}=Sth_(g(<|zUZb1T;u-b5 z%Fo2^HLJrOok9aQ$dOfE@H1%^fL2iDgG9RLyOtE4wGP0pre2DN5;%z9MfnU@w)oHe z*lara#C{lIJ@pnHf}7#0(-+#R+Z>KCobH=l2cPvh@cLCx1NTeCyNl}l9tx6AJXxq3d}!sg&g zz?z~%jR3?-JPvdLyT%!{Ef;@xkX$}Kyeawn4#;m`BemrAASPs2^MMTdLU)bwX; ztJ{<|QUHHYSVlMND^>ZL!v*M8rR7xjZ58v}hC4bv{-A2g@%Z?IS1b)Oux@iUYBXLe z!iQR;x8acz_)KwF;heEH*2qI8X7-(1Q)hV12sw&nf2mBT=kFyuBAaCRwXN_!Kga)m zil!7(OLRk3Z+^{Mqb}1=fYYdAJK#cts|PqK$8Nc2z^Not=HAIuBO%s;9o#dFf3gJt= zs9=@+6jrEHP~r|l7L43^S>%ze7b{#rC#wN##IISTH)lIm& z@;Cr7?G|}zXaQk|!!R5gzikl$DZfrs`Qf!j!uGF8hBV>;6D73T*KqC86k_U#Zl=y2 zwfh+gVL-xiwfh)cjTgNR200E2R#sSlQ#tPjsPw{xriK?Ffi-TGHQ>XB*AfOd&ld-* z>g9w&?L!~o3E+L;wmo2L4sXG&T-Z*r2YIDHYKWkn2fHk;!Q@rl2=jd75o zpafzalENYfT)sFw1kj3cw!QP0~POG_QShExn9s`FM+=g4u1}IYmi<* zz1iLuQ13JUJ`BLSQ@ti}U9d(XdlsxY_nl&`71P^gKaf#jV~}M%c=K^JM2Wd z?&~|H2efAM3w{PQ$`(z#%_kE0tO`)frw7cHqryYbBa0#hp@pYg|2th_QONN z`)r5}uugOKI=9N$zxc@``@|3(F?nMGNiH{*sxF$F9#CNtt!-|T7b4-XFQQ*s-7xMS zG00O!qSi1OXOB=L@1y#|2ZRoqUOo9$pYuMmETxq6!y5t=?!^0Y7Vx z{QwnRsudM8*BxTsnM{)A=Q3^IL-U9nIb|0sLv>cC$HFUt*Wq^#9spGxg*7!M9z;RW zfMO{pmF>6I*C5AY)m(-@Vupov?3CT-Yd4w-+6~@|ZaW-fkFZYj=b*o!HQ9KrB>vWT zJQNfmt%kpihc*L!>2ZGk6@IT{7wE=LbR$pRe5TtCDT9V|xr^lrRnQA7U+Dm1B)-;`n+jeMK#Yxp24u|bFnUZhB#by}evp{Ij zbY1+=`90Yjvx9Z}RO* zwMyU4ZHh@bqO=uNe_E*3U~7S)W6=zc2RR7fk=&(;)(M6!Va)=s)!ZsdhEiAE92Mmj|t?eB$w3+JdbyzLW7mQH_~~T_%I$RUk^}LWDaiE5Av=*sGu=i* z+%lB$Xe2_3MAWF7ip6?Ty!(Od@{$77O1VgXx4BOLfp5>{x9BbYH<0Dm`5UYmT$f6? zl6+7IMRZzBhv+L;FVi1hId8s(D4ovC(tU@H(jENum(y7zR_D(S7e73aQpI`WOD}WT zoggX=ZNxEn9w0Ohc&mYs%DbrI<3qgQGeuevId>zZ5S~?|Q-2Vd2Uh9PBfw4-1VU8M zL5I4N#(=RD%$TE}9`5FMKunOl>SKt61Y&Isb?%9n!#wZ3`E3<;Z(+FywF0PDz+%5% zDblwK8}!ursvx=*%ZUViib zAE#f>9vU0zWvq?Nh&6$$>xn9@N*0z|d#ouUw{R6rcdW)dBvl2nU6Y{)Js99S|p z{a&L)U%YaG{$gX*mE02du;snC3(G4pXp1g!h0~6I%8y%adY8K1ot~wCdGaQ@F_vbb z&!5&Y3aEAe5^DdtI!zb{g*97Y^%ATdhyXMOx!FcJDDEF+j(ubn-4x$GKa8eDCxQcT z-H{f<>75p__nul@s`gi^*$r^UWs_d2ZqawPw&>|~f7(p1Bp)V@#$)u~ zoj6K=`u$M$W**_d#;Be%S@3Gi}h8zh7b-ZcD>C$bNf}z}L2NGM0bL!DMMa?W3{#1i?{sg~{9Oghe0zj%Ll@_Uo;$(@m2MSwzd%}SHf^j72EmUU%F>ag ztnYmHFd$>T_nFv#UU*O3RFVT{S+VwVbpZ%(lTLC8ko^;f{1rET<;uD^>?DvL{5~Is ze_ni_p08|(1Hdn!s*7QdJ>*?e34hl~v$?l4Lsr7cA{%&!eg|w=f7isry@=D@VyG5f z#N~SP(P3(SGnteqCrzgkGzoZPcbk65G5E!L(e~$XUw^peU4>^5XT0iqw?Kv6Hd`&b z)SC(%a=6bY7Tez+2>t{9>|U?!(&^Y7-5GO!|8TWoOed2aHcBH$oM@@~vRhRwzaPh6}y0FY%wH-!GUl$6z zU8kEx6*$fJ|2#kEP~!EW_|#aJgiR{c2aqZ&aVlyiouWwvV()Jiuh5R}OtJ z8m#Pc_+(6!Jxc?|z=>|A(q8H(m4$TnpK-yLL^izt%`!$pVX_gls3uxoFHZv#)c4Ln8{ zskA;wzMC)6>iSlzL&&KRrW>F!1#Vfq2X|P^gy%n^D7N9)4OaZm@)x=yIRrRAE;~|G z`w1V;&eC$yTR-{w+NR^re)Po4^a9^{B{}ey-+G1q%YXL&(&bfmeMAnSkfNC#$z>nD zbnEeNOreo_K{tyuli?Yq@=A-4H()g}B2aByL#IWBj?#}~b|Sa=+E!W>BcCR_7C;1ZMkpZ@;; z?@4xft-vzd4a~H2?Lm6n*GR)rS3qG6gt@%f-&Yk4FvW81VT8XlKn#@V%|7CCkQ~-q z0;Q`9Y!T*W2NR>?^z1gK%CInlKO*wH|K{pD^!@xgbqhZN99;Hk$(MHvqzMrkcmcwk zikI0{9~8+YK=%LTu|M}6h!V@Q?3H5C_VEbO@EvQh=CDZsO&wu>~~n_pe7)JOKZ8pqck84gy@FLZokUd3s#b2=Gw8t^S&me z4iorueSdddki6Hy4$t~<+3%Q1?@O0mMS|03eyZjW;-F&gxi9^QhKYau|9#ifg?ED5 zq#}W49GO0Y2M8lEh&}N9SPy|)Gyn{Q=#5abXYnA3!G>lY6(olx?>8nN1pcnDN`JF; ziF$=!YktrC{&d-`_yB7yUcwx#`rn+pNUN`{(XjDnUo#F95Hn$JqxC~hIob+wyb)8E zMe&RTD&8>UBs-I670RGR8pAI%A45$n_sKQ}O zgwieiSaZazaN7?Ou49YtWz`e!c=rJe0=xM#oqu<&t=~KpGV14hg%|~GW_Dotty5_u zmWj9O*fL~wTAOt&MKrOP*0)vfi|DZCgYGdO7bMBon*Fax_G8U&KLD}09o(#XsNA4P zZtyFro$&MXogqT6p|$LyL%fcuGh*en-Z|zPfc3`Y8cZFMm2tY_8CW%du%<0n8;!m? z+#HZm_?GAry55^O8!KUvio(KPgq^xPoUGtrx&@Kt+|AgRS%U$yH7p0T@;mRvfmumxK!@#I*=2K)aDmd*I_<9zK5Gs=GBd^ zasv#WKhFmPM83ATU(>`v!Z(n-XRF>5(S_u%>krbDW6&fAysF@kYC5RaT*Aq!hsuNd zJNNOB#?8KMWewdVcS zs}CB(Ao))VB~NRPUhRWy%_Z!tdVPMlBe$(M-bEWL+-Jnq$pK9-lGSi-K>ThI^m?}H zT@$^R;usXgT)MI;zD@#U3T==i2VUOsuAVz6d2=t=S@rt-Xe%MR=~s`Km6GUMi$zDX zy(eVp(?{&j9>7D;$EZtm{fc4DySYCsxvYG{S8LPCI4}&7LxlTj(LF795USw1!7rpb ziQY_%F2#D+?f&}I0o5hf`snUPjvT_BpW>XVMHqyhz&b*7nWu{Xd%%sxzZY7~g$v19@Vg2=rarL|qh>Rl1N+q}88S!GZyr@i3qT0u-09R^k_ zRZkJ_o(1pn&w~i-Aj$IevwWMOVdM6HbgMa&&U@Sy0AUP*oLd>I7tr~JT)8pSqS~<{ z%xQGA9E54v9Me^-?wjb{R>8rVt4e!zjBcwjRn&QuQjuM;Vzc)g?(^H;Wt-O}0&8vu zJ5g@)lm27pUU0`}Zl%nUVRYWE7lfzhg|Jz^>US2r9b~Rt%yt*6pPZjZLR8afosUzl zHnJ2h-$y>-o9O*o^Kp)3b`#&um#g$rz9eoJE4}4Wy(-~A@<4=hhEaJhT!H$Y(xC57RP|VHlw9Fp0h60_bPV8`4+}~ z5gpw#(`wDd<_`yw*ZD)dRNea!-(?rP-sRu`j6m{o+wYj2+)1{R0Elu{?0n=X?R@ko z4H6*v#~(do`?GJAS6$0>-EvrFH5L#IjnS*Z;(;cWdR`hsd$WCcDCgF?_=zCbxV9Nw;f`W z(}IEkdw}}BVlyBTL`Qx-`|bBAlIv~K&n>@x6Wz{nIZ|qFKzs5_)tu`pC@-L-73#co zuq29Xps)iVI;^)Fu_5}&p_5gYMjO7*eMq%=jfeImI#O!A_P!vMHm-CUEIC~E0xKS` zB9t&c)RRo?6G|E5VJ7$CSa0#OAGO`jdZGXvr}O6o%Wk)gHGkJ3p~N43<}yWoc-8k~ z9{u2wAo!6F`p?Zj&)3ZjkxXA(UG1jFVpO<<*Q6QiVh4l`qSqY1ptIP4Bn@qxf?_XUs~6Ju?E z_RbDvURKqC3$8Yu+88 zzRSZCoouPswwT~+ZBu8uao%$)TjKWi*%c9+ANk;6x`P$>^pgAG3l|>7;?}5p?E{Dh zHyjomVwxLJS%-Rg@U&Fzt^69K#%#N?`TOC$kP!VRXWUjfxn9vigwuz&@^~1qs_Y=` zW#xm7^M{pCPDNOA^<&SAY=Jk?;bMAO^nmEy0qQ$eIKg@^{N=j?ZClN+6)Dez2kCD( zv+PNDluE;ek8Ac$FI;gcs$nJ^ZVi`;Is6O?yPutHQ1;Uf`Fb_aZdA;*u2tmJ&pJJ( ztLv`m+8LT6K!CHBxWYo;l#J6HYX!L7!s@P7kh__Gp62i2cG71X;sqbI?Lm=DcPPEP zL(BiqPwCKC-x+FeI>`R?BWJ}|C&>MJaHFj*Gf?M)UQUk@#V z9U6c&ZHK`bxM2d#!ffI+IaqaPiyop+(-Z+Q70MY04Lgzps~%N!*CoccWoFtih((IM zLRxohhq1%c+m}VzsvYF`s6lOKQGv(AK~gi*)9@H%>8#WJ3+0-lDCbOcoqX5P0@iA{ zQR6ul;G9%*#NnIu?p1Xsp!fVr)m;_};&X3c=lR(`{NBKKtaS@mcaR;zofQz~yvk#T z1+lidP{F!0A@+H#V`i7%k+Zf$H_GZX-<>faIQq^pMFzKOZL}xzE7cwP4?8>bO0jHP z!U|RWjbeq~D)m-yVZ{{>YmQV=(30v6tX}FS^)*ZgvOoH)t!4Mq^fD@#gCd=NoOLRR zwIzDDC9gGQ_7zAUiw^@J)b=B>(at`bM!;R46p~9I zbg*;d2W8cv)Z>OySig-gD$p=9KNA;&DiRJUVAlR^t-3wYyHwlDl|4%tkBye(h|LR} zPP=O+MR#XXw&kWDVmBI!dWBt~R|;i%ez(|`a_bgeiu=uh+wKYoZ{v2?odC;jx8zY8 zlWMI_tLxnlFG-=!JslG_l)|C_#%|Dr8?fFe(2bgLtzo!In6LMYM;mtCOv9<|so;&ZC}x)3dbxMzcY}-765aXQ_b9;h;M#xn@<^8`4gyzx z_hvE2PU62^xQ716*iy^5q5k~X(t?<~;1^D1u(Ea@*ySt{ie@ChDg1`@_ds-;Ar~Oz zDYbRA?bY?Nhgr>?i{W+JXlmpC@bY;IFg+06XH!OUAp7n1%)nQ-S1C5PS49WQ3`5uK zB9r4(U;_&;%UV^EL&%#qcc;fKp)uTl>vLoF`}$V7El#%_L(MbPCF)93d^e|BKR_Mw z-|bFLZZm*4(1kXQK#QzecfZXc-&6TDTAbgTR10Ibw&A@h+Y7Qlrf8}2k1CsCQWjAU zhlV=bMQ(Ama=DF}nijg3xPBv<2~^e;uvPUAr*%C>ot#Is1ZIK{1(bUoyU+!_5S#8E&$3Aaso-K-mR@2H%cGxPsD<~+ICaHp_{9D3O$ z4!b#(qIh`nCh~088njY&&XXH^;fB*zi+prx3e|QiPHxmFc}M$o_gZL+%xXyH3b$x{GEzNM4N1**)YFnl+OZ4#o-3m9*>2>8Tmc4R2Fgi6ZnrkqHEzh zx+#9F7wxhFQ7fOmvX^Hx4h-Fn|J?r)m0eVxvL7Z263KHRD?FY9E!5 zd(%L|Xhh@RbLSgMUdz^n5thRNqor^dVB(FnaE9;N>ALf`&(MV9?OKuEDOXyx=i5xo zb4<*F%@QjsNny3q=7E6U$F8+o*u4Sy%X()H~gKDw! zZ~sQEJCPBjl}i{@!48%imR|EwgHozCI@dUHTpqV6J?Eb-%l{TvvmwMyKK^~avw;Pq zeXHE*8iK5HemkuAZ7h~ZxBbbP-yV?M9(DwoM$~K$EO?kZ zdUpqzT&tu)w68|}m5z(aGvc6Vh{77V#>sBU1K%fmIagZ7e~V+FLAB*>(`w_uk4yF^ zC@eZ=wWlm`HI_A;v>{+boNvYlbG}lg4F8_jHkIv~JJFq{g&uS)EN8{`(q4fB)@|(- z4%iQ*hTj&PK5Snj@^&rny4j^_UN>&dUQp994%#VZNB=s;JcHO3bD6`g`~myw-)ja4HdNZEr!-3MS9M zg?Gbt-8~LF8nmX`VxEbPTdx4opS|E*H<4hX-+t|2moavO^@hW=R=&1&^F`7466b3J zMP*KKnga*G+RJLV47>M8^OYRBHRYg45WSUhBW)R9g&E@5;122$sPldA(mGvQ-QYJh z_~DEaKPtJG7$z(S3}SGnIdN&W;2&aFUj9zqJR~;w{?GAyc(d3U3T$~;bST#W29Jb? zr&Vr@XSbik+jV;bRAB8$t`0}l+-{*L)bho-q~pwXJLnbz2GS=zj7r-Q#!%=wd^JDC z-V0^awp6#1sb9Qbw^xnDqQpeI@(xS=k~t^t1X!tYfNrhRrjq)AfC4> z?;v|D>MvB(^rq0?%zHO`7UNV`y{QlHbj?Kd4yz5p?KHdG$IbColEj-9H(TyKhwz3m zQc&t~KSQwFJ0vjQ{Uft!+qmA#b@#G70eXkmXY1u{4I57LFD1+M$hFc)$6mdefX zy6@upv_V|ViQ5zLHn$2)_8pO4iWXztMg?3P|#W7RQ84%aEnE;oq0%r3mTFcJ4%WS4r0 zl}NT>w_t8?@w%9Ttt`VsBJ6I_uks;1&h+N{jglZ(f{6|H z9A&7o=6r58#Uw4aI@`85gK#9C6_a9+O9yG47DT9!yW3QuEt8D4rR9*1#~=!I;UgaZ z%2t^ob!ue$(aEhbI@HENEAQ{s_v(x>=IK(kDoFS$1P_zEaJSVhs93?Ij)a0$Kb;BD z4xfZ!etiq-zG~&U;>wArmdm=uf`Je>Tul1si+sAQO9x=7rBexFW@l2A+crcy2y>-! zg_4Q5V>%6lMUQ#IM)Oj2u(*w@A2u^z>M)8ZhvpHD8p}S`q&vboY@*JD81!8Cb;UIT z5l``lWVO(U!)Hx_4c}j|areX`#aQQiMVwx48u9(jHiI3#thtxP3D6(Byrpr+=lKPu!lEPz54xR>n=9WkIB6?6P zGhBETGmSe&2iBPAvt{S&eqk;nQqgJFl@_vTVg2C+AU;_H21I4yBg-E>&yTg;MHb>@27Vp-+S(tTQ7 zud29gkXjom5*}@xlR$K@QdkIc$B!-uWnEpvj22f^xyiB~T@Iu22$><5elA zB1o+b7jqu2`y>$EsX|T#ykGQ*TboxD0_K@NSgQ69p6@~&Tl?(NA!5f9mTC-@#nX*b8+u8Ix@wN~FdSa5K}WA?oEgD{gw+gIi%G9075+OM8C^Sa+1iE?CrD0Ue+KEQIni_C5T7F!zggLa z0U{o^S>~wjNduWrTb3Onua1EdoBRQwJoeQ>XIU*$a@NW?&pP)YhkoEu@nO+X-PUwS znK_N1YTAS)YD+4-1S`q$SSZTL7hAdk3T7sIAmmQdAQ^~@K5;alAcjW$`Zl{Su)>JX z5nsPrFp6d5qEU*D^K)}*V+Lm2PcA?Zm3pgB*cp`xCXcA&^CrHs)W?)1C4Tpyi z*VGgl#4${SkvVYuY^Ug45m#XGO~&YA$OJ#0JQVO8af=WvNp8i&U&R-yWhT2Z+YPQZ zOBo{OMyV?R62syn?k+2nXJqRsEHrZL2!GJPg8M?8Lkz##!BH zZc|l|@KMBq->dQsv*O_T%fXPSl2Z={H=<+a_xYdf()pin)8@+Fy!jZrTd{PMj^4UN zM?PrW0_<;8sQo%=cw_@m4vX4%d`0GJelA1Z0J-+4rK+{)FqC571fa0O($dSxt}5V) zgZxrWM+`2f%)sj3ri>#(n=2j*@%>w+3X6B0LJJ6<{r3A}i^oNq*Ha*)K3}W}{wh&s zDMh#cqg&~QGs_g9d+f&)W|<9F#Uv*7twJu|r>dr|UIC`L!Z_?@KSxPrmXxed4h*lwKOmp1S>lY3L_F zaTkUC*ObiQdNbRgMd1sJ$uk-I>jE)4)+6JR#o8yh=n&peRTt&!8MEn}lzR-c80e>m zC&O5&johv{p(>UBNBu%!x2sVHIk)RjIgOveLcx9`mf*YYnK=Z|5v~NExP!lV1L{}2ZS_r3Qe)>oQsF>2Fr}! zK{>1hta`y*x9kMaVW31u#g3-9SjBSx8#+$J;%zZFNGz=_I@k)9_*sf@u+SiN%Hr}# z6>qxQ-aflR0S1H0lnSQm6dD-DxR69a!~l1qLr*K3qeTsdk=zAS+U6H~{2;$2-%>mP;-cGnm9LE3u4M1!a`U zefUq`>R7bbFJ5@!Wibx9pDC#U48*52hL4)YQFedISlx<>sBo)^&{!^iFnZu4q7$xR zj)1mnYlSE{@J}bDsMUx&`gPw?0SEFP=Qo7BILM6rH~u|miQBH>`{EBCQOaLe*>y)D zgNWai^o80+zu=Jv*S_}7KrFcx(y|9QxPU7@qf~ts9ExXTu?#xaB$-kAiY6C420GUw zE_?rwUtggC0x`!R-S?Pg3~)TcB$h-qCcnl-S*-jXzDh-1)~o}U*lj`DOq~RacwRO_ z;IU4+jL(I8z4qc28Ws?RVj23 z8H&F%n^sS#vfB&r1oGT3vNCC`P%4Uo77TJc&j(1AJq8(T!!=h6X$sBGNwD}`hzU>(p2r3V3|PeWM`Z8-M-QzS&VWER1`8yC1|u-Bn16G0$v~v0FW?7{^MrW(a5@ zqq@#1%i9knkKRq%zzc66Ri~uEko%Un%8H}fgN}T+wB!|=>te@Dm6cr% z9^6M3=B%CH78ZBd0LgPJTR~Bc3-)7mqRQWEk3y$mMV+%eg;LEdm92_uIVVF|HtXmk z%z;wXSwG_J`&LLNZG{GmZqf!`aC3)Xjp25K+)&3)D9@R;5oho{xcJuj@b6b^dmCDn zG{yFhhv%|e@%GsZ6kxj1t?Ldl%V{wS*^C%5rH++CJYFcdb4T3Ap-@JGIljfOOy8m* zK&B1i99yd?gl>_xxI>7kB~d}#*+^KFtIkD?{pK>wBEDzF?+J68%R{)-k z<;tsNw)vlK(5SHb+EohB8z!Y{t{3GWqoL5WB6p#8>_Z))0_A`lq_e#cDfMKltrIF!O#F)I#>nwTFa z501rP(8R>SONg6*S9e4d1Y7@eGrIi zd%5$vi&`$lH^k;3yPPqXW&+E)_&L+9mq2Rn!!s$zdG8svuKBB@v+990x5qRn?y&*Y zo*}QEH20w+UiahDtG|OwS&opF0LL*(YA9mdps*A&3izR@pLbF3PbFh*m2Y=3fpIWM z65{}{%W_7PrkIOnCmh^;45o;Q5k8|KI(pXw*rU?$+LSYXL$wqh%$Rd^opLnd@9?lr zfVKXfPT>{Sv_t1;f+1a^9hdGLwY=3Rh%p8~s$-f(3eI%PA;O)@mqg4iReH3o$~8~& zKsj>j=`>%xK0oR25j63@a_?!bu=KCJFl{6S-hx4G_Z{OCaid!zvWlOLWHZ_fP!NaT*m) z{JqgV*R-MRF;{I8mM|`b{V)6gLYk~|Zy%6?PP6w`w`k@44SM&IcX}6!yuhXnUV*iT;1)$8 zkA{}iC==NjDk*d6I56a($xHk^uo7vuvT&(KQJ#9_gNG@AJ+^9-Umm;^>dtjXp-VCm z>JZ#&O!T#loz}noY$rw>j~m>6tY?x#BSH4C|1UziGCM*(R?msOlr|qeR{;41Y-xM;gNt;?D2KSjD0G)x0-vy zZ{AGNL&IA+1!W(z2iO0||MZJA zEZqK?TLWwEf$e&C7yGC&D8*eOs&J&+?Zjwu!Q6P1)tcRHdlucur4JuQ+^{EFU?_#3 z!4ykjPIsC4bnA>jS>JAqnDTeB?937gZ*^*86PF)l{N~DSVbj1X%TOqtUc>V{{GHKj?$&y`w-RVW7I34;-C40Uk*s_6Ud>v<h%wGq#Q1m9%68RQEG8^^>z;_y`O-d3BS0z{6WwjnZO1UlG!rr6;(AMq z4y(Jd+j+u+_)y9VN?{F%3>O@h9)PvqAUU@^+_L^?TSYBAQg+h)R~JG6^8BbCPtm1^ zZll@nUZKp3YZS@(>$L~Lq2S;A>o)~ea9v=v;{N%L+imxgR*RY&Ppdw)hET{~+8OiQ zOO_nES|!KNdA9YK@;^qUyh~uYgB0eV5azb?uJ?x^C*yQ0Ku!#Jw6nx2(3%R%E>ml= z{1qxbex}Cw@B)k#%29D3%qSOB>{3BYgl#7MUu3Zq=i`gUB$)vI;Z-a%?()~`Bi^$X zGU=GGvVIX#Qgsa_GlYpVa=-)>`+UjM$h0`!x>7@=pXD>)0U8lty&=qDbKWR)@f2|lz}Hks z&*(RQt{jkL_F3ucFGNi)9r|kxpJq|hHH;z7Fpp2 z$O8ZaCUyQ6#5cs@ajao#pys_MCd`8H5cgI%rG|szdcGgNS1Z)$+*e{R4?w(<^S{fk&;{@Sism0#OJQN>G^Lr zu2oFKNUIGJx~{izxh)V_%azV(n*Iz;FhJ7(&tjGKKZeoTB_) zL67>R;*YMom418S7>x>HbM2By)3I0D@QC|$UHjP5eDbXPcbn@Tp)w0{v608=-2mdp z-RW8Si;cmKM^3$-qsYH~i+Z0xKUf_4>Ur@sAp2zEqnUXc736MoSp$<`m6N-Oeb)MojsQp38(*ghK$TNnS5|YP7QR|*chf(@r|B=G1|wL31y^a#by(wKqz_p9sM;wy5H>l-^AZ}!2( zYiJP2e*682CQ_)>Xu0tL#*dS1$?wk|p<$xT>0tDKU_oh>Q@>T4VJa1uU28f;&RW0M zL3HHPN7dls1XRt<&Fooh5PofAyX`*zc>Z|bLjf}{u8Z5G1MWL&>jR7*9~nVxUc$1H z-J}`LrPWx9?AEcyW;M2gj~={W)CfSf52aY7e^XWRpAEHh0P$jVvm3rIAI2 zZT+B5;iojxAEGg1el8>8^O_l>WBe4Bzt5x%YyVG|PSDpjR~>V$P8J`llcPUcovPRu zEs$Qh_sD$YP_|V%`|t7>>6_b|ZH+7_kU=2~+EeYeu%Myk?|$#! zQ*C{8OB4qIw71^>rC;m1%@w5iqYKVOIM*c-^gGu~))ZR2UN^G;>f${8Qg(($iO*)1 zeHF>Ts_S}hBnRGof35Z3ON+CVO!yvbv2`e$vW+bqkBg>_ayJejUSC?6YnwHKtK7Sp)7CfRA#((ZHbO&!Sr6!N@tc7^skwwd5w7X4&$uv1;UaBFndcO_gu zB)4Xm!rfSOO?%4eM~;u8QRoSp5X7PpcH{RN2;jQQ?w)HSNxC7LYHeHrFmU2kbG(!t ze*XOQ#O7AKdjQH~S4!?76*J=E1VMM2@+4G||PZ1}C*$ z!u4u_`@XSNIH!{~lA)3P0DbDeojpn~RB}R$!}0}8l#zr$k2=~{-+pzKrX3rvPyb=% zgez6|&OyZI*Cj_&XAXjo(wYNDmW)If>PEso*CrcodwUa$6`p1R>O1ph;)xFYTKX`p z>IM4VPQDehW9J$!4k^xsiql_of+3Smz&I?#?|83R6MZ5+xjoZ%_{pyO=eV_x_1JTf9w{Q^x1(8790INK)7)y24|T1$a1uNP_Bfizp;YUle9yWvqj z$+`qG4-n_-eWZFu4HQ1DcnEPfr-!!(PX!1Lw|yEAw&_;0!jtGm>ncq}td8QE>vlA{ zf%alJjB=K{+K^YDgrO3Hz@=JEWE8H}_D@-?mk0-pDu4c)slituc8dsKyJ43Z@Az3F z9(lr8z~b>(d(~Q@v=bJ@M101#NlK8UR~K1s{+p}?~DHitsydA33C{NEjOh` zYizYKpMGzTJpMb<<22E6;HL$5Zwp&lMv*^19h)Efn5Y#0?UIRzD8AV(Rz+ir6{x^V zC!)SGW4?$EQEq*!bas*lM`Gx`hSRUXUI;rgAJ=B+Z}c6pP!Y;INQv0>*dKhj<9G3M zEOdZi_a?{kW%GalaZpMt$$|lrss+7!CQ0X3F1gM%JC#b&F^jv2=Zf-UqL$BV<{1tb zOuEHfybqZRD)D+yBOnh4xad-JM2Za_4AxI|yR&=T_-VntT5%L=I|%;wM=ZeEAsRLQ zcA`m!wU%1FvqqxPXs&1;X*CJ|SAyP{sGPbLSYv}4c)3XaAL=v0g}Q48M=}GH7DH#TTC+0T7>^ z`X1Z>OFnM5eOxX(G$l-uu>!Gqg~{G<7y#LgH3zyf1xBiEvaPxVmN=p|{)SqdZm8~E zBp~)FCN_4XgS8bMU3ZwAb7L6 zyb;fRz0!iH?taH}IuS~WZt}m6+tfs(1I#!uyt51q4F^NT*P(!ea>2)71$=gDTGI{V zcH2SnajERvJH{lLOIJ2+32z5jb3Z9H6BQyn&%`dm)by|(pX-luO z)h@NfHg$EAw3^ln6J)u_7ww|CM)~ecQYhbXC0_nuCQUE96nfw>;l5|1QL+9BZup|j z{f?Q6xqJ-}|3JPIHcGP;-JC4_EvY2sn}!gXZ@91Pg)oTKR2v)%E;5PhI*EhGPAH7~ zT)8uu-*21;!9(3*;Q+f|HiDibjtgEHg)~U#rf09oI;LFtG49*|>-xz$`q0gt4_%RpNnpJ_Mp3?)!PuHI>#7WQG+zrwDQ& z?n#S|U_E%S2Y$ySuA4g#LM}2vx=F8rm>@h3G%xoY;f zE+5wpHCS^M)~-w^M0#i;@0#e-6057g8d{v#_oa^0jZ9Wp8)?n07q!^OW+83D6z3L| z9qAw&EIocV%onp)emBRK9NvKJFuN#R+j>U@+Y!m_tr0vf_A=I!DTTT(&J!ya2o zXCwU!fM^;1+YN7&<~r0Q^h(7#PA^7yA|X2fnS>SUby#zt=-mtTlc|%@bd7uku4bF zcewcSet1q*Y2U67PGqZbN>6$zqNX5~@XYVKg$;fH@8Na6(BJnE?1tsGk2_!9i#11V z9_eu)Oi#Uz(-y0%xHFa&x$5UD8=T`dgeq80Rk${Uwsv>%T%jmp*CTuY%vw%fxZXD~ zIhOf?baO+0xV=mFF3gGlKE^~T^TiH-OX@ufd-dkh^Xm8crT8|kd^yHNsn#5# zu^(&ROKdLD^Ct$Y-k{h+lf0+R2{_4jnl*Y(H$62EUfOkho6wLw_yabI9-2FnMp0jW7?1Xrur?;c_oe=pXr znr{Ca1*57D2Nk>l2z&Uwn1|uT!H1X|zbDsCxBZ-7!`^7Fx}!kY4RE+!_EFo8!Ic0$ zW9dDqYq7h9B>6b=>L5OfWkB|A zJ~$3X8%zkJSO+(_)T-qb1T?b<*4MJehmetW)2ZZjwVuP}zI+#UBffx>hNcw%WbPWeHkuGU+@i&^XsF;In~aGOhrhw!NBd`n zFSMe#(AeF!`Q(tM@)C|9$|2PziL@hf_XZK+8mokY$52uJ-LMe)jZ!DcB_I&OLU+4A zTzV-e(S;5H?ZR3twCLhG<8yqGLlA@^>W+G(`egeoa{DiPq|^ZQv&3PGL+&H9>5j0* z4?yzYOkFGXnvWCcGe3TGL0EFkg!7Y5>$VEc4t)nHOLua(_;N~&`ct55qxg`6n!A! zOi&OGDtO)Gm2gY>dpBG1#ON}^1-k5^62q!rD7VM*NU_P-8sPzM$PPrrjQV)n;K#$! zs8N+?v9ur|4u(^S?k?yU8Dbo6vQiJvr-ljmH>|1D6(B(3u#xV##6on>4OP>WcprC} z)rha|